本文使用 Zhihu On VSCode 创作并发布
BLE(4) -- 频偏估计算法的实现
之前我们分析了OFDM系统中的定时同步和频偏估计,并对定时同步算法进行了verilog实现,对于BLE系统,频偏估计和OFDM系统类似,只不过由多载波系统变为单载波系统。
首先通过定时同步来确定好帧的起始位置,随后和本地的preamble相共轭去除调制信号的信息:
其中, |c(k)^{2}| = 1;f_d表示频偏;n(k)表示加性高斯白噪声。
这里我们就以最简单的延时相关算法进行实现,其计算公式为:
式中,D表示延时长度,n表示计算的窗口长度。
首先给出其matlab实现代码:
T = 1/16;
symbolPerbit = 16;
Ts = 1/16;
fd = 0.075;
k=[1:4096];
SNR = 15;
s = exp((j*2*pi*fd*k*Ts));
s_n = awgn(s,SNR);
Len = length(s_n);
%% CFO est
delay_len = symbolPerbit*2;
cal_len = symbolPerbit*5; % 5个符号的窗口长度
cfo_est_mtx = [];
corr_mtx = [];
for kk = 1:cal_len:Len-cal_len-delay_len
corr = sum(s_n(kk+delay_len:kk+cal_len+delay_len).*conj(s_n(kk:kk+cal_len)));
corr_mtx = [corr_mtx corr];
cfo_est = (1/(2*pi*T*delay_len)) * angle(corr);
cfo_est_mtx = [cfo_est_mtx cfo_est];
end
cfo_est_res = mean(cfo_est_mtx);
可以看出我设置的归一化频偏值为0.075,计算后的结果为:
随后进行verilog实现,实现思路和定时同步的方法类似,但是这里我采用了定点小数的实现方法。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/09/08 09:58:45
// Design Name:
// Module Name: CFO_est
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module CFO_est(
input clk,
input rst_n,
input data_in_vaild,
input signed [9-1:0] s_I_data,
input signed [9-1:0] s_Q_data,
input start_est_flag,
output cfo_est_vaild,
output signed [32-1:0] cfo_est_value
);
// delay_len = 2*SamplePerBits = 2*16 = 32; cal_window = 5*SamplePerBits = 5*16 = 80
// save data in shift reg
reg signed [9-1:0] Idata_delay [32-1:0]; // 1 1 7
reg signed [9-1:0] Qdata_delay [32-1:0];
always @(posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
Idata_delay[0] <= 9'd0;
Qdata_delay[0] <= 9'd0;
end
else
begin
Idata_delay[0] <= (data_in_vaild && start_est_flag) ? s_I_data : Idata_delay[0];
Qdata_delay[0] <= (data_in_vaild && start_est_flag) ? s_Q_data : Qdata_delay[0];
end
end
generate
genvar i;
for (i=0;i<32;i=i+1)
begin
always@(posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
Idata_delay[i+1] <= 9'd0;
Qdata_delay[i+1] <= 9'd0;
end
else
begin
Idata_delay[i+1] <= (data_in_vaild && start_est_flag) ? Idata_delay[i] : Idata_delay[i+1];
Qdata_delay[i+1] <= (data_in_vaild && start_est_flag) ? Qdata_delay[i] : Qdata_delay[i+1];
end
end
end
endgenerate
/*
[I(k+D) + jQ(K+D)] * [I(K) - jQ(K)] = I(K+D)I(K) + (-jQ(k)I(K+D)) + jQ(K+D)I(K) + Q(K+D)Q(K)
real_temp0 = I(K+D)I(K) ;
real_temp1 = Q(K+D)Q(K);
real_temp = real_temp0 + real_temp1;
imag_temp0 = Q(K+D)I(K) ;
imag_temp1 = Q(k)I(K+D)
imag_temp = imag_temp0 - imag_temp1;
*/
wire signed [18-1:0] real_temp0; // 1 1 7 * 1 1 7 = 1 3 14 = 18bit
wire signed [18-1:0] real_temp1;
wire signed [19-1:0] real_temp;
wire signed [18-1:0] imag_temp0;
wire signed [18-1:0] imag_temp1;
wire signed [19-1:0] imag_temp;
mult_conj mult_conj_real0_inst (
.CLK(clk), // input wire CLK
.A(Idata_delay[31]), // input wire [8 : 0] A
.B(Idata_delay[0]), // input wire [8 : 0] B
.P(real_temp0) // output wire [17 : 0] P
);
mult_conj mult_conj_real1_inst (
.CLK(clk), // input wire CLK
.A(Qdata_delay[31]), // input wire [8 : 0] A
.B(Qdata_delay[0]), // input wire [8 : 0] B
.P(real_temp1) // output wire [17 : 0] P
);
mult_conj mult_conj_imag0_inst (
.CLK(clk), // input wire CLK
.A(Qdata_delay[31]), // input wire [8 : 0] A
.B(Idata_delay[0]), // input wire [8 : 0] B
.P(imag_temp0) // output wire [17 : 0] P
);
mult_conj mult_conj_imag1_inst (
.CLK(clk), // input wire CLK
.A(Qdata_delay[0]), // input wire [8 : 0] A
.B(Idata_delay[31]), // input wire [8 : 0] B
.P(imag_temp1) // output wire [17 : 0] P
);
assign real_temp = real_temp0 + real_temp1;
assign imag_temp = imag_temp1 - imag_temp0;
// 19+7 = 26bits 1 4 14 * 80 = 1 11 14
parameter CAL_WINSIZE = 7'd79;
reg signed [26-1:0] sum_real;
reg signed [26-1:0] sum_imag;
reg [7-1:0] cal_cnt;
wire signed [26-1:0] cordic_in_real;
wire signed [26-1:0] cordic_in_imag;
wire cordic_in_vaild;
always @(posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
cal_cnt <= 7'd0;
sum_real <= 26'd0;
sum_imag <= 26'd0;
end
else
begin
if (cal_cnt <= CAL_WINSIZE && (data_in_vaild && start_est_flag))
begin
sum_real <= sum_real + real_temp;
sum_imag <= sum_imag + imag_temp;
cal_cnt <= cal_cnt + 1'b1;
end
else
begin
sum_imag <= 26'd0;
sum_real <= 26'd0;
cal_cnt <= 7'd0;
end
end
end
// send data in cordic module
assign cordic_in_real = (cal_cnt == CAL_WINSIZE) ? sum_real : 0;
assign cordic_in_imag = (cal_cnt == CAL_WINSIZE) ? sum_imag : 0;
assign cordic_in_vaild = (cal_cnt == CAL_WINSIZE) ? 1'b1 : 0;
reg [63 : 0] s_axis_cartesian_tdata;
always @(posedge clk or rst_n)
begin
if (!rst_n)
begin
s_axis_cartesian_tdata <= 64'd0;
end
else
begin
s_axis_cartesian_tdata[57:32] = cordic_in_imag;
s_axis_cartesian_tdata[25:0] = cordic_in_real;
end
end
wire signed [31 : 0] m_axis_dout_tdata;
cordic_angle cordic_angle_inst (
.aclk(clk), // input wire aclk
.s_axis_cartesian_tvalid(cordic_in_vaild), // input wire s_axis_cartesian_tvalid
.s_axis_cartesian_tdata(s_axis_cartesian_tdata), // input wire [63 : 0] s_axis_cartesian_tdata
.m_axis_dout_tvalid(cfo_est_vaild), // output wire m_axis_dout_tvalid
.m_axis_dout_tdata(m_axis_dout_tdata) // output wire [31 : 0] m_axis_dout_tdata
);
parameter DEALY_FACTOR = 9'b0_0_0001010; // 1 1 7
wire [35-1:0] cfo_est_value_temp;
assign cfo_est_value_temp = { {2{m_axis_dout_tdata[31]}},m_axis_dout_tdata,1'b0} + {m_axis_dout_tdata,3'b0};
assign cfo_est_value = cfo_est_value_temp[35-1:0+3];
endmodule
仿真结果如下:
这里发现最后cordic计算相位的输出有点问题,但是我最后输入进cordic module的I,Q信号分别为:
进matlab验证:
发现结果计算出来的应该也是正确的0.0733和0.075在误差范围内,应该是我使用的xilinx cordic module限制了输入数据的幅值,需要将累加的输入数据进行归一化处理,不过这里如果需要进行频偏补偿的化不需要获得这个相位值,这个相位值是为了输出观察需要的,所以暂时这个问题有空再去处理~
最后不得不说大佬写的VScode ON ZHihu 插件真好用!