Skip to content
Snippets Groups Projects
Commit ac7261b1 authored by Eric Kooistra's avatar Eric Kooistra
Browse files

Copied FIR coef scripts from Apertif.

parent c5602f5f
No related branches found
No related tags found
1 merge request!188Updated model/pfs_coeff_final.m, but preserved old FTF results for reference...
%-----------------------------------------------------------------------------
%
% Copyright (C) 2016
% ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
% P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/>.
%
%-----------------------------------------------------------------------------
% Author: R. de Wild, 2015 (Original)
% E. Kooistra, 2016
%
% Purpose : Calculate FIR filter coefficients for Apertif channel
% filterbank using fircls1.
% Description :
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% workspace initiation
clear all
close all
fig=0;
% preset FIR-filter parameters
K = 64; % Number of channels
Taps = 8; % Number of taps, even 4 could be sufficient (erko)
M = Taps*K; % Number of filter coefficients (taps)
h_ix = 1:M; % coefficient index
nbit = 9; % bits/coefficient = 1 sign + (nbit-1) mantissa
fs = 781250; % sampling clock = 800.0 MHz / 1024
f_nyq = fs/2; % Nyquist frequency
f_pb = fs/K/2; % end of passband
f_chan = fs/K;
% normalized frequency axis & normalized amplitude axis
w_pb = f_pb/f_nyq; % = f_chan
% The fircls1 pass band deviation and stop band deviation are positive
% fractions of 1. The ripple in dB needs to be divided by 10 to get from
% dB = 10*log10() the log10() and divided by 2 because it is a relative
% voltage value (instead of a relative power value).
% When the pass band ripple in dB is positive then the linear factor is
% sligthly larger than 1, so subtract 1 to get the pass band deviation.
% The pass band deviation defines the +- fraction around 1. The stop band
% 'ripple' in dB is negative and defines the attenuation. The stop band
% deviation is the fraction of 1 that remains in the stop band. Therefore
% both the pass band ripple and the stop band attenuation result both in
% linear deviations that are close to 0.
% The fircls1 description defines DEVP is the maximum passband deviation
% or ripple (in linear units) from 1, and DEVS is the maximum stopband
% deviation or ripple (in linear units) from 0.
r_pb = 10^(0.025)-1; % passband ripple = 0.5 dB
r_sb = 10^(-3.0); % stopband ripple = -60.0 dB
% compute M filter-coefficients
h = fircls1(M-1, w_pb, r_pb, r_sb);
hfs_abs = abs(fftshift(fft(h ,M)));
% compute quantized filter coefficients
hmax = max(h);
hq = double( uencode(h, nbit, hmax, 'signed') );
hqfs_abs = abs(fftshift (fft( hq ,M))) * sum(h)/sum(hq); % normalize to response for DC
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Print FIR-filter parameters
sprintf([ ...
' fs = sample rate = %6.2f [Hz] \n' , ...
' f_chan = channel bandwidth = %6.2f [Hz]\n' , ...
' r_pb = pass band deviation = %9.7f = %6.2f dB_W ripple\n' , ...
' r_sb = stop band deviation = %9.7f = %6.2f dB_W attenuation\n' , ...
' M = number of coefficients = %d'], ...
fs, f_chan, r_pb, 20*log10(1+r_pb), r_sb, 20*log10(r_sb), M)
% Save FIR-filter-coefficients
% N.B. - read '.txt'-file with 'wordpad' or use \r\n !!!
file_name = ['FIR_LPF_ApertifCF.txt'];
FIRtable = [ h(h_ix) ] ;
fid = fopen(file_name , 'w');
fprintf(fid ,'%14.10f \n', FIRtable);
fclose(fid);
% Plot FIR-filter coefficients
fig=fig+1;
figure('position', [300+fig*20 200-fig*20 1000 800]);
figure(fig);
plot(hq);
title(['FIR filter coefficients (', num2str(nbit), ' bits)'] );
grid on;
% Plot FIR-filter amplitude characteristic
fig=fig+1;
figure('position', [300+fig*20 200-fig*20 1000 800]);
figure(fig);
f = (h_ix - M/2-1) * fs/M / f_chan;
plot ( f, 20.0 * log10 ( hfs_abs ), 'r-', ...
f, 20.0 * log10 ( hqfs_abs ), 'k-') ;
xlo = f(1);
xhi = f(M);
xlo = -3;
xhi = 3;
xlim( [xlo xhi] );
grid on;
zoom on;
title('FIR filter transfer function');
text( xlo, -5, ['sampling rate = ', num2str(fs), ' [Hz]']);
text( xlo, -10, ['channel bandwidth = ', num2str(f_chan), ' [Hz]']);
text( xlo, -15, ['number of taps = ', num2str(Taps)]);
text( xlo, -20, ['number of channels = ', num2str(K)]);
text( xlo, -25, ['number of coefficients = ', num2str(M)]);
text( xlo, -30, ['number of bits/coefficient = ', num2str(nbit)]);
xlabel('Frequency [channels]');
ylabel('Power [dB]');
print -dpng 'arts_fir';
%-----------------------------------------------------------------------------
%
% Copyright (C) 2016
% ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
% P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/>.
%
%-----------------------------------------------------------------------------
% Author: W. Lubberhuizen, 2005
% E. Kooistra, 2016
%
% pfir_coeff - compute polyphase FIR filter coefficients
% Computes the coefficients of the prototype FIR filter for a polyphase
% filterbank with N fft channels, and subfilter length L and channel
% bandwidth BWchan. Typically BWchan = 1/N or somewhat more have -3dB
% reponse between channels.
%
% The FIR coefficients can be caluculated with Matlab 'fir1' or with
% Matlab 'fircls1' dependend on config.design.
%
% For Matlab fircls1() r_pass and r_stop are the maximum passband and
% stop band deviations, respectively.
% For Matlab fircls1() and Q>1, the filter coefficients are computed by
% interpolating a shorter filter with an upsampling factor Q. This
% reduces computation time substantially. N, L and Q must be powers of 2.
%
% The FIR coefficients are normalized to fit in range +-1. Given that
% the impulse response has a sync pulse shape this implies that the
% largest coefficient is h_fir_max = 1. Most energy is passed on via
% the center tap and therefore the filter DC gain per poly phase
% h_fir_dc_gain is close to 1. The quantization maps h_fir_max to
% optional input argument coeff_q_max which defaults to 2^(nof_bits-1)-1.
%
% For nof_bits>1, the FIR filter coefficients are quantized with
% nof_bits bits.
%
% The returned value is a coefficients vector of length NL, which also
% represents the impulse response of prototype FIR filter.
%
% The run_pfir_coeff.m shows how this pfir_coeff.m can be used.
function coeff = pfir_coeff(N, L, BWchan, r_pass, r_stop, nof_bits, config, coeff_q_max)
% Default options
if ~exist('coeff_q_max', 'var'); coeff_q_max = 2^(nof_bits-1)-1; end; % the quantization maps h_fir_max to coeff_q_max
% requested filter length
M2=N*L;
if strcmp(config.design, 'fir1')
Q = 1;
elseif M2<=1024
Q = 1;
else
Q = L; % use interpolation to speed up calculation
%Q = 1; % no interpolation
end
% initial filter length
M1=N*L/Q;
% pass bandwidth
w_pb = Q * BWchan;
% compute initial filter
if strcmp(config.design, 'fir1')
h_comp = fir1(M1-1, w_pb);
disp(sprintf('pfir_coeff : FIR coefficients calculated using Matlab fir1.\n'))
else
if strcmp(config.design_flag, '')
h_comp = fircls1(M1-1, w_pb, r_pass, r_stop);
else
h_comp = fircls1(M1-1, w_pb, r_pass, r_stop, config.design_flag);
end
disp(sprintf(['pfir_coeff : FIR coefficients calculated using Matlab fircls1.\n', ...
'pfir_coeff : . pass band deviation = %e\n', ...
'pfir_coeff : . stop band deviation = %e\n'], ...
r_pass, r_stop))
end
if Q<=1
h_fir = h_comp;
disp(sprintf('pfir_coeff : FIR coefficients calculated directly with no interpolation.\n'))
else
% 1a) Use Matlab fourier interpolation method
h_interpft = interpft(h_comp,length(h_comp)*Q);
% 1b) Use DIY fourier interpolation method
f1=fft(h_comp);
f2=zeros(1, M2);
% copy the lower frequency half.
n=0:M1/2;
f2(1+n)=f1(1+n);
% to make the impulse response symmetric in time,
% we need to apply a small phase correction,
% corresponding to a shift in the time domain.
f2(1+n)=f2(1+n).*exp(-sqrt(-1)*(Q-1)*pi*n/M2);
% recreate the upper part of the spectrum from the lower part
f2(M2-n)=conj(f2(2+n));
% back to time domain
h_fourier = real(ifft(f2));
% 2) Use resample interpolation method
h_resample = resample(h_comp, Q, 1);
% select FIR coefficients from either interpolation method (all are good, but resample causes peek gratings in stopband when N>64)
if strcmp(config.interpolate, 'resample')
h_fir = h_resample;
disp(sprintf('pfir_coeff : FIR coefficients calculated with resample interpolation.\n'));
elseif strcmp(config.interpolate, 'fourier')
h_fir = h_fourier;
disp(sprintf('pfir_coeff : FIR coefficients calculated with DIY Fourier interpolation.\n'));
else
h_fir = h_interpft;
disp(sprintf('pfir_coeff : FIR coefficients calculated with Matlab Fourier interpolation.\n'));
end
end;
% normalize the coefficients to fit in range +-1
disp(sprintf('pfir_coeff : Normalizing the PFIR coefficients to fit in range +-1.'));
h_fir = h_fir / max(h_fir);
h_fir_max = max(h_fir); % note: is 1 due to the normalization
h_fir_min = min(h_fir);
h_fir_dc_gain = sum(h_fir) / N;
disp(sprintf(' . Maximum coefficient value : %f', h_fir_max));
disp(sprintf(' . Minimum coefficient value : %f', h_fir_min));
disp(sprintf(' . DC gain per polyphase : %f\n', h_fir_dc_gain));
% quantize the coeffients
if nof_bits>0
disp(sprintf('pfir_coeff : Quantizing FIR coefficients nof_bits = %d\n', nof_bits));
h_fir = quantize(h_fir, h_fir_max, nof_bits, 'half_away', 'clip', coeff_q_max); % note: h_fir_max is 1 due to the normalization
end;
% output FIR coefficient
coeff = h_fir;
\ No newline at end of file
%-----------------------------------------------------------------------------
%
% Copyright (C) 2016
% ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
% P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/>.
%
%-----------------------------------------------------------------------------
% Author: E. Kooistra, 2016
%
% pfir_coeff_adjust - Compute and adjust polyphase FIR filter coefficients
% Computes the coefficients using pfir_coeff.m and then adjusts the
% coefficients dependent on:
%
% config.hp_factor : = 1, is initial or default value for BWchan
% With config.hp_factor the BWchan can be set relative to fchan = 1/N.
% Default BWchan = config.hp_factor/N = 1/N = fchan.
%
% config.hp_adjust :
% when true then enable automatic channel half power bandwidth
% adjustment of config.hp_factor by iteration until the
% config.hp_factor has reached an accuracy of hp_resolution.
% For floating point nof_bits=0 the config.hp_factor can achieve an
% arbitrary accurate solution. For fixed point nof_bits>0 the solution
% can only be as good as possible, so then decreasing hp_resolution
% does not change the coeff anymore. The input BWchan is then ignored
% and the initial BWchan is then set to config.hp_factor/N.
%
% config.dc_adjust :
% when true then after quantization the FIR coefficients are adjusted
% such that each of the N polyphase FIR sections with L taps each has
% the same DC response. This then ensures that for DC input the output
% of all N polyphases will also be DC. The FFT in a polyphase
% filterbank will then transform this DC to bin 0, and there will be
% no affect on the other bins.
% The DC adjustment algorithm first determines the median of the sums
% per polyphase. For each polyphase the L taps are then DC adjusted
% to this dc_polyphases_median. If the DC adjustment is > L then first
% an equal coarse adjustment is made to all L taps. The remining DC
% adjustment is then < L and applied by adjusting the outer taps by 1.
%
% The DC adjustment must not cause coefficient overflow. If it does
% then the script will rerun with coeff_q_max sufficiently smaller
% than the default 2^(nof_bits-1)-1 until it succeeds.
%
% The run_pfir_coeff.m shows how this pfir_coeff_adjust.m and
% pfir_coeff.m can be used.
%
% For more information see help of pfir_coeff.m.
function coeff = pfir_coeff_adjust(N, L, BWchan, r_pass, r_stop, nof_bits, config, coeff_q_max)
% Default options
if ~exist('coeff_q_max', 'var'); coeff_q_max = 2^(nof_bits-1)-1; end; % the quantization maps h_fir_max to coeff_q_max
while true
if config.hp_adjust==false
% Calculate the FIR coefficients using the specified BWchan
coeff = pfir_coeff(N, L, BWchan, r_pass, r_stop, nof_bits, config, coeff_q_max);
else
% Automatically adjust channel half power bandwidth, so need db(sqrt(2)) = -3 dB gain instead of half gain
hp_radix = 2; % choose 2 for binary search, or 10 for 'decimal' search
hp_step = 1/hp_radix;
hp_resolution = 0.000001;
while true
BWchan = config.hp_factor/N;
coeff = pfir_coeff(N, L, BWchan, r_pass, r_stop, nof_bits, config, coeff_q_max);
hf_abs = abs(fftshift(fft(coeff / sum(coeff), N*L)));
fi_0 = N*L/2+1; % frequency index of center of channel, so 0 Hz
fi_p = fi_0 + L/2; % frequency index of edge of channel at +f_chan/2
hf_abs_p = hf_abs(fi_p); % gain edge of channel at +f_chan/2
disp(sprintf('hp_factor = %10.8f, hf_abs_p = %10.8f, hp_step = %10.8f\n', config.hp_factor, hf_abs_p, hp_step));
if hf_abs_p < sqrt(0.5)
config.hp_factor = config.hp_factor + hp_step;
else
if hp_step <= hp_resolution, break; end
config.hp_factor = config.hp_factor - hp_step;
hp_step = hp_step/hp_radix;
config.hp_factor = config.hp_factor + hp_step;
end
end
end
if config.dc_adjust==true && nof_bits>0
% Automatically equalize the DC response of the polyphases to have flat DC level at FFT input
% Reshape the coefficients in N polyphases with L taps each.
% Work with q_full_scale quantized coefficients to have q_bit unit quantization.
q_bit = 1;
q_full_scale = 2^(nof_bits-1); % maximum amplitude, magnitude
q_coeff = round(q_full_scale * reshape(coeff, N, L).');
% The DC response is the sum of the FIR coefficients per polyphase.
dc_polyphases = sum(q_coeff);
% The median will be used as aim for the dc adjustment, because most polyphases are already
% close to the median. For larger N the first and last polyphases deviate the most.
dc_polyphases_median = median(dc_polyphases);
% DC adjust each polyphase to the DC median
for I = 1:N
polyphase = q_coeff(:,I);
dc_polyphase = sum(polyphase);
dc_adjust = dc_polyphase - dc_polyphases_median;
% First equally adjust all L coefficients by an equal amount of
% dc_offset >= 0
dc_offset = floor(abs(dc_adjust)/L);
% Then adjust the dc_remainder < L, over dc_adjust number of
% coefficients from the outer to the inner taps of the polyphase
% by 1
dc_remainder = abs(dc_adjust) - L*dc_offset;
% Treat dc_adjust < 0, > 0 separately, nothing to do when dc_adjust = 0
if dc_adjust > 0
polyphase = polyphase - dc_offset;
if dc_remainder > 0
for J = 1:dc_remainder
if mod(J,2)==0
polyphase(J) = polyphase(J) - 1;
else
polyphase(L+1-J) = polyphase(L+1-J) - 1;
end
end
end
end
if dc_adjust < 0
polyphase = polyphase + dc_offset;
if dc_remainder > 0
for J = 1:dc_remainder
if mod(J,2)==0
polyphase(J) = polyphase(J) + 1;
else
polyphase(L+1-J) = polyphase(L+1-J) + 1;
end
end
end
end
dc_polyphase = sum(polyphase);
dc_adjusted = dc_polyphase - dc_polyphases_median;
if dc_adjusted ~= 0
% Return with Error messages when dc_adjusted for a polyphase is not 0
disp(sprintf('Error polyphase %4d : dc_adjust = %4d, dc_offset= %4d, dc_remainder = %4d, dc_adjusted = %4d must be 0', I, dc_adjust, dc_offset, dc_remainder, dc_adjusted));
return
end
q_coeff(:,I) = polyphase / q_full_scale; % back to normalized full scale coefficients
end
coeff = reshape(q_coeff.', 1, N*L);
% Check whether the q_coeff are still in range q_min : q_max
q_full_scale = 2^(nof_bits-1);
q_max = q_full_scale-1;
q_margin = q_max - max(q_coeff(:));
if q_margin < 0
coeff_q_max = coeff_q_max+q_margin; % Recalculate pfir_coeff with more positive margin to fit DC adjustment
else
break; % Finished DC adjustment of the coefficients
end
else
break; % No need to while loop
end
end
end
This directory contains scripts and documents for modelling DSP aspects of LOFAR2.0:
Contents:
1) Original LOFAR1 scripts from FilterTaskForce.zip [1]
2) Original LOFAR1 documents
3) Copied from APERTIF [3]
4) LOFAR2.0
References:
[1] https://git.astron.nl/desp/hdl/-/blob/master/applications/lofar1/FilterTaskForce.zip
......@@ -9,6 +17,8 @@ References:
[4] https://git.astron.nl/desp/hdl/-/blob/master/libraries/dsp/verify_pfb/tb_tb_verify_pfb_wg.vhd
1) Original LOFAR1 scripts from FilterTaskForce.zip [1]
- git/hdl/libraries/dsp/filter/src/hex/Coeffs16384Kaiser-quant.dat :
......@@ -32,14 +42,23 @@ References:
- quanreq.pdf, 2002 [2] : Modeling the Effects of Re-quantization for
ALMA-FC: Cascading of Degradation Factors
3) Copied from APERTIF [3]
APERTIF uses the same PFB FIR coefficients as LOFAR1.
FIR_LPF_ApertifCF.m Script Calculate FIR filter coefficients for Apertif channel filterbank
using fircls1 (original by Ronald de Wild).
pfir_coeff.m Function Compute polyphase filterbank coefficients (same purpose as
FIR_LPF_ApertifCF.m but with more options)
pfir_coeff_adjust.m Function Compute polyphase filterbank coefficients with optionbal half power
bandwidth adjustment (calls pfir_coeff.m)
pfir_coeff_dc_adjust.m Function Apply DC adjustment to polyphase filterbank coefficients
run_pfir_coeff.m Script Run pfir_coeff_adjust.m and pfir_coeff.m to show filter response in
time and frequency domain, and optionally calls pfir_coeff_dc_adjust.m
4) LOFAR2.0
- tb_tb_verify_pfb_wg.vhd, 2021 [4] : VHDL simulation of LOFAR1 PFB2 and APERTIF WPFB for LOFAR2.0
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment