OFDM接收机学习-第二章 符号同步模块FPGA的实现

系列文章目录

第一章 分组检测模块FPGA的实现

第二章 符号同步模块FPGA的实现


文章目录
前言

一、符号同步的目的

二、符号同步算法描述

2.1符号同步原理

2.2符号同步算法简化

2.3符号同步偏移的影响

2.4符号同步的硬件结构

三、符号同步算法在FPGA上的实现

3.1量化模块

3.2匹配滤波模块

[3.2.1 相关累加计算](#3.2.1 相关累加计算)

3.2.2幅值简化计算

3.2.3峰值寻找

3.3符号输出模块

总结


前言

第一章根据短序列的周期性以及延迟相关并长度保持算法进行了判断新数据分组到来的判断。输出的结果就是根据输入给与一个大概位置的有效标志,之后更加精确的判断交给后面的其他模块。因为本实验只使用了一个载波,所以不需要进行载波同步,直接进入符号同步,符号同步会充分利用短序列,燃烧最后价值。消除空闲间隔,输出长序列以及有效数据。

上板环境:

ILA抓取波形:可以看到短序列消失,只输出了长序列和数据域的有效值。3.2us


一、符号同步的目的

符号同步是对细节的打磨,是求得单个OFDM符号开始以及结束的精确时刻。

二、符号同步算法描述

2.1符号同步原理

充分利用已知的前导结构,也就是短序列。对于发射机和接收机来讲,短序列的数值本身就是固定的,即使通过传输也不会发生太大改变。将接受到的实际信号与已知的短序列进行互相关计算,进而进一步定位符号开始和结束的准备时刻。

将接受的数据与本地已知的段训练序列进行互相关计算,可依据公式1,D毋庸置疑就是窗口的长度,也就是短序列的点数,16点。其他的参数跟第一章基本一致。

(1)

当新的数据来到时,最前面的10个段训练符号依次进行互相关计算,结合第一章的计算过程可以想到,由于数值的相关性,理论情况下,在每个段训练符号的结束时刻就是相关数值的峰值,因此10个段训练符号会得到10个峰值。因此我们只需要判断这个峰值大于某个阈值然后进行累加10次,就可以判断出每个符号的结束时刻。

2.2符号同步算法简化

关于寻找峰值的优化,设定一个阈值,超过这个阈值就是出现了峰,即找到了峰值。

短训练序列为16点,每一个都是复数的数据类型,消耗的乘法器资源过多。因此采用持续累加短序列符号的方法降低噪声的影响,并且将接收到的信号量化为{-1,+1},大大节省了硬件资源并提高了运算速度。

简化完的效果也可以实现预想的效果,win。

2.3符号同步偏移的影响

由于多径效应的影响,可能造成符号同步估计的时刻不准,可能正好,可能提前,可能落后。一旦偏了就会对后面数据域的取值造成偏移,进而fft计算结果出现错误。

分析一下危害:正好不多说了,理想状态。提前就是会多一点循环前缀,这个对结果影响不是很大。如果落后了,会包括下一个符号的循环前缀,这样影响比较大。所以取点的时候,一般直接前移一些点。教材给出的经验值是4-6点,这个具体的值还要根据你自己的实际情况进行更改。

2.4符号同步的硬件结构

三步走,量化,匹配滤波,符号输出。

当分组检测模块的输出数据到达符号同步模块时,为了降低运算复杂度及对资源的需求,首先通过量化器把接收到的信号量化为(-1,1)。量化后的接收信号在接下来的匹配滤波器中进行与本地短训练符号的相关计算、相关值累加、幅值计算及峰值的寻找,最后确定短训练符号的结束位置。符号输出模块负责在找到短训练符号的结束点后,按后续处理要求去除循环前缀,输出长训练符号与数据符号:且当接收数据不是symbol长度的整数倍时,在其后补零以满足整数倍条件。由于短训练符号在后续模块均不再使用,故此模块不再输出短训练符号。

三、符号同步算法在FPGA上的实现

3.1量化模块

短序列符号16点,持续累加后,分别将16个最高位缓存下来,正为0,负为1。 先把传过来的实部和虚部缓存下来。

复制代码
reg [7:0]BufferDataR;
reg [7:0]BufferDataI;

always @(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			begin
				BufferEnable <= 0;
				BufferDataR <= 0;
				BufferDataI <= 0;
			end
		else
			begin
				if(inEn)
					begin
						BufferEnable <= 1;
						BufferDataR <= bitInR;
						BufferDataI <= bitInI;
					end
				else
					begin
						BufferEnable <= 0;
						BufferDataR <= 0;
						BufferDataI <= 0;
					end
			end
	end

进行扩展累加缓存

复制代码
reg [191:0] Continual_Accumulation_Real;		  
reg [191:0] Continual_Accumulation_Imag;
/*持续累加有效信号 *//
reg AddEnable;	
//********************************取得量化结果********************************//
always @ (posedge Clk or negedge Rst_n)
	begin
   	if (!Rst_n)
			begin
				Continual_Accumulation_Real <= 0;	
				Continual_Accumulation_Imag <= 0;
				AddEnable <= 0;
			end
		else
			begin
				if(BufferEnable)
					begin
						/*持续累加移位寄存器左移一个单元*****//
						Continual_Accumulation_Real[191:12] <= Continual_Accumulation_Real[179:0];	  
						Continual_Accumulation_Imag[191:12] <= Continual_Accumulation_Imag[179:0];
						/*最高一个单元与输入数据的12位扩展相加赋值给最低一个单元****//
						Continual_Accumulation_Real[11:0] <= {{4{BufferDataR[7]}},BufferDataR} + Continual_Accumulation_Real[191:180]; 
						Continual_Accumulation_Imag[11:0] <= {{4{BufferDataI[7]}},BufferDataI} + Continual_Accumulation_Imag[191:180];
						AddEnable <= 1;
					end
				else
					begin
						Continual_Accumulation_Real <= 0;
						Continual_Accumulation_Imag <= 0;
						AddEnable <= 0;
					end
			end
	end

将最低位12位的最高位缓存到量化数组中

复制代码
always @(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			begin
				Quantization_Result_Real <= 0;
				Quantization_Result_Imag <= 0;	
				QuantizationEnable <= 0;
			end
		else
			begin
				if(AddEnable)
					begin
						QuantizationEnable <= 1;
						/*量化结果移位******//
						Quantization_Result_Real[14:0] <= Quantization_Result_Real[15:1];			
						Quantization_Result_Imag[14:0] <= Quantization_Result_Imag[15:1];
						/*最高位为0,表正,量化为+1, 在此用0代替,下面语句只是用于判断******//
						Quantization_Result_Real[15] <= Continual_Accumulation_Real[11];
						/*最高位为1,表负,量化为-1,在此用1表示******//
						Quantization_Result_Imag[15] <= Continual_Accumulation_Imag[11];	 
					end
				else
					begin
						Quantization_Result_Real <= 0;
						Quantization_Result_Imag <= 0;	
						QuantizationEnable <= 0;
					end
			end
	end

3.2匹配滤波模块

匹配滤波模块主要负责寻找各个短训练符号的结束点。经过量化处理后,接收信号送入匹配滤波单元,首先与本地16个短训练符号取样进行相关计算,并将相关值累加求和,然后通过寻找其幅值的峰值来确定各个短训练符号的结束点,其实现结构如图所示,可分为相关及累加计算、幅值简化计算和峰值寻找三部分。

3.2.1 相关累加计算

将量化后的数据与本地的短序列共轭进行相关计算。

由于量化的存在,所以共轭乘积就可以简化成了4中加减法运算:

先把传过来的量化数据存下来:

复制代码
//the enable signal Buffer
reg BufferEnable;
//the input datas buffer
reg [15:0]BufferDataR;
reg [15:0]BufferDataI;

always @(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			begin
				BufferEnable <= 0;
				BufferDataR <= 0;
				BufferDataI <= 0;
			end
		else
			begin
				if(QuanEnable)
					begin
						BufferEnable <= 1;
						BufferDataR <= QuanDataR;
						BufferDataI <= QuanDataI;
					end
				else
					begin
						BufferEnable <= 0;
						BufferDataR <= 0;
						BufferDataI <= 0;
					end
			end
	end

进行16点的计数,因为STS16点

复制代码
reg [4:0]Counter;
reg TempEnable;
always @(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			begin
				Counter <= 0;
				TempEnable <= 0;
			end
		else
			begin
				if(BufferEnable)
					begin
						if(Counter < 15)  //when the counter is 16, the enable of correlation is set high, beacuse the lenght of the STS is 16 
							Counter <= Counter + 1;
						else
							TempEnable <= 1;
					end
				else
					begin
						Counter <= 0;
						TempEnable <= 0;
					end
			end
	end

进行计算,调用模块,改变对应的输入即可,4种情况

复制代码
 always @ (posedge Clk or negedge Rst_n)	
    begin
	    if (!Rst_n)
		   begin
				output_Real <= 0;
				output_Imag <= 0;
				OutputEnable <= 0;
			end
	    else if (inEn)
		   begin
				OutputEnable <= 1;		  								              
			   if (multiplier_Real == 0 && multiplier_Imag == 0)			 
				  begin
                 output_Real <= {{1{known_Real[15]}},known_Real} + {{1{known_Imag[15]}},known_Imag};
					  output_Imag <= {{1{known_Real[15]}},known_Real} - {{1{known_Imag[15]}},known_Imag};
				  end
			   else if (multiplier_Real == 0 && multiplier_Imag == 1)
				  begin
                 output_Real <= {{1{known_Real[15]}},known_Real} - {{1{known_Imag[15]}},known_Imag};
					  output_Imag <= - {{1{known_Real[15]}},known_Real} - {{1{known_Imag[15]}},known_Imag};
				  end
			   else if (multiplier_Real == 1 && multiplier_Imag == 0)
				  begin
				     output_Real <= - {{1{known_Real[15]}},known_Real} + {{1{known_Imag[15]}},known_Imag};
					  output_Imag <= {{1{known_Real[15]}},known_Real} + {{1{known_Imag[15]}},known_Imag};
				  end
			   else  //(buffer_multiplier_Real==1 && buffer_multiplier_Imag==1)
				  begin
				     output_Real <= - {{1{known_Real[15]}},known_Real} - {{1{known_Imag[15]}},known_Imag};
					  output_Imag <= - {{1{known_Real[15]}},known_Real} + {{1{known_Imag[15]}},known_Imag};
				  end
			end
	   else		 
			begin
		      output_Real <= 0;
		      output_Imag <= 0;
				OutputEnable <= 0;
		   end            
	 end

都算完了,进行累加

复制代码
always @(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			begin
				CorrelationEnable <= 0;
				CorrelationSumR <= 0;
				CorrelationSumI <= 0;
			end
		else
			begin
				if(TempEnable&CorrelationEn0&CorrelationEn1&CorrelationEn2&CorrelationEn3&CorrelationEn4&CorrelationEn5
				   &CorrelationEn6&CorrelationEn7&CorrelationEn8&CorrelationEn9&CorrelationEn10&CorrelationEn11
					&CorrelationEn12&CorrelationEn13&CorrelationEn14&CorrelationEn15)
					begin
						/*赋值语句右边的操作数位宽已扩展为21位,跟左边的结果位宽一致,保证带符号位二进制加法运算的正确性*******//
						CorrelationSumR <= ((({{4{CorrelationR0[16]}},CorrelationR0} + {{4{CorrelationR1[16]}},CorrelationR1}) 
						                   + ({{4{CorrelationR2[16]}},CorrelationR2} + {{4{CorrelationR3[16]}},CorrelationR3})) 
						                   + (({{4{CorrelationR4[16]}},CorrelationR4} + {{4{CorrelationR5[16]}},CorrelationR5})
												 + ({{4{CorrelationR6[16]}},CorrelationR6} + {{4{CorrelationR7[16]}},CorrelationR7}))) 
												 + ((({{4{CorrelationR8[16]}},CorrelationR8} + {{4{CorrelationR9[16]}},CorrelationR9})
												 + ({{4{CorrelationR10[16]}},CorrelationR10} + {{4{CorrelationR11[16]}},CorrelationR11})) 
												 + (({{4{CorrelationR12[16]}},CorrelationR12} + {{4{CorrelationR13[16]}},CorrelationR13})
												 + ({{4{CorrelationR14[16]}},CorrelationR14} + {{4{CorrelationR15[16]}},CorrelationR15})));
	   			   CorrelationSumI <= ((({{4{CorrelationI0[16]}},CorrelationI0} + {{4{CorrelationI1[16]}},CorrelationI1}) 
						                   + ({{4{CorrelationI2[16]}},CorrelationI2} + {{4{CorrelationI3[16]}},CorrelationI3})) 
						                   + (({{4{CorrelationI4[16]}},CorrelationI4} + {{4{CorrelationI5[16]}},CorrelationI5})
												 + ({{4{CorrelationI6[16]}},CorrelationI6} + {{4{CorrelationI7[16]}},CorrelationI7}))) 
												 + ((({{4{CorrelationI8[16]}},CorrelationI8} + {{4{CorrelationI9[16]}},CorrelationI9})
												 + ({{4{CorrelationI10[16]}},CorrelationI10} + {{4{CorrelationI11[16]}},CorrelationI11})) 
												 + (({{4{CorrelationI12[16]}},CorrelationI12} + {{4{CorrelationI13[16]}},CorrelationI13})
												 + ({{4{CorrelationI14[16]}},CorrelationI14} + {{4{CorrelationI15[16]}},CorrelationI15})));
						CorrelationEnable <= 1;
					end
				else
					begin
						CorrelationEnable <= 0;
						CorrelationSumR <= 0;
						CorrelationSumI <= 0;
					end
			end
	end

3.2.2幅值简化计算

在得到相关值累加和后,需要计算其幅值,用于峰值判断。显然如果直接计算幅值,不仅需要乘法操作还要开方运算,非常不利于硬件实现。硬件实现上进行式(5-10)简化,即计算输入数据的实部和虚部的绝对值之和,近似其幅值,结构如图所示。首先分别判断实部和虚部是否为正数,若为正数则保持不变,若为负数则其相反数,然后相加。

复制代码
 always @ (posedge Clk or negedge Rst_n)				    /*输入增加一级缓存,延迟一个时钟周期******//
	    begin
		    if (!Rst_n)
			   begin
					BufferEnable <= 0;
               BufferDataRe <= 0;
			      BufferDataIm <= 0;			      
				end
			 else
			   begin					         	                /*缓存的数据为输入数据的绝对值******//
				   if(DataEnable)
						begin
							BufferEnable <= 1;
							if(DataInRe[20] == 0)                /*符号位为0,表示为正数,绝对值即为输入数******//
					  			begin
				        			BufferDataRe <= DataInRe;
					  			end
							else								          /*符号位为1,表示为负数,绝对值为输入数取反加1******//
					  			begin
					     			BufferDataRe <= ~ DataInRe + 1;
					  			end

				   		if (DataInIm[20] == 0)			       /*虚部运算同实部******//
					  			begin
				        			BufferDataIm <= DataInIm;
					  			end
							else
					  			begin
					     			BufferDataIm <= ~ DataInIm + 1;
					  			end
						end
					else
						begin
							BufferEnable <= 0;
							BufferDataRe <= 0;
							BufferDataIm <= 0;
						end
				end
		 end

    always @ (posedge Clk or negedge Rst_n) 
      begin
	      if (!Rst_n)
	   	  begin
				  Absolute <= 0;
				  AbsoluteEnable <= 0;
			  end
		   else
			  begin						 
			     if(BufferEnable)
				  	begin
						/*求取实部与虚部的绝对值之和******//
						Absolute <= {BufferDataRe[20],BufferDataRe} + {BufferDataIm[20],BufferDataIm};    
						AbsoluteEnable <= 1;
					end
				  else
				  	begin
						Absolute <= 0;
						AbsoluteEnable <= 0;
					end
			  end
       end							 

3.2.3峰值寻找

峰值寻找单元主要负责搜索接收的短训练符号取样与本地短训练符号相关累加和幅值的峰值,即短训练符号的结束点。考虑到硬件实现的复杂度,实现上不采用搜寻最大值的方法。而是采用制定门限值的方法来代替。即当累加值超过了预先设定的门限值时,即认为找到峰值。理论上在每一组短训练符号16个取样的结束点,都将产生一个峰值。但由于分组检测模块输出的第一组短训练符号是不完整的,所以本模块只能连续检测到9个峰值点。当检测到第9个峰值时,意味着短训练符号已经结束。

先缓存下来累加值

复制代码
reg BufferEnable;
//the input data buffer
reg [21:0]BufferData;

always @(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			begin
				BufferEnable <= 0;
				BufferData <= 0;
			end
		else
			begin
				if(DataEnable)
					begin
						BufferEnable <= 1;
						BufferData <= AbsoluteData;
					end
				else
					begin
						BufferEnable <= 0;
						BufferData <= 0;
					end
			end
	end

判断,计数峰值,判断的具体值根据实际结果进行修改

复制代码
reg [3:0] STS_end_counter;  /*峰值数目计数器******//
always @(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			begin
				STS_end_counter <= 0;
				PeakFinded <= 0;
			end
		else
			begin
				if(BufferEnable)
					begin      /*absolute_sum位宽22位(1位符号位 + 7位整数位 + 14位小数位)******//
						if(STS_end_counter < 9)
							begin
								if (BufferData > 22'b0000_0001_10_0000_0000_0000)//1.5
									begin
										STS_end_counter <= STS_end_counter + 1;  /*大于阈值,计数器加1******//
									end
								PeakFinded <= 0;
							end
						else
							begin
								PeakFinded <= 1;  /*当找到9个峰值,即短训练序列的结束位置,把信号拉高******//
							end
					end
				else
					begin                      /*帧结束时,把寄存器赋予初始值******//
						STS_end_counter <= 0;
						PeakFinded <= 0;
					end
			end
	end

3.3符号输出模块

由于数据符号均为16个循环前缀加64个有效数据,在去除循环前缀后数据格式为每64个有效数据之间间隔16个空数据(即这16个时钟没有数据),硬件实现上由一个同步使能信号有效来表示有效数据。而长训练为32个循环前缀加连续两个64样值的格式,为了使长训练符号在去除循环前缀后也保持统一的数据格式,操作上首先输出第一个长训练符号,而第二个长训练符号及后续的数据符号经过深度为16级的移位寄存器输出,如图5-37所示。此外,如果接收数据不为symbol长度的整数倍时,在其后补零以满足整数倍条件。

就是长序列是挨着的,先输出第一个,然后后面的数据统一延时16点

复制代码
always @(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			begin
				Counter1 <= 0;
				Counter2 <= 0;
				DataOutEnable <= 0;
				DataOutRe <= 0;
				DataOutIm <= 0;
				DataSymbol <= 0;
				BufferOutEnable <= 0;
				BufferDataSymbol <= 0;
				BufferDataOutRe <= 0;
				BufferDataOutIm <= 0;
				TempSymbol <= 0;
			end
		else
			begin
				if(PeakFinded)
					begin
						if(Counter1 <= 85)  //the counter is set by 85, because the processing (quantization,correlation,and so on) delay is 10 clock
							begin
								Counter2 <= 0;
								Counter1 <= Counter1 + 1;
								if(Counter1 >= 22 && Counter1 <= 85)//the cyclix prefix of the LTS is 32, and the delay is 10, thus 32-10=22
									begin
										DataOutEnable <= 1;
										DataOutRe <= DataInRe;
										DataOutIm <= DataInIm;
									end
								else
									begin
										DataOutEnable <= 0;
										DataOutRe <= 0;
										DataOutIm <= 0;
									end
								if(Counter1 == 22)
									begin
										DataSymbol <= DataSymbol + 1;
										TempSymbol <= DataSymbol + 1;
									end
							end
						else
							begin//when the value of counter2 is between 0 and 63, the input datas are the datas we need
							     //and when the valus of counter2 is between 64 and 79, the input datas are the cyclic prefix of the next symbol
								if(Counter2 == 79)
							 		begin
										Counter2 <= 0;
										TempSymbol <= TempSymbol + 1;
									end
								else
									begin
										Counter2 <= Counter2 + 1;
									end
								if(Counter2 >= 0 && Counter2 <= 63)
									begin
										BufferOutEnable[15] <= 1;
										BufferDataOutRe[127:120] <= DataInRe; 
										BufferDataOutIm[127:120] <= DataInIm;
									  	BufferDataSymbol[127:120] <= TempSymbol + 1;
									end
								else
									begin
										BufferOutEnable[15] <= 0;
										BufferDataOutRe[127:120] <= 0; 
										BufferDataOutIm[127:120] <= 0;
										BufferDataSymbol[127:120] <= 0;
									end
								BufferOutEnable[14:0] <= BufferOutEnable[15:1];
								BufferDataOutRe[119:0] <= BufferDataOutRe[127:8];
								BufferDataOutIm[119:0] <= BufferDataOutIm[127:8];
								DataOutEnable <= BufferOutEnable[0];
								DataOutRe <= BufferDataOutRe[7:0];
								DataOutIm <= BufferDataOutIm[7:0];
								BufferDataSymbol[119:0] <= BufferDataSymbol[127:8];
								DataSymbol <= BufferDataSymbol[7:0];
							end
					end
				else
					begin
						if(Counter2 == 0)   //the length of the input datas is an integral multiple of the length of symbol (64)  
							begin					
								BufferOutEnable <= 0;
								BufferDataOutRe <= 0;
								BufferDataOutIm <= 0;
								BufferDataSymbol <= 0;
								DataOutEnable <= 0;
								DataOutRe <= 0;
								DataOutIm <= 0;
								DataSymbol <= 0;
							end
						else     //if not, keeping output until it satifies (the values of counter2 is 79)
							begin
								if(Counter2 == 79)    
									Counter2 <= 0;
								else
									Counter2 <= Counter2 + 1;
								BufferDataSymbol[127:120] <= TempSymbol+1;
								BufferDataSymbol[119:0] <= BufferDataSymbol[127:8];
								DataSymbol <= BufferDataSymbol[7:0];
								BufferOutEnable[14:0] <= BufferOutEnable[15:1];
								BufferDataOutRe[119:0] <= BufferDataOutRe[127:8];
								BufferDataOutIm[119:0] <= BufferDataOutIm[127:8];
								DataOutEnable <= BufferOutEnable[0];
								DataOutRe <= BufferDataOutRe[7:0];
								DataOutIm <= BufferDataOutIm[7:0];
								BufferOutEnable[15] <= 1;
								BufferDataOutRe[127:120] <= DataInRe; 
								BufferDataOutIm[127:120] <= DataInIm;
							end										
						Counter1 <= 0;
					end
			end

前面确定短序列的结束位置,这部分一个是要控制延时多少输出,一个是做好计数就可以了


总结

理解教材公式,做好数据控制,验证数据计算。

实际上板需要结合实际更新阈值等数据。

代码参考教材:基于xilinx FPGA的OFDM通信系统基带设计

相关推荐
西岸行者8 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
ZPC82108 天前
docker 镜像备份
人工智能·算法·fpga开发·机器人
ZPC82108 天前
docker 使用GUI ROS2
人工智能·算法·fpga开发·机器人
悠哉悠哉愿意8 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码8 天前
嵌入式学习路线
学习
毛小茛8 天前
计算机系统概论——校验码
学习
babe小鑫8 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms8 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下8 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。8 天前
2026.2.25监控学习
学习