编程实现一种运动估计算法_BLE(4) -- 频偏估计算法的实现

eeeac24031731d76b8d9233ce63335e1.png

本文使用 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,计算后的结果为:

04e37c405fd7498102121808afc66d7f.png
Image


随后进行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

仿真结果如下:

4d86879f856a65b35e46e37126192c11.png
Image


这里发现最后cordic计算相位的输出有点问题,但是我最后输入进cordic module的I,Q信号分别为:

320e4fdd0232ad2a5cb92d02f7c2f4ce.png
Image


进matlab验证:

bb41378f3d2f5307ca354bd0c1d7c7af.png
Image

发现结果计算出来的应该也是正确的0.0733和0.075在误差范围内,应该是我使用的xilinx cordic module限制了输入数据的幅值,需要将累加的输入数据进行归一化处理,不过这里如果需要进行频偏补偿的化不需要获得这个相位值,这个相位值是为了输出观察需要的,所以暂时这个问题有空再去处理~

最后不得不说大佬写的VScode ON ZHihu 插件真好用!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值