mirror of
https://github.com/jhshi/openofdm.git
synced 2025-04-23 18:13:43 +00:00
feature update: sampling frequency offset (SFO) compensation
This commit is contained in:
parent
b40c221e67
commit
8730912d6f
@ -68,7 +68,7 @@ module dot11 (
|
||||
// equalizer
|
||||
output [31:0] equalizer_out,
|
||||
output equalizer_out_strobe,
|
||||
output [2:0] equalizer_state,
|
||||
output [3:0] equalizer_state,
|
||||
output wire ofdm_symbol_eq_out_pulse,
|
||||
|
||||
// legacy signal info
|
||||
@ -120,9 +120,9 @@ module dot11 (
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// extra info output to ease side info and viterbi state monitor
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
reg [2:0] equalizer_state_reg;
|
||||
reg [3:0] equalizer_state_reg;
|
||||
|
||||
assign ofdm_symbol_eq_out_pulse = (equalizer_state==4 && equalizer_state_reg==6);
|
||||
assign ofdm_symbol_eq_out_pulse = (equalizer_state==4 && equalizer_state_reg==7);
|
||||
|
||||
always @(posedge clock) begin
|
||||
if (reset==1) begin
|
||||
|
@ -24,7 +24,7 @@ module equalizer
|
||||
output reg [31:0] sample_out,
|
||||
output reg sample_out_strobe,
|
||||
|
||||
output reg [2:0] state,
|
||||
output reg [3:0] state,
|
||||
|
||||
// for side channel
|
||||
output wire [31:0] csi,
|
||||
@ -82,7 +82,7 @@ reg [63:0] pilot_mask;
|
||||
reg [126:0] polarity;
|
||||
reg [3:0] ht_polarity;
|
||||
reg [3:0] current_polarity;
|
||||
reg [3:0] pilot_count;
|
||||
reg [3:0] pilot_count1, pilot_count2;
|
||||
|
||||
reg signed [15:0] input_i;
|
||||
reg signed [15:0] input_q;
|
||||
@ -112,14 +112,33 @@ wire [15:0] buf_q_out;
|
||||
reg pilot_in_stb;
|
||||
wire signed [31:0] pilot_i;
|
||||
wire signed [31:0] pilot_q;
|
||||
reg signed [31:0] pilot_i_reg, pilot_q_reg;
|
||||
reg signed [15:0] pilot_iq_phase[0:3];
|
||||
|
||||
reg signed [31:0] pilot_sum_i;
|
||||
reg signed [31:0] pilot_sum_q;
|
||||
|
||||
assign phase_in_i = pilot_sum_i;
|
||||
assign phase_in_q = pilot_sum_q;
|
||||
assign phase_in_i = pilot_i_reg;
|
||||
assign phase_in_q = pilot_q_reg;
|
||||
|
||||
reg signed [15:0] pilot_phase;
|
||||
reg signed [15:0] pilot_phase_err;
|
||||
reg signed [15:0] cpe; // common phase error due to RFO
|
||||
reg signed [15:0] Sxy;
|
||||
localparam Sx2 = 980;
|
||||
|
||||
// linear varying phase error (LVPE) parameters
|
||||
reg signed [7:0] sym_idx;
|
||||
reg lvpe_in_stb;
|
||||
wire lvpe_out_stb;
|
||||
wire signed [31:0] lvpe_dividend, lvpe;
|
||||
wire signed [23:0] lvpe_divisor;
|
||||
assign lvpe_dividend = (sym_idx <= 33 ? sym_idx*Sxy : (sym_idx-64)*Sxy);
|
||||
assign lvpe_divisor = Sx2;
|
||||
|
||||
|
||||
reg signed [15:0] phase_err;
|
||||
wire signed [15:0] sym_phase;
|
||||
assign sym_phase = (phase_err > 1608) ? (phase_err - 3217) : ((phase_err < -1608) ? (phase_err + 3217) : phase_err);
|
||||
|
||||
reg rot_in_stb;
|
||||
wire signed [15:0] rot_i;
|
||||
@ -152,11 +171,11 @@ reg signed [18:0] lts_sum_q;
|
||||
reg [2:0] lts_mv_avg_len;
|
||||
reg lts_div_in_stb;
|
||||
|
||||
wire [31:0] dividend_i = (state == S_SMOOTH_CH_DC || state == S_SMOOTH_CH_LTS) ? (lts_sum_i[18] == 0 ? {13'h0,lts_sum_i} : {13'h1FFF,lts_sum_i}) : (state == S_ADJUST_FREQ_OFFSET ? prod_i_scaled : 0);
|
||||
wire [31:0] dividend_q = (state == S_SMOOTH_CH_DC || state == S_SMOOTH_CH_LTS) ? (lts_sum_q[18] == 0 ? {13'h0,lts_sum_q} : {13'h1FFF,lts_sum_q}) : (state == S_ADJUST_FREQ_OFFSET ? prod_q_scaled : 0);
|
||||
wire [23:0] divisor_i = (state == S_SMOOTH_CH_DC || state == S_SMOOTH_CH_LTS) ? {21'b0,lts_mv_avg_len} : (state == S_ADJUST_FREQ_OFFSET ? mag_sq[23:0] : 1);
|
||||
wire [23:0] divisor_q = (state == S_SMOOTH_CH_DC || state == S_SMOOTH_CH_LTS) ? {21'b0,lts_mv_avg_len} : (state == S_ADJUST_FREQ_OFFSET ? mag_sq[23:0] : 1);
|
||||
wire div_in_stb = (state == S_SMOOTH_CH_DC || state == S_SMOOTH_CH_LTS) ? lts_div_in_stb : (state == S_ADJUST_FREQ_OFFSET ? prod_out_strobe : 0);
|
||||
wire [31:0] dividend_i = (state == S_SMOOTH_CH_DC || state == S_SMOOTH_CH_LTS) ? (lts_sum_i[18] == 0 ? {13'h0,lts_sum_i} : {13'h1FFF,lts_sum_i}) : (state == S_ADJUST_FREQ_and_SAMPL_OFFSET ? prod_i_scaled : 0);
|
||||
wire [31:0] dividend_q = (state == S_SMOOTH_CH_DC || state == S_SMOOTH_CH_LTS) ? (lts_sum_q[18] == 0 ? {13'h0,lts_sum_q} : {13'h1FFF,lts_sum_q}) : (state == S_ADJUST_FREQ_and_SAMPL_OFFSET ? prod_q_scaled : 0);
|
||||
wire [23:0] divisor_i = (state == S_SMOOTH_CH_DC || state == S_SMOOTH_CH_LTS) ? {21'b0,lts_mv_avg_len} : (state == S_ADJUST_FREQ_and_SAMPL_OFFSET ? mag_sq[23:0] : 1);
|
||||
wire [23:0] divisor_q = (state == S_SMOOTH_CH_DC || state == S_SMOOTH_CH_LTS) ? {21'b0,lts_mv_avg_len} : (state == S_ADJUST_FREQ_and_SAMPL_OFFSET ? mag_sq[23:0] : 1);
|
||||
wire div_in_stb = (state == S_SMOOTH_CH_DC || state == S_SMOOTH_CH_LTS) ? lts_div_in_stb : (state == S_ADJUST_FREQ_and_SAMPL_OFFSET ? prod_out_strobe : 0);
|
||||
|
||||
|
||||
reg [15:0] num_output;
|
||||
@ -339,7 +358,7 @@ rotate rotate_inst (
|
||||
|
||||
.in_i(buf_i_out),
|
||||
.in_q(buf_q_out),
|
||||
.phase(pilot_phase),
|
||||
.phase(sym_phase),
|
||||
.input_strobe(rot_in_stb),
|
||||
|
||||
.rot_addr(rot_addr),
|
||||
@ -401,14 +420,29 @@ divider norm_q_inst (
|
||||
.quotient(quotient_q)
|
||||
);
|
||||
|
||||
// LVPE calculation to estimate SFO
|
||||
divider lvpe_inst (
|
||||
.clock(clock),
|
||||
.enable(enable),
|
||||
.reset(reset),
|
||||
|
||||
.dividend(lvpe_dividend),
|
||||
.divisor(lvpe_divisor),
|
||||
.input_strobe(lvpe_in_stb),
|
||||
|
||||
.quotient(lvpe),
|
||||
.output_strobe(lvpe_out_stb)
|
||||
);
|
||||
|
||||
localparam S_FIRST_LTS = 0;
|
||||
localparam S_SECOND_LTS = 1;
|
||||
localparam S_SMOOTH_CH_DC = 2;
|
||||
localparam S_SMOOTH_CH_LTS = 3;
|
||||
localparam S_GET_POLARITY = 4;
|
||||
localparam S_CALC_FREQ_OFFSET = 5;
|
||||
localparam S_ADJUST_FREQ_OFFSET = 6;
|
||||
localparam S_HT_LTS = 7;
|
||||
localparam S_CALC_SAMPL_OFFSET = 6;
|
||||
localparam S_ADJUST_FREQ_and_SAMPL_OFFSET = 7;
|
||||
localparam S_HT_LTS = 8;
|
||||
|
||||
always @(posedge clock) begin
|
||||
if (reset) begin
|
||||
@ -435,22 +469,32 @@ always @(posedge clock) begin
|
||||
ht_polarity <= HT_POLARITY;
|
||||
|
||||
current_polarity <= 0;
|
||||
pilot_count <= 0;
|
||||
pilot_count1 <= 0;
|
||||
pilot_count2 <= 0;
|
||||
|
||||
in_waddr <= 0;
|
||||
in_raddr <= 0;
|
||||
sym_idx <= 0;
|
||||
|
||||
lts_reg1_i <= 0; lts_reg2_i <= 0; lts_reg3_i <= 0; lts_reg4_i <= 0; lts_reg5_i <= 0;
|
||||
lts_reg1_q <= 0; lts_reg2_q <= 0; lts_reg3_q <= 0; lts_reg4_q <= 0; lts_reg5_q <= 0;
|
||||
lts_sum_i <= 0;
|
||||
lts_sum_q <= 0;
|
||||
lts_mv_avg_len <= 0;
|
||||
lts_div_in_stb <= 0;
|
||||
|
||||
phase_in_stb <= 0;
|
||||
pilot_sum_i <= 0;
|
||||
pilot_sum_q <= 0;
|
||||
pilot_phase <= 0;
|
||||
pilot_phase_err <= 0;
|
||||
cpe <= 0;
|
||||
Sxy <= 0;
|
||||
lvpe_in_stb <= 0;
|
||||
phase_err <= 0;
|
||||
pilot_in_stb <= 0;
|
||||
pilot_i_reg <= 0;
|
||||
pilot_q_reg <= 0;
|
||||
pilot_iq_phase[0] <= 0; pilot_iq_phase[1] <= 0; pilot_iq_phase[2] <= 0; pilot_iq_phase[3] <= 0;
|
||||
|
||||
prod_in_strobe <= 0;
|
||||
|
||||
@ -616,7 +660,9 @@ always @(posedge clock) begin
|
||||
|
||||
pilot_sum_i <= 0;
|
||||
pilot_sum_q <= 0;
|
||||
pilot_count <= 0;
|
||||
pilot_count1 <= 0;
|
||||
pilot_count2 <= 0;
|
||||
cpe <= 0;
|
||||
in_waddr <= 0;
|
||||
in_raddr <= 0;
|
||||
input_i <= 0;
|
||||
@ -647,7 +693,7 @@ always @(posedge clock) begin
|
||||
|
||||
pilot_mask <= {pilot_mask[0], pilot_mask[63:1]};
|
||||
if (pilot_mask[0]) begin
|
||||
pilot_count <= pilot_count + 1;
|
||||
pilot_count1 <= pilot_count1 + 1;
|
||||
current_polarity <= {current_polarity[0],
|
||||
current_polarity[3:1]};
|
||||
// obtain the conjugate of current pilot sub carrier
|
||||
@ -669,34 +715,90 @@ always @(posedge clock) begin
|
||||
if (pilot_out_stb) begin
|
||||
pilot_sum_i <= pilot_sum_i + pilot_i;
|
||||
pilot_sum_q <= pilot_sum_q + pilot_q;
|
||||
if (pilot_count == 4) begin
|
||||
phase_in_stb <= 1;
|
||||
end else begin
|
||||
phase_in_stb <= 0;
|
||||
end
|
||||
pilot_i_reg <= pilot_i;
|
||||
pilot_q_reg <= pilot_q;
|
||||
phase_in_stb <= 1;
|
||||
end else begin
|
||||
phase_in_stb <= 0;
|
||||
end
|
||||
|
||||
if (phase_out_stb) begin
|
||||
pilot_count2 <= pilot_count2 + 1;
|
||||
pilot_iq_phase[pilot_count2] <= phase_out;
|
||||
`ifdef DEBUG_PRINT
|
||||
$display("[PILOT OFFSET] %d", phase_out);
|
||||
`endif
|
||||
pilot_phase <= phase_out;
|
||||
end else if (pilot_count2 > 3) begin
|
||||
pilot_count2 <= pilot_count2 + 1;
|
||||
end
|
||||
|
||||
if (pilot_count2 == 8) begin
|
||||
pilot_count1 <= 0;
|
||||
pilot_count2 <= 0;
|
||||
cpe <= {(cpe[15] == 0 ? 2'b00:2'b11),cpe[15:2]};
|
||||
Sxy <= 0;
|
||||
state <= S_CALC_SAMPL_OFFSET;
|
||||
end else if (pilot_count2 > 3) begin
|
||||
// sampling rate offset (SFO) is calculated as pilot phase error
|
||||
if(pilot_sum_i < 0 && pilot_sum_q > 0 && pilot_iq_phase[pilot_count2[1:0]] < 0) begin
|
||||
cpe = cpe + pilot_iq_phase[pilot_count2[1:0]] + 3217;
|
||||
end else if(pilot_sum_i < 0 && pilot_sum_q < 0 && pilot_iq_phase[pilot_count2[1:0]] > 0) begin
|
||||
cpe = cpe + pilot_iq_phase[pilot_count2[1:0]] - 3217;
|
||||
end else begin
|
||||
cpe = cpe + pilot_iq_phase[pilot_count2[1:0]];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
S_CALC_SAMPL_OFFSET: begin
|
||||
if (pilot_count1 < 4) begin
|
||||
// sampling rate offset (SFO) is calculated as pilot phase error
|
||||
if(cpe > 804 && pilot_iq_phase[pilot_count1] < 0) begin
|
||||
pilot_phase_err <= pilot_iq_phase[pilot_count1] - cpe + 3217;
|
||||
end else if(cpe < -804 && pilot_iq_phase[pilot_count1] > 0) begin
|
||||
pilot_phase_err <= pilot_iq_phase[pilot_count1] - cpe - 3217;
|
||||
end else begin
|
||||
pilot_phase_err <= pilot_iq_phase[pilot_count1] - cpe;
|
||||
end
|
||||
|
||||
pilot_count1 <= pilot_count1 + 1;
|
||||
end
|
||||
|
||||
if(pilot_count1 == 1) begin
|
||||
Sxy <= Sxy + 7*pilot_phase_err;
|
||||
end else if(pilot_count1 == 2) begin
|
||||
Sxy <= Sxy + 21*pilot_phase_err;
|
||||
end else if(pilot_count1 == 3) begin
|
||||
Sxy <= Sxy + -21*pilot_phase_err;
|
||||
end else if(pilot_count1 == 4) begin
|
||||
Sxy <= Sxy + -7*pilot_phase_err;
|
||||
|
||||
in_raddr <= 0;
|
||||
sym_idx <= 0;
|
||||
lvpe_in_stb <= 0;
|
||||
// compensate for RAM read delay
|
||||
lts_raddr <= 1;
|
||||
rot_in_stb <= 0;
|
||||
num_output <= 0;
|
||||
state <= S_ADJUST_FREQ_OFFSET;
|
||||
state <= S_ADJUST_FREQ_and_SAMPL_OFFSET;
|
||||
end
|
||||
// Sx² = ∑(x-x̄)*(x-x̄) = ∑x² = (7² + 21² + (-21)² + (-7)²) = 980
|
||||
// phase error gradient (PEG) = Sxy/Sx²
|
||||
end
|
||||
|
||||
S_ADJUST_FREQ_OFFSET: begin
|
||||
S_ADJUST_FREQ_and_SAMPL_OFFSET: begin
|
||||
if (sym_idx < 64) begin
|
||||
sym_idx <= sym_idx + 1;
|
||||
lvpe_in_stb <= 1;
|
||||
end else begin
|
||||
lvpe_in_stb <= 0;
|
||||
end
|
||||
|
||||
// first rotate, then normalize by avg LTS
|
||||
if (in_raddr < 64) begin
|
||||
in_raddr <= in_raddr + 1;
|
||||
if (lvpe_out_stb) begin
|
||||
phase_err <= cpe + lvpe[15:0];
|
||||
rot_in_stb <= 1;
|
||||
in_raddr <= in_raddr + 1;
|
||||
end else begin
|
||||
rot_in_stb <= 0;
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user