Polar SCL的C語言實現

Part 1.SCL背景簡介

極化碼作爲我國5G通信的主導方案,是目前唯一能夠被嚴格證明可以達到香農極限的方法。我們已經在前面的博客中講解了極化碼家族得到最廣泛應用的譯碼方式——SC,雖然SC算法簡單、運行效率高,但在稍微極端的通訊環境下,我們還是需要使用算法更復雜、糾錯性能更強大的SCL。如果是SC是使用迅速判別並確定譯碼結果的貪心算法,那麼SCL則是將多種譯碼的可能結果進行更深的比較,“放長線釣大魚”,從而得到更加靠譜的譯碼結果。

至於SC與SCL的真實名稱,咱直接上圖:

顧名思義,SCL需要將不同的譯碼結果放入列表進行比較,至於怎麼比較、怎樣確定要選擇的路徑,我們現在開始介紹。

Part 2.SCL譯碼原理

在編碼調製、加噪聲後(與SC的編碼、加噪聲方法相同),我們以碼長64的4-SCL爲例,即4條路經的SCL,進行譯碼。

第一步,從右至左進行f運算,譯碼算法採用的是SC一維數組的方式,由於SCL有多條路徑,我們就需要給數組增加一個維度用於標記路徑。

第二步,在出現信息位之前,仍然沒開始分裂,每條路徑的譯碼結果、b值均相同。因此,仍使用一維SC的譯碼方式。

第三步,遇到首次分裂的兩個信息位,開始分裂。

   

第四步,判別並選擇其中兩條最佳路徑,即篩選出總路徑的1/2,然後將最佳路徑分別複製到廢除的路徑。

判別方法:我們在此稱譯碼運算至最左端的比特爲LLR,LLR是浮點數,在SC當中遵循:固定位,譯爲0;信息位,LLR>0譯成0,LLR<0譯成1。而在SCL中,在固定位,譯碼爲0;在信息位,分裂成0與1作爲可能的譯碼結果。我們用0和1分別往下譯碼,得到下一個譯碼的LLR。此時,我們引用一個定義——PM值作爲懲罰機制的累加值,注意,PM初始值爲0。如果下一位是固定比特位,若LLR>0,PM值不變;若LLR<0,PM=PM+LLR,因此,這相當於對在固定位譯碼出錯的一種懲罰機制,PM值越小,懲罰程度越大。在信息位時,我們將PM值較大的兩條路徑保留並且複製,然後繼續分裂,從而持續的保留其中最好的兩條路徑,最終,我們挑最佳的一條路徑作爲最終譯碼結果。

例如,上圖從左到右4條路經,假如計算得到的LLR值最大的兩條路徑是第1條與第2條,那麼保留1、2條,並且將第1條路徑複製給第3條,第2條複製給第四條。

於是,原有的4條不同路經縮減成2種不同路徑,我們在這兩種不同路徑的基礎上在信息位繼續分裂,以此類推。

如果看到這裏,還是感到很抽象,不必糾結,請直接看下面的硬核操作。

Part 3.代碼實戰

程序要求:寫碼長爲64的4-SCL譯碼,輸入模擬信噪比(建議範圍:1.5~3.5dB),程序按照比特的排列規則,隨機生成一次碼長爲64的比特序列,模擬其經過編碼、調製、受白噪聲干擾,被接收後,將比特序列進行SC譯碼糾錯,還原信息序列,返回給用戶端。

輸出顯示:1.原碼序列;2.譯碼序列; 3.比較原碼與譯碼序列是否完全相同,完全相同,輸出right;不完全相同,輸出wrong。

注意:上文提到,對於使用數組存儲數據的極化碼程序,SCL需要在SC的基礎上增加一個維度,但在這裏,我們表面上數據處理的對象仍是一維數組,但思想仍是二維的方法,具體方法將會在下面講解。

第一步:定義。我們使用Encode結構體處理編碼,使用Decode結構體處理譯碼。

#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<time.h>//種子隨機數頭文件 
const int N=64; 
const int n=7;
const int L=4;
float t,PM[L]={0,0,0,0};//4條路經對應的PM值
int v[L]={0,1,2,3};//用於排序函數sort
int CBR[N]={            
        0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,1,
	0,0,0,0,0,0,0,1,
	0,0,1,1,1,1,1,1,
	0,0,0,0,0,0,1,1,
	0,1,1,1,1,1,1,1,
	0,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1};//固定比特集
typedef struct Encode{ 
	int code[N*2];//編碼 
}Encode;
typedef struct Decode{
	float a[N*2];//浮點比特 
	int b[N];//b值 
	int LLR[N];//譯碼值 
}Decode;

第二步,函數聲明。

float add_gassrand(float EbNo);//加噪聲函數
float gaussrand();//生成噪聲函數
float f(float Lin1,float Lin2);
float g(float Lin1,float Lin2,int b);
void first_4(int i);
void first_4_plus(int i);
void function_back(int i,int v,int l);
void first_split(int i);
void second_split(int i);
void sort(int tail);

第三步,實例化結構體(類似C++實例化對象)。雖然只有四條路徑,但我們實例化5個Decode結構體,是因爲第五個實例要用作複製、交換、賦值的“中介”,作用類似於將a與b的數值交換,使用c存儲。

Encode encode;
Encode *pE=&encode;
Decode decode1,decode2,decode3,decode4,decode5;
Decode *pD[5];
int main()
{
	pD[0]=&decode1;
	pD[1]=&decode2;
	pD[2]=&decode3;
	pD[3]=&decode4;
	pD[4]=&decode5;

第四步,編碼、調製、加噪聲。

main()函數內部:

srand((unsigned)time(NULL));//設定種子隨機數,使隨機數隨時間改變 
	int Vi,e,qq,ss,sum=0,s=0;
	float EbNo;
	printf("EbNo(dB):");
        scanf("%f",&EbNo);//輸入信噪比
        printf("\n");
	for(Vi=0;Vi<N;Vi++)
	{if(CBR[Vi])pE->code[Vi]=pE->code[Vi+N]=rand()%2; 
	//CBR數組中非0的元素是信息比特位,在對應行產生0或1的隨機數 
	else pE->code[Vi]=pE->code[Vi+N]=0;
	//固定比特位仍然爲0 
	}
//編碼部分 
	int h=N,y1,o;
for(y1=0;y1<n-1;y1++)
{
	for(o=0;o<N;o=o+(2*N)/h)
	{for(e=o;e<o+N/h;e++)
	{pE->code[e+N]=pE->code[e+N]^pE->code[e+N+N/h]?1:0;}
	}
	h/=2;
}
//調製 
for(y1=0;y1<N;y1++)
{pD[0]->a[y1+N]=pE->code[y1+N]?-1.0:1.0;}
//加噪聲 
add_gassrand(EbNo);

main()函數外部——加噪聲函數

float gaussrand(){
	static float V1,V2,S;
	static int phase=0;
	float X;
	if (!phase){
		do{
		   float U1=(float)rand()/RAND_MAX;
		   float U2=(float)rand()/RAND_MAX;
	           V1=2*U1-1;
		   V2=2*U2-1;
		   S=V1*V1+V2*V2;
		}while(S>=1||!S);
		X=V1*sqrt(-2*log(S)/S);
	}
	else X=V2*sqrt(-2*log(S)/S);
	phase=1-phase;
	return X;
}
float add_gassrand(float EbNo){
	int i;
	float Sigma2;//噪聲方差
	float Sigma;//噪聲標準差
	float Rate=(N/2)/(float)N;//數據的傳輸速率
	Sigma2=(float)1/(2*Rate*pow(10,(EbNo / 10.0)));//白噪聲的方差
	Sigma=sqrtf(Sigma2);//白噪聲的標準差
	for(i=0;i<N;i++)
	{	
		pD[0]->a[i+N] = 2 * (pD[0]->a[i+N] + gaussrand() * Sigma) / Sigma2;
                //先只對第一條路徑的譯碼賦值,待第一次分裂前,會將其譯碼值、b值與浮點LLR值賦值給2、3、4路徑
	}
	return 0;
}

第五步,譯碼。

1).f運算,在遇到第一個信息位分裂之前,仍使用SC的譯碼方法,只不過在這段譯碼中,比特位均爲固定位,因此,b值、LLR譯碼值均爲0,此部分譯碼函數名爲first_4(),我們直接將這部分譯碼函數簡化成以下片段:

void first_4(int i)
{pD[0]->b[i]=pD[0]->LLR[i]=0;
pD[0]->b[i+1]=pD[0]->LLR[i+1]=0;
pD[0]->b[i+2]=pD[0]->LLR[i+2]=0;
pD[0]->b[i+3]=pD[0]->LLR[i+3]=0;
}

而在主函數當中,它是以這樣的形式被調用的:

/譯碼部分 
int j,i,u=N,p=N;
for(j=n;j>0;j--)
{u/=2;
for(i=0;i<u;i++)
{pD[0]->a[i+u]=f(pD[0]->a[i+p],pD[0]->a[i+u+p]);}
p/=2;
}
int password_1[6]={0,1,0,2,0,1}; 
int clue=0,clue0=0,clue1=0,clue2=0;
for(Vi=0;Vi<6;Vi++)
{
switch(password_1[Vi])
{
	case 0:first_4(clue);clue+=4;break;
	case 1:function_back(clue0,2,0);clue0+=8;break;
	case 2:function_back(clue1,3,0);clue1+=16;break;
}
}

至於function_back函數的功能,我們前面也講過,不過這次,function_back函數多了一個形參,我們待會解釋,先上代碼:

inline void function_back(int i,int v,int l)
{int u=(int)pow(2.0,v);
if(v==3){
for(int x=i;x<i+4;x++)
{pD[l]->b[x]=pD[l]->b[x]^pD[l]->b[x+4]?1:0;}
}
else if(v==4){
for(int x=i+8;x<i+12;x++)  
pD[l]->b[x]=pD[l]->b[x]^pD[l]->b[x+4];
for(int x=i;x<i+8;x++)
{pD[l]->b[x]=pD[l]->b[x]^pD[l]->b[x+8]?1:0;}
}
else if(v==5){
for(int x=i+24;x<i+28;x++)  
pD[l]->b[x]=pD[l]->b[x]^pD[l]->b[x+4];
for(int x=i+16;x<i+24;x++)
{pD[l]->b[x]=pD[l]->b[x]^pD[l]->b[x+8]?1:0;}
for(int x=i;x<i+16;x++)
{pD[l]->b[x]=pD[l]->b[x]^pD[l]->b[x+16]?1:0;}
}
for(int x=i;x<i+u;x++)
{pD[l]->a[x+2*u]=g(pD[l]->a[x+2*u],pD[l]->a[x+3*u],pD[l]->b[x]);
}
int p1=u,p2=0;
for(int j=v;j>0;j--)
{for(int x=i;x<i+p1/2;x++)
{pD[l]->a[x+u+p1/2]=f(pD[l]->a[x+2*p1+p2],pD[l]->a[x+2*p1+p2+p1/2]);}
p2+=p1/2;
p1/=2;
}
} 

爲確保代碼完整性,fg運算函數咱也照常展示:

float f(float Lin1,float Lin2)
{float Lout,s,min;
int sign;
s=Lin1*Lin2;
if(s>0)sign=1;
else sign=-1;
min=fabs(Lin1)<=fabs(Lin2)?fabs(Lin1):fabs(Lin2);
Lout=sign*min;
return Lout;
}
float g(float Lin1,float Lin2,int b)
{float Lout;
Lout=pow((float)-1.0,b)*Lin1+Lin2;
return Lout;
}

請注意,這幾個函數聲明與具體內容都是在主函數之外的。

2).在第一次分裂之前,將路徑1的譯碼值LLR、b值與浮點比特值a賦值給2、3、4路徑,第5條路經不需要。

//深度拷貝
for(int y=0;y<L;y++)
for(int x=0;x<N*2;x++)
pD[y]->a[x]=pD[0]->a[x];
for(int y=0;y<L;y++)
for(int x=0;x<N/4;x++)
pD[y]->b[x]=pD[0]->b[x];
for(int y=0;y<L;y++)
for(int x=0;x<clue;x++)
pD[y]->LLR[x]=pD[0]->LLR[x];

3).第一次分裂函數如下,具體分裂點爲函數中的“i+3",即第16位比特:

void first_split(int i)
{float temp[L];
for(int l=0;l<L;l++){
if(!CBR[i])pD[l]->b[i]=0;
else pD[l]->b[i]=pD[l]->a[i+1]>0.0?0:1;
pD[l]->LLR[i]=pD[l]->b[i];
pD[l]->a[i+1]=g(pD[l]->a[i+2],pD[l]->a[i+3],pD[l]->b[i]);
if(!CBR[i+1])pD[l]->b[i+1]=0;
else pD[l]->b[i+1]=pD[l]->a[i+1]>0.0?0:1;
pD[l]->LLR[i+1]=pD[l]->b[i+1];
pD[l]->b[i]=pD[l]->b[i]^pD[l]->b[i+1]?1:0; 
temp[l]=pD[l]->a[i+2]=g(pD[l]->a[i+4],pD[l]->a[i+6],pD[l]->b[i]);
pD[l]->a[i+3]=g(pD[l]->a[i+5],pD[l]->a[i+7],pD[l]->b[i+1]);
pD[l]->a[i+2]=f(pD[l]->a[i+2],pD[l]->a[i+3]);
if(!CBR[i+2])pD[l]->b[i+2]=0;
else pD[l]->b[i+2]=pD[l]->a[i+2]>0.0?0:1;
pD[l]->LLR[i+2]=pD[l]->b[i+2];
pD[l]->a[i+3]=g(temp[l],pD[l]->a[i+3],pD[l]->b[i+2]);
//split
if(l<L/2){pD[l]->b[i+3]=0;
if(pD[l]->a[i+3]<0.0)PM[l]+=pD[l]->a[i+3];
}
else{pD[l]->b[i+3]=1;
if(pD[l]->a[i+3]>0.0)PM[l]-=pD[l]->a[i+3];
}
pD[l]->LLR[i+3]=pD[l]->b[i+3];
pD[l]->b[i+2]=pD[l]->b[i+2]^pD[l]->b[i+3]?1:0;
pD[l]->b[i]=pD[l]->b[i]^pD[l]->b[i+2]?1:0;
pD[l]->b[i+1]=pD[l]->b[i+1]^pD[l]->b[i+3]?1:0;
}
}

類似的,第二次分裂函數也恰巧在第”i+3“位比特分裂,即第24位比特:

void second_split(int i)
{float temp[L];
for(int l=0;l<L;l++){
if(pD[l]->a[i+1]<0.0)PM[l]+=pD[l]->a[i+1];
pD[l]->LLR[i]=pD[l]->b[i]=0;
pD[l]->a[i+1]=g(pD[l]->a[i+2],pD[l]->a[i+3],pD[l]->b[i]);
if(pD[l]->a[i+1]<0.0)PM[l]+=pD[l]->a[i+1];
pD[l]->LLR[i+1]=pD[l]->b[i+1]=0;
pD[l]->b[i]=pD[l]->b[i]^pD[l]->b[i+1]?1:0; 
temp[l]=pD[l]->a[i+2]=g(pD[l]->a[i+4],pD[l]->a[i+6],pD[l]->b[i]);
pD[l]->a[i+3]=g(pD[l]->a[i+5],pD[l]->a[i+7],pD[l]->b[i+1]);
pD[l]->a[i+2]=f(pD[l]->a[i+2],pD[l]->a[i+3]);
if(pD[l]->a[i+2]<0.0)PM[l]+=pD[l]->a[i+2];
pD[l]->LLR[i+2]=pD[l]->b[i+2]=0;
pD[l]->a[i+3]=g(temp[l],pD[l]->a[i+3],pD[l]->b[i+2]);
}
//split
for(int l=0;l<L;l++){
if(!(l%2)){pD[l]->LLR[i+3]=pD[l]->b[i+3]=0;
if(pD[l]->a[i+3]<0.0)PM[l]+=pD[l]->a[i+3];
}
else{pD[l]->LLR[i+3]=pD[l]->b[i+3]=1;
if(pD[l]->a[i+3]>0.0)PM[l]-=pD[l]->a[i+3];}
pD[l]->b[i+2]=pD[l]->b[i+2]^pD[l]->b[i+3]?1:0;
pD[l]->b[i]=pD[l]->b[i]^pD[l]->b[i+2]?1:0;
pD[l]->b[i+1]=pD[l]->b[i+1]^pD[l]->b[i+3]?1:0;
}
}

在main函數中,它們是這樣被調用的:

//第一次分裂 
first_split(clue);clue+=4;
//由於function_back函數內部並無循環計算四條路經的功能,因此,我們特別給其增加多一個參數,循環調用,以覆蓋全部路徑的計算
for(int x=0;x<L;x++)function_back(clue2,4,x);clue2+=32;
first_4_plus(clue);clue+=4;
for(int x=0;x<L;x++)function_back(clue0,2,x);clue0+=8;
//第二次分裂
second_split(clue);clue+=4;

4).最後是first_4_plus函數,這個名字一聽就知道是first_4函數的加強版,它的功能相對複雜:在固定位,它計算每條路徑的PM值大小;在信息位,它調用sort函數對各路徑PM值進行排序,根據sort函數篩選出兩條最佳路徑,然後繼續分裂。因此,它涵蓋的功能非常強大,它也可以直接替代first_4函數,但遵循最簡化原則,我們不必這麼做。

先介紹sort排序函數,它會將最佳路徑複製給第5條路徑,然後,將次佳路徑複製給路徑2和4,接着,再將路徑5複製給1和3,請務必留意,數組的第一位元素是從0數起的。至於複製的範圍,我們這次講的是保守範圍,比起lazy copy,會浪費大量的時間進行循環複製:浮點比特a是該比特值到碼長的1.5倍;LLR值作爲一種結果而非中間計算,它只需呈現已有的譯碼結果;而b值的複製採用全碼長覆蓋,因爲假使你計算了第300位的b值,而全面的b值已覆蓋到第512位,你仍有較大概率會使用其他(更好的)路徑剩餘的b值去進行g運算。

void sort(int tail)
{int i,j,c;
for(i=0;i<L;i++)v[i]=i;
for(i=0;i<L-1;i++)
   for(j=0;j<L-1-i;j++){
   	if(PM[v[j]]<PM[v[j+1]]){
   		c=v[j];
   		v[j]=v[j+1];
   		v[j+1]=c;
	   }
   }
tail+=1;
for(i=tail;i<N+N/2;i++)pD[4]->a[i]=pD[v[0]]->a[i];
for(i=0;i<N;i++){
	pD[4]->b[i]=pD[v[0]]->b[i];
	pD[4]->LLR[i]=pD[v[0]]->LLR[i];
}
for(i=tail;i<N+N/2;i++)pD[1]->a[i]=pD[3]->a[i]=pD[v[1]]->a[i];
for(i=0;i<N;i++){
	pD[1]->b[i]=pD[3]->b[i]=pD[v[1]]->b[i];
	pD[1]->LLR[i]=pD[3]->LLR[i]=pD[v[1]]->LLR[i];
}
PM[1]=PM[3]=PM[v[1]];
for(i=tail;i<=N+N/2;i++)pD[0]->a[i]=pD[2]->a[i]=pD[4]->a[i];
for(i=0;i<N;i++){
	pD[0]->b[i]=pD[2]->b[i]=pD[4]->b[i];
	pD[0]->LLR[i]=pD[2]->LLR[i]=pD[4]->LLR[i];
}
PM[0]=PM[2]=PM[v[0]];
}

接着,first_4_plus函數也就很好理解了:

void first_4_plus(int i)
{float temp[L]={};
if(!CBR[i])
for(int l=0;l<L;l++){pD[l]->b[i]=0;
if(pD[l]->a[i+1]<0.0)PM[l]+=pD[l]->a[i+1];
}
else{sort(i-1);
for(int l=0;l<L;l++)
if(l<L/2){pD[l]->b[i]=0;
if(pD[l]->a[i+1]<0.0)PM[l]+=pD[l]->a[i+1];
}
else{pD[l]->b[i]=1;
if(pD[l]->a[i+1]>0.0)PM[l]-=pD[l]->a[i+1];
}
}
for(int l=0;l<L;l++){
pD[l]->LLR[i]=pD[l]->b[i];
pD[l]->a[i+1]=g(pD[l]->a[i+2],pD[l]->a[i+3],pD[l]->b[i]);
}
if(!CBR[i+1])
for(int l=0;l<L;l++){pD[l]->b[i+1]=0;
if(pD[l]->a[i+1]<0.0)PM[l]+=pD[l]->a[i+1];
}
else{sort(i);
for(int l=0;l<L;l++)
if(l<L/2){pD[l]->b[i+1]=0;
if(pD[l]->a[i+1]<0.0)PM[l]+=pD[l]->a[i+1];
}
else{pD[l]->b[i+1]=1;
if(pD[l]->a[i+1]>0.0)PM[l]-=pD[l]->a[i+1];
}
}
for(int l=0;l<L;l++){
pD[l]->LLR[i+1]=pD[l]->b[i+1];
pD[l]->b[i]=pD[l]->b[i]^pD[l]->b[i+1]?1:0; 
temp[l]=pD[l]->a[i+2]=g(pD[l]->a[i+4],pD[l]->a[i+6],pD[l]->b[i]);
pD[l]->a[i+3]=g(pD[l]->a[i+5],pD[l]->a[i+7],pD[l]->b[i+1]);
pD[l]->a[i+2]=f(pD[l]->a[i+2],pD[l]->a[i+3]);
}
if(!CBR[i+2])
for(int l=0;l<L;l++){pD[l]->b[i+2]=0;
if(pD[l]->a[i+2]<0.0)PM[l]+=pD[l]->a[i+2];
}
else{sort(i+1);
for(int l=0;l<L;l++)
if(l<L/2){pD[l]->b[i+2]=0;
if(pD[l]->a[i+2]<0.0)PM[l]+=pD[l]->a[i+2];
}
else{pD[l]->b[i+2]=1;
if(pD[l]->a[i+2]>0.0)PM[l]-=pD[l]->a[i+2];
}
}
for(int l=0;l<L;l++){
pD[l]->LLR[i+2]=pD[l]->b[i+2];
pD[l]->a[i+3]=g(temp[l],pD[l]->a[i+3],pD[l]->b[i+2]);
}
if(!CBR[i+3])
for(int l=0;l<L;l++){pD[l]->b[i+3]=0;
if(pD[l]->a[i+3]<0.0)PM[l]+=pD[l]->a[i+3];
}
else{sort(i+2);
for(int l=0;l<L;l++)
if(l<L/2){pD[l]->b[i+3]=0;
if(pD[l]->a[i+3]<0.0)PM[l]+=pD[l]->a[i+3];
}
else{pD[l]->b[i+3]=1;
if(pD[l]->a[i+3]>0.0)PM[l]-=pD[l]->a[i+3];
}
}
for(int l=0;l<L;l++){
pD[l]->LLR[i+3]=pD[l]->b[i+3];
pD[l]->b[i+2]=pD[l]->b[i+2]^pD[l]->b[i+3]?1:0;
pD[l]->b[i]=pD[l]->b[i]^pD[l]->b[i+2]?1:0;
pD[l]->b[i+1]=pD[l]->b[i+1]^pD[l]->b[i+3]?1:0;
}
}

在主函數當中,它們的具體調用如下:

int password_2[20]={2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0};
for(Vi=0;Vi<20;Vi++)
{
switch(password_2[Vi])
{
	case 0:first_4_plus(clue);clue+=4;break;
	case 1:for(int x=0;x<L;x++)function_back(clue0,2,x);clue0+=8;break;
	case 2:for(int x=0;x<L;x++)function_back(clue1,3,x);clue1+=16;break;
	case 3:for(int x=0;x<L;x++)function_back(clue2,4,x);clue2+=32;break;
	case 4:for(int x=0;x<L;x++)function_back(0,5,x);break;
}
}
//譯碼全過程結束

譯碼真的結束了嗎!?不,別忘了我們的排序機制,是“排前不排後”的,而最後一個比特是信息比特,它是經過上一位信息比特的路徑篩選後,進行了分裂但沒有確定譯碼值的比特。因此,別忘了加上這一句:

sort(N-1);

最終,最佳路徑就是第1條了。我們將最佳路徑與原碼序列進行對比,輸出運行結果,程序結束。

/輸出端
printf("原碼序列:");
for(i=0;i<N/2;i++)printf("%d ",pE->code[i]);
printf("\n         ");
for(i=N/2;i<N;i++)printf("%d ",pE->code[i]);
printf("\n譯碼序列:");
for(i=0;i<N/2;i++)printf("%d ",pD[0]->LLR[i]);
printf("\n         ");
for(i=N/2;i<N;i++)printf("%d ",pD[0]->LLR[i]);
//判斷正誤 
int w=0; 
for(int i=0;i<N;i++)
{if(pE->code[i]^pD[0]->LLR[i]){w++,s++;}
}
if(w)printf("\n結果:wrong\n");
else printf("\n結果:right\n");
return 0;
}

Part 4.程序驗收

最後,感謝耐心觀看。做一件事情很容易,但把事情做好卻很難。同樣,想要了解極化碼算法不難,但想要真正的去領悟其編程實操,甚至是自己動手去寫代碼,都需要經歷無數個bug的洗禮與一定時間的“煎熬”。能看到這裏,相信大家對SCL的譯碼過程的瞭解已有一定深度,編者水平有限,歡迎對文章中有錯誤的地方進行指正,也非常歡迎分享不同的思路。

 

發佈了9 篇原創文章 · 獲贊 14 · 訪問量 4699
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章