diff --git a/libraries/dsp/fft/src/vhdl/fft_pkg.vhd b/libraries/dsp/fft/src/vhdl/fft_pkg.vhd index 05eaa1ffb226920d9127c5d69ea56bdd2ecacefe..84f1eef69fa088e941de3d1bef2e5d3c9e396d98 100644 --- a/libraries/dsp/fft/src/vhdl/fft_pkg.vhd +++ b/libraries/dsp/fft/src/vhdl/fft_pkg.vhd @@ -32,6 +32,14 @@ package fft_pkg is function fft_switch_new_seed(seed : std_logic_vector; offset : natural) return std_logic_vector; + -- The FFT gain for an real input sinus signal is 0.5, because a real input + -- sinus with amplitude A yields two subband phasors each with amplitude + -- A/2, one at -f_bin and one at +f_bin. The power stays the same, because + -- the power of an real input sinus is A**2 / 2 and the power of the f_bin + -- signal complex phasors is (A/2)**2 + (A/2)**2 = A**2 / 2. + CONSTANT c_fft_real_input_gain_w : INTEGER := -1; + CONSTANT c_fft_real_input_gain : REAL := 2.0**REAL(c_fft_real_input_gain_w); + -- FFT parameters for pipelined FFT (fft_pipe), parallel FFT (fft_par) and wideband FFT (fft_wide) type t_fft is record use_reorder : boolean; -- = false for bit-reversed output, true for normal output diff --git a/libraries/dsp/wpfb/src/vhdl/wpfb_pkg.vhd b/libraries/dsp/wpfb/src/vhdl/wpfb_pkg.vhd index d87302058e1779ac79bba9a0542157a249cedcf7..2f47ccaf17a13d4b1ed4192e4e1d852c75ae8f26 100644 --- a/libraries/dsp/wpfb/src/vhdl/wpfb_pkg.vhd +++ b/libraries/dsp/wpfb/src/vhdl/wpfb_pkg.vhd @@ -21,6 +21,7 @@ library ieee, common_lib, rTwoSDF_lib, fft_lib, filter_lib; use IEEE.std_logic_1164.all; +use IEEE.math_real.all; use common_lib.common_pkg.all; use rTwoSDF_lib.rTwoSDFPkg.all; use fft_lib.fft_pkg.all; @@ -69,6 +70,46 @@ package wpfb_pkg is fil_pipeline : t_fil_ppf_pipeline; -- Pipeline settings for the filter units end record; + ----------------------------------------------------------------------------- + -- LOFAR2 subband filter + ----------------------------------------------------------------------------- + + -- Fsub settings: + -- . Settings used until at least March 2022 + constant c_wpfb_lofar2_subbands_2021 : t_wpfb := (1, 1024, 0, 6, + 16, 0, 14, 17, 16, + true, false, true, 17, 18, 0, 22, 1, true, 54, 2, 195313, + c_fft_pipeline, c_fft_pipeline, c_fil_ppf_pipeline); + + constant c_wpfb_lofar2_subbands_2022 : t_wpfb := (1, 1024, 0, 6, + 16, 1, 14, 0, 16, + true, false, true, 0, 19, 1, 24, 1, true, 56, 2, 195313, + c_fft_pipeline, c_fft_pipeline, c_fil_ppf_pipeline); + + constant c_wpfb_lofar2_subbands : t_wpfb := c_wpfb_lofar2_subbands_2022; + + -- DC gain of WPFB FIR filter with Coeffs16384Kaiser-quant.dat used in LOFAR1. + -- . Calculated with applications/lofar2/model/run_pfir_coef.m using application = 'lofar_subband' + -- . Not used in RTL, only used in test benches to verify expected subband levels + constant c_wpfb_lofar1_fir_filter_dc_gain : real := 0.994817; + + -- The FFT output has more bits to be able to preserve the sensitivity of + -- the processing gain of the FFT. The FFT has a processing gain of + -- sqrt(N_sub = N_fft / 2 = 512), so 4.5 bits. Therefore choose + -- fft_out_dat_w = fil_in_dat_w + 5 = 14 + 5 = 19b. Using fft_out_gain_w = + -- 1 compensates for the fil_backoff_w = 1. The func_wpfb_subband_scale_w + -- then thus returns 19 + 1 - (14 + 1) = 5 bits. + function func_wpfb_subband_scale_w(wpfb : t_wpfb) return natural; + + -- The WPFB subband gain is the expected factor between subband amplitude + -- and real signal input amplitude. The WPFB subband gain consists of: + -- . the FFT gain for a real input and + -- . the extra bits to preserve the sensitivity of the FFT processing gain. + -- For example: + -- . func_wpfb_subband_gain() = 8 for c_wpfb_lofar2_subbands_2021 + -- . func_wpfb_subband_gain() = 16 for c_wpfb_lofar2_subbands_2022 + function func_wpfb_subband_gain(wpfb : t_wpfb) return real; + ----------------------------------------------------------------------------- -- Apertif application specfic settings ----------------------------------------------------------------------------- @@ -143,7 +184,7 @@ package wpfb_pkg is -- apertif_unb1_correlator_vis_offload -- . fft_out_dat_w = 18, because in there is a separate dp_requantize to get from 18b --> 9b in -- node_apertif_unb1_correlator_processing, this dp_requantize uses symmertical clipping. - CONSTANT c_wpfb_apertif_channels : t_wpfb := (1, 64, 1, 12, + constant c_wpfb_apertif_channels : t_wpfb := (1, 64, 1, 12, 8, 0, 8, 16, 9, false, false, false, 16, 18, 0, c_dsp_mult_w, 2, true, 56, 2, 12500, c_fft_pipeline, c_fft_pipeline, c_fil_ppf_pipeline); @@ -168,7 +209,7 @@ package wpfb_pkg is -- . Using fft_out_dat_w = 12 instead of 9 and fft_out_gain_w = 2 instead of 0 created 12 - 9 - 2 = 1 bit more -- dynamic range. Therefore it may not be necessary to use fine channel symmetrical clipping using an external -- dp_requantize, like in Apertif X. - CONSTANT c_wpfb_arts_channels_sc4 : t_wpfb := (1, 64, 1, 12, + constant c_wpfb_arts_channels_sc4 : t_wpfb := (1, 64, 1, 12, 8, 0, 8, 16, 9, true, true, false, 16, 12, 2, c_dsp_mult_w, 2, true, 56, 2, 12500, c_fft_pipeline, c_fft_pipeline, c_fil_ppf_pipeline); @@ -184,12 +225,22 @@ package wpfb_pkg is -- Estimate maximum number of blocks of latency between WPFB input and output function func_wpfb_maximum_sop_latency(wpfb : t_wpfb) return natural; - function func_wpfb_set_nof_block_per_sync(wpfb : t_wpfb; nof_block_per_sync : NATURAL) return t_wpfb; + function func_wpfb_set_nof_block_per_sync(wpfb : t_wpfb; nof_block_per_sync : natural) return t_wpfb; end package wpfb_pkg; package body wpfb_pkg is + function func_wpfb_subband_scale_w(wpfb : t_wpfb) return natural is + begin + return wpfb.fft_out_dat_w + wpfb.fft_out_gain_w - (wpfb.fil_in_dat_w + wpfb.fil_backoff_w); + end; + + function func_wpfb_subband_gain(wpfb : t_wpfb) return real is + begin + return c_fft_real_input_gain * 2.0**real(func_wpfb_subband_scale_w(wpfb)); + end; + function func_wpfb_maximum_sop_latency(wpfb : t_wpfb) return natural is constant c_nof_channels : natural := 2**wpfb.nof_chan; constant c_block_size : natural := c_nof_channels * wpfb.nof_points / wpfb.wb_factor; @@ -208,7 +259,7 @@ package body wpfb_pkg is end func_wpfb_maximum_sop_latency; -- Overwrite nof_block_per_sync field in wpfb (typically for faster simulation) - function func_wpfb_set_nof_block_per_sync(wpfb : t_wpfb; nof_block_per_sync : NATURAL) return t_wpfb is + function func_wpfb_set_nof_block_per_sync(wpfb : t_wpfb; nof_block_per_sync : natural) return t_wpfb is variable v_wpfb : t_wpfb; begin v_wpfb := wpfb;