Select Git revision
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
one_pfb.m 29.89 KiB
%-----------------------------------------------------------------------------
%
% 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, 2015 (top.m for LOFAR Station, uses classes)
% Eric Kooistra, 2016 (for Apertif)
%
% Purpose : Model one PFB = PFIR + PFFT with real input:
% 1) to show the transfer function of a polyphase filterbank with real
% input like the subband filterbank
% 2) to create reference data for HDL implementation in data/:
%
% . PFIR coeffcients: one_pfb_m_pfir_coeff_fircls1_16taps_128points_16b.dat
% . PFIR coeffcients, WG, PFIR, PFFT data: one_pfb_m_chirp_8b_16taps_128points_16b_16b.dat
%
% Description :
%
% * General
% The data path (DP) is modelled per block of data. The block of data
% are counted by the block sequence number that thus acts as a
% timestamp. The data path consist of DSP functions. Each DSP
% function expects normalized full scale input (range -1:1) and it also
% normalized full scale output. Dependend on the tb.model_quantization
% the DSP functions outputs floating point or fixed point data that is
% quantized to the specified number of bits. The output full scale
% range remains -1:1.
%
% * Subband filterbank transfer function
% The real input implies that ctrl_pfft_subband.complex = false, and
% that the filterbank outputs tb.nof_subbands, where tb.subband_fft_size
% = tb.nof_complex*tb.nof_subbands.
% The subband filterbank can use LOFAR settings (tb.model_filterbank =
% 'LOFAR' or 'free choice' settings. With 'free choice' the number of
% subbands (tb.nof_subbands) and the quantization (tb.model_quantization)
% are free to set.
% Using a chirp via tb.chirp = 1 and ctrl_wg.df > 0 shows the transfer
% function of the polyphase filterbank.
% If tb.plot_per_block == 1 then the filterbank subband spectrum is
% plotted per block, else it is only plotted as a subband spectrogram
% image. Used tb.plot_per_block == 1 to debug the indexing in the
% filterbank input, pfir and pfft chain. Especially the
% flipud(ctrl_pfir_subband.coeff) is essential to have the indexing
% correct (see pfir.m).
% The chirp frequency increments forever, so the filterbank output
% sweeps from subband 0 up to fs/2, down to 0, up to fs/2, etc. For
% chirp frequency between DC and fsub/2 the subband 0 output is irratic
% between 1 and 0. For chirp frequency between fs/2-fsub/2 and fs/2 the
% the subband 0 output is irratic but almost 0.
% The PFIR can be bypassed using ctrl_pfir_subband.bypass = 0 to only
% have the FFT in the filterbank. Without PFIR and frequency there is
% aliasing on all subbands and no particular difference for frequencies
% near DC or fs/2.
% Towards fs/2 the WG sinusiod amplitude gets modulated more and more
% due to that there are less and less samples per period. Hence within
% the impulse response of the filterbank the WG sinusoid is not stable
% for higher frequencies.
%
% * Indexing
% Matlab indices are from 1:N, but most languages use 0:N-1. Typically
% time starts at 0 and bin 0 represents DC. Therefore here default use
% 0:N-1 and add +1 to the index whenever it is used as index in a Matlab
% vector. Use capital I in index name if it is already a Matlab index.
%
% * Block processing
% The data is processed in blocks and captured at each interface in a
% matrix of tb.nof_tsub rows. The block size before the subband
% filterbank is defined by the FFT size (tb.subband_fft_size). The block
% size after the subband filterbank is defined by the number of subbands
% (tb.nof_subbands).
%
% * Flipped order of FIR coefficients:
% The FIR filter in the PFB is defined by a prototype filter h[] with N_fft
% * N_taps = 1024 *16 = 16384 coefficients. The impulse response of this
% prototype filter will show the coefficients in h[0:16383] order. The
% FIR coefficients of h[] in the PFB are flipped per column of N_fft = 1024
% coefficients, for all N_taps = 16 columns. In VHDL simulation this shows
% with tb_verify_pfb_response.vhd and in the Matlab model this flip is also
% done by ctrl_pfir_subband.coeff = flipud(ctrl_pfir_subband.coeff).
% With the flip the one_pfb.m yields SNR subband / spurious max = 64.305310
% dB in subband 64 for an input WG at 64.015625. Without the flip the SNR
% subband / spurious max = 29.857938 dB, so much worse. Hence flipping the
% FIR coefficients is needed, and not due to an implementation detail in
% the VHDL. The run_pfb.m is equivalent to one_pfb.m and is used to create
% reference input and expected output data for the wpfb implementation in
% VHDL by wpfb_unit_wide.vhd. The tb_tb_wpfb_unit_wide.vhd verifies multiple
% sets of reference data including sinus, chirp, noise and real input or
% complex input and wideband (f_sample > f_clk) or same rate (f_sample =
% f_clk). This tb guarantees that the VHDL agrees with the Matlab model.
% In the Matlab model the data blocks and data time are defined as:
%
% WG FIR with four taps FFT
% index t t
% 0 -1023 1023 2047 3071 4095 --> + --> -1023
% . . . . .
% . . . . .
% -2 2 . . . -2
% -1 1 . . . -1
% 1023 0 0 1024 2048 3072 --> + --> 0
% d h h h h d
%
% The waveform generator (WG) generates a block of 1024 samples with index
% 0:1023, where sample at index 1023 is the newest and sample at index 0 is
% the oldest. In a filter the newest sample needs to be multiplied with h[0]
% and the older samples are multiplied by the subsequent coefficients,
% because it is a convolution. Therefore the FIR coefficients need to be
% flipped up/down per column, to allow doing the filter as a d .* h vector
% multiply in pfir.m. The FFT operates on blocks of data with same index and
% time range as the WG. The data output of the FIR filter fits this input
% range of the FFT. Therefore no data flipping is needed.
%
% In the tb_verify_pfb_response.vhd the input stimuli is a block of N_fft =
% 1024 ones followed by N_taps-1 blocks with zeros. In time the oldest data
% will appear first in the simulator Wave Window, so therefore the
% fil_re_scope signal in the tb_verify_pfb_response.vhd will show the FIR
% coefficients h[] in order 1023:0, 2047:1024, ..., so flipped per block.
%
clear all;
close all;
fig=0;
%% Selectable test bench settings
tb.model_filterbank = 'Free choice'; % 'LOFAR' = use fixed settings of LOFAR subband filterbank, else use free choice of tb.nof_subbands and PFIR coefficients
tb.model_filterbank = 'LOFAR';
if strcmp(tb.model_filterbank, 'LOFAR')
tb.model_quantization = 'fixed point';
tb.nof_subbands = 512;
else
tb.model_quantization = 'floating point';
tb.model_quantization = 'fixed point';
tb.nof_subbands = 64;
end
% Input signal 'sinusoid' carrier frequency
tb.subband_wg = 47.4; % subband range 0:tb.nof_subbands-1, can be fraction to have any sinusoid frequency
%tb.subband_wg = 1.55;
%tb.subband_wg = 12;
tb.subband_wg = 32;
tb.subband_wg = 64 + 1/64;
% Model a frequency sweep of the 'sinusoid'
tb.chirp = 0; % 0 = use fixed tb.subband_wg frequency, else increment WG frequency every block to have chirp frequency sweep
%tb.chirp = 1;
if tb.chirp
tb.nof_tsub = 200; % number of subband periods to simulate
else
tb.nof_tsub = 100; % number of subband periods to simulate
end
tb.plot_per_block = 0; % 1 = plot spectrum for each block in time, else skip this plot to save time
%tb.plot_per_block = 1;
%% Derived test bench settings
tb.nof_complex = 2;
tb.subband_i = floor(tb.subband_wg); % natural subband index in range 0:tb.nof_subbands-1
tb.subband_I = tb.subband_i+1; % equivalent Matlab indexing for range 1:tb.nof_subbands
if tb.subband_i<tb.nof_subbands-1 % determine index of next neighbour subband
tb.subband_iplus1 = tb.subband_i+1;
else
tb.subband_iplus1 = 0; % wrap subband index tb.nof_subbands to subband index 0
end
tb.subband_Iplus1 = tb.subband_iplus1+1; % equivalent Matlab index for next neighbour subband in range 1:tb.nof_subbands
tb.subband_fft_size = tb.nof_complex*tb.nof_subbands; % subband filterbank real FFT
fs = 1; % normalized sample frequency
fsub = fs/tb.subband_fft_size; % subband frequency relative to fs
disp(sprintf (['Test bench settings:\n', ...
'. Model filterbank = %s\n', ...
'. Model coefficients and data = %s\n', ...
'. Number of subbands = %d\n', ...
'. Number of subband periods to simulate = %d\n', ...
'. Subband filterbank real FFT size = %d\n', ...
'. WG subband frequency = %f\n'], ...
tb.model_filterbank, tb.model_quantization, tb.nof_subbands, tb.nof_tsub, tb.subband_fft_size, tb.subband_wg));
%% Waveform generator
ctrl_wg.signal = 'sinusoid';
ctrl_wg.complex = false;
ctrl_wg.overflow = 'clip';
ctrl_wg.block_nr = 0;
ctrl_wg.block_size = tb.subband_fft_size;
ctrl_wg.sop = 0;
ctrl_wg.psk = 0;
if strcmp(tb.model_quantization, 'floating point')
ctrl_wg.data_w = 0;
lsb = 1/2^8; % Assume 8 bit ADC to have reference for AGWN level
else
ctrl_wg.data_w = 8;
ctrl_wg.q_full_scale = 2^(ctrl_wg.data_w-1);
lsb = 1/2^ctrl_wg.data_w;
end
ctrl_wg.agwn_sigma = 2.6*lsb;
ctrl_wg.agwn_sigma = 0; % AGWN sigma
ctrl_wg.ampl = 1; % full scale maps to 1, so ampl > 1 causes analogue clipping
if ctrl_wg.agwn_sigma>0
ctrl_wg.ampl = 1-5*ctrl_wg.agwn_sigma;
ctrl_wg.ampl = 0.9;
end
if tb.chirp
ctrl_wg.df = 0.5*fsub; % increment freq by df per block to create chirp
else
ctrl_wg.df = 0;
end
if ctrl_wg.df == 0
ctrl_wg.freq = tb.subband_wg*fsub; % start WG at tb.subband_wg
else
ctrl_wg.freq = (tb.subband_i-1)*fsub; % start WG about one subband before tb.subband_wg to account for impulse response of several tsub
end
ctrl_wg.phase = 0; % Phase offset normalized to 2pi
if ctrl_wg.freq == 0 && ctrl_wg.df == 0
ctrl_wg.offset = 1; % DC offset
else
ctrl_wg.offset = 0; % DC offset
end
%% Subband PFIR filter parameters
ctrl_pfir_subband.bypass = 0;
ctrl_pfir_subband.nof_polyphases = tb.subband_fft_size;
if strcmp(tb.model_quantization, 'floating point')
ctrl_pfir_subband.data_w = 0; % no quantization
ctrl_pfir_subband.coeff_w = 0; % no quantization
else
ctrl_pfir_subband.data_w = 16;
ctrl_pfir_subband.overshoot_w = 1; % account for factor 2^overshoot_w in PFIR output quantization
ctrl_pfir_subband.coeff_w = 16; % nof bits per coefficient including 1 sign bit
ctrl_pfir_subband.coeff_q_max = 2^(ctrl_pfir_subband.coeff_w-1)-1;
ctrl_pfir_subband.coeff_q_full_scale = 2^(ctrl_pfir_subband.coeff_w-1);
ctrl_pfir_subband.data_q_full_scale = 2^(ctrl_pfir_subband.data_w-1-ctrl_pfir_subband.overshoot_w);
end
if strcmp(tb.model_filterbank, 'LOFAR')
% Load the quantized PFIR coefficients of LOFAR station (fixed point)
ctrl_pfir_subband.nof_taps = 16; % Number of taps
ctrl_pfir_subband.nof_coefficients = ctrl_pfir_subband.nof_polyphases*ctrl_pfir_subband.nof_taps; % Number of filter coefficients (taps)
ctrl_pfir_subband.data_w = 16;
ctrl_pfir_subband.config.design = 'lofar_file';
hfir_subband_coeff = load('data/Coeffs16384Kaiser-quant.dat');
hfir_subband_coeff = hfir_subband_coeff/max(hfir_subband_coeff);
hfir_subband_coeff = hfir_subband_coeff'; % Use column vector, same format as by pfir_coeff()
else
% Calculate free choice PFIR coefficients (can be floating point or fixed point)
ctrl_pfir_subband.nof_taps = 16; % Number of taps
ctrl_pfir_subband.nof_coefficients = ctrl_pfir_subband.nof_polyphases*ctrl_pfir_subband.nof_taps; % Number of filter coefficients (taps)
ctrl_pfir_subband.config.design = 'fir1';
ctrl_pfir_subband.config.design = 'fircls1'; % 'fir1', 'fircls1'
ctrl_pfir_subband.config.design_flag = ''; % only for fircls1: 'trace', ''
ctrl_pfir_subband.config.interpolate = 'interpft'; % only for fircls1: 'resample', 'fourier', 'interpft'
ctrl_pfir_subband.r_pass = 1e-3; % only for fircls1
ctrl_pfir_subband.r_stop = 1e-4; % only for fircls1
ctrl_pfir_subband.hp_factor = 1.050; % Adjust channel half power bandwidth
ctrl_pfir_subband.hp_factor = 1;
ctrl_pfir_subband.BWchan = ctrl_pfir_subband.hp_factor / tb.subband_fft_size; % Channel bandwidth
hfir_subband_coeff = pfir_coeff(ctrl_pfir_subband.nof_polyphases, ...
ctrl_pfir_subband.nof_taps, ...
ctrl_pfir_subband.BWchan, ...
ctrl_pfir_subband.r_pass, ...
ctrl_pfir_subband.r_stop, ...
ctrl_pfir_subband.coeff_w, ...
ctrl_pfir_subband.config);
end
% Try effect if FIR coefficients would be shifted
sh = 1024;
sh = 15*1024;
sh = 0;
%hfir_subband_coeff = [hfir_subband_coeff(1+sh:16384), hfir_subband_coeff(1:sh)];
% Try effect if FIR coefficients would be in reversed order per wideband
% (ro = 4), per polyphase (ro = 1024) or another factor. There occur peaks
% at subband index N_fft / ro, so for ro = 4 at subband +-256+-0.5 and
% +-511.5. For ro = 32 at subband +-32+-0.5, +-64+-0.5, etc. Using
% ro = 1024 is equivalent to reverse order per polyphase and this yields
% peaks at +-0.5, +-1.5+-0.25, +-2.5+-0.25, +-3.5+-0.5, etc.
ro = 16;
x = reshape(hfir_subband_coeff, ro, ctrl_pfir_subband.nof_polyphases*ctrl_pfir_subband.nof_taps/ro);
x = flipud(x);
%hfir_subband_coeff = x(:);
% Try effect if FIR coefficients would be in reversed per tap
x = reshape(hfir_subband_coeff, ctrl_pfir_subband.nof_polyphases, ctrl_pfir_subband.nof_taps);
x = fliplr(x);
%hfir_subband_coeff = x(:);
% Flip ctrl_pfir_subband.coeff per poly phase, because that is the order
% in which the pfir() model and HDL expect the FIR coefficients
ctrl_pfir_subband.coeff = reshape(hfir_subband_coeff, ctrl_pfir_subband.nof_polyphases, ctrl_pfir_subband.nof_taps);
ctrl_pfir_subband.coeff = flipud(ctrl_pfir_subband.coeff);
ctrl_pfir_subband.Zdelays = zeros(ctrl_pfir_subband.nof_polyphases, ctrl_pfir_subband.nof_taps-1);
ctrl_pfir_subband.gain = 1; % no gain adjustment in PFIR, just apply the coefficients to the input data
disp(sprintf(['Subband PFIR filter settings:\n', ...
'. Bypass = %d\n', ...
'. Number of polyphases = %d\n', ...
'. Number of taps = %d\n'], ...
ctrl_pfir_subband.bypass, ...
ctrl_pfir_subband.nof_polyphases, ...
ctrl_pfir_subband.nof_taps));
if strcmp(tb.model_quantization, 'fixed point')
disp(sprintf(['. Coefficient width = %d\n', ...
'. Coefficient q_max = %d\n', ...
'. Data width = %d\n', ...
'. Data full scale width = %d\n', ...
', Data q_full_scale = %d\n'], ...
ctrl_pfir_subband.coeff_w, ...
ctrl_pfir_subband.coeff_q_max, ...
ctrl_pfir_subband.data_w, ...
ctrl_pfir_subband.overshoot_w, ...
ctrl_pfir_subband.data_q_full_scale));
end
%% Subband FFT parameters
ctrl_pfft_subband.fft_size = tb.subband_fft_size;
ctrl_pfft_subband.complex = false; % real input PFB
ctrl_pfft_subband.gain = ctrl_pfft_subband.fft_size; % compensate default gain of FFT in PFFT
if strcmp(tb.model_quantization, 'floating point')
ctrl_pfft_subband.data_w = 0;
else
ctrl_pfft_subband.full_scale_w = 0;
ctrl_pfft_subband.data_w = 16;
ctrl_pfft_subband.data_q_full_scale = 2^(ctrl_pfft_subband.data_w-1);
end
%% Run the data path processing
t_start = cputime;
data_wg = zeros(tb.nof_tsub, tb.subband_fft_size);
data_pfir_subband = zeros(tb.nof_tsub, tb.subband_fft_size);
data_pfft_subband = zeros(tb.nof_tsub, tb.nof_subbands);
for bi = 0:tb.nof_tsub-1
bI = bi+1;
% Data path (DP)
[ctrl_wg, block_wg] = wg( ctrl_wg);
[ctrl_pfir_subband, block_pfir_subband] = pfir( ctrl_pfir_subband, block_wg); % block_pfir_subband = fliplr(block_pfir_subband); % Try flipped FFT input
[ block_pfft_subband] = pfft( ctrl_pfft_subband, block_pfir_subband);
% Capture data at each DP interface
data_wg(bI, :) = block_wg;
data_pfir_subband(bI, :) = block_pfir_subband;
data_pfft_subband(bI, :) = block_pfft_subband;
end
t_stop = cputime;
disp(sprintf('Total processing time: %f seconds\n', t_stop-t_start));
%% Save coeffcients and data reference files for stimuli and verification of HDL implementation
if strcmp(tb.model_quantization, 'fixed point')
file_name_prefix = 'data/one_pfb_m_';
L = ctrl_pfir_subband.nof_taps;
N = ctrl_pfir_subband.nof_polyphases; % = tb.subband_fft_size;
% Quantize subband PFIR filter coefficients
int_hfir = round(ctrl_pfir_subband.coeff_q_full_scale * hfir_subband_coeff); % map largest coefficient to q_max
% Quantize WG output
wg_q_data = round(ctrl_wg.q_full_scale * data_wg);
% Quantize PFIR subband output
pfir_subband_q_data = round(ctrl_pfir_subband.data_q_full_scale * data_pfir_subband);
% Quantize PFFT subband output real and imaginary
pfft_subband_q_data = round(ctrl_pfft_subband.data_q_full_scale * data_pfft_subband);
% Save the quantized integer subband PFIR filter coefficients in a separate file
file_name = sprintf([file_name_prefix, 'pfir_coeff_', ctrl_pfir_subband.config.design, '_%dtaps_%dpoints_%db.dat'], L, N, ctrl_pfir_subband.coeff_w);
fid = fopen(file_name, 'w');
fprintf(fid,'%d\n', int_hfir);
fclose(fid);
% Save PFIR filter coefficients again together with the data path signals for WG, PFIR and PFFT
tbegin = 1;
tend = tb.nof_tsub; % <= tb.nof_tsub
file_name = sprintf([file_name_prefix, '%s_%db_%dtaps_%dpoints_%db_%db.dat'], ctrl_wg.name, ctrl_wg.data_w, L, N, ctrl_pfir_subband.data_w, ctrl_pfft_subband.data_w);
fid = fopen(file_name, 'w');
fprintf(fid,'Nof lines PFIR coefficients: %d\n', length(int_hfir)); % save PFIR filter coefficients again also in the data file to have a complete set
fprintf(fid,'Nof lines WG output: %d\n', length(wg_q_data(:)));
fprintf(fid,'Nof lines PFIR output: %d\n', length(pfir_subband_q_data(:)));
fprintf(fid,'Nof lines PFFT output: %d\n', length(pfft_subband_q_data(:)));
% Subband PFIR coefficients
fprintf(fid,'%d\n', int_hfir);
% WG output
for bI = tbegin:tend
for bJ = 1:N
fprintf(fid,'%d\n', wg_q_data(bI, bJ));
end
end;
% PFIR subband output
for bI = tbegin:tend
for bJ = 1:N
fprintf(fid,'%d\n', pfir_subband_q_data(bI, bJ));
end
end
% PFFT subband output real and imaginary
for bI = tbegin:tend
for bJ = 1:tb.nof_subbands
fprintf(fid,'%10d%10d\n', real(pfft_subband_q_data(bI, bJ)), imag(pfft_subband_q_data(bI, bJ)));
end
end
fclose(fid);
end
%% Log subband statistics for subband_i
% bin indices for a subband spectrum after the impulse response has
% settled, so after ctrl_pfir_subband.nof_taps * tb.nof_subbands
tg = (ctrl_pfir_subband.nof_taps + 4) * tb.nof_subbands;
tgI = tg + 1;
sub_bins = tgI + [0:tb.nof_subbands-1]; % Matlab indices of subbands in block tg
sub_I_bin = tgI + tb.subband_i; % Matlab index of subband_I
sub_ampl = abs(data_pfft_subband);
data = sub_ampl.';
data = data(:);
sub_I_ampl = data(sub_I_bin);
sub_I_power = sub_I_ampl^2;
sub_spurious = data(sub_bins);
sub_spurious(tb.subband_I) = 0;
sub_spurious_max = max(sub_spurious);
sub_spurious_power = sum(sub_spurious.^2);
sub_I_snr = sub_I_power / sub_spurious_power;
sub_I_snr_max = sub_I_power / sub_spurious_max^2;
disp(sprintf(['\nStatistics for WG subband %f in subband bin %d:\n', ...
'. Subband amplitude = %10.8f\n', ...
'. Subband power = %10.8f\n', ...
'. Spurious amplitude max = %10.8f\n', ...
'. Spurious total power = %10.8f\n', ...
'. SNR subband / spurious total = %f dB\n', ...
'. SNR subband / spurious max = %f dB\n'], ...
tb.subband_wg, tb.subband_i, ...
sub_I_ampl, ...
sub_I_power, ...
sub_spurious_max, ...
sub_spurious_power, ...
10 * log10(sub_I_snr), ...
10 * log10(sub_I_snr_max)));
%% Plot data path results
xfig = 300;
yfig = 200;
xfigw = 1000;
yfigw = 800;
dfig = 20;
ts = (0:tb.subband_fft_size*tb.nof_tsub-1)/tb.subband_fft_size; % time of ADC / WG samples in subband periods
tsub_all = (0: tb.nof_subbands*tb.nof_tsub-1)/tb.nof_subbands; % time in subband periods for block of subbands
tsub_one = (0: tb.nof_tsub-1); % time in subband periods for one subband
sub_I = tb.subband_I + [0: tb.nof_subbands: tb.nof_subbands*tb.nof_tsub-1]; % get Matlab indices of all subband_I
sub_Iplus1 = tb.subband_Iplus1 + [0: tb.nof_subbands: tb.nof_subbands*tb.nof_tsub-1]; % get Matlab indices of all next neighbour subband_I+1
%% Plot WG output
fig=fig+1;
figure('position', [xfig+fig*dfig yfig-fig*dfig xfigw yfigw]);
figure(fig);
data = data_wg.';
plot(ts, data(:))
ylim([-1.3 1.3]);
title(sprintf('WG output data (WG subband %6.3f)', tb.subband_wg));
xlabel(sprintf('Time 0:%d [Tsub]', tb.nof_tsub-1));
ylabel('Voltage');
grid on;
%% Plot FIR-filter coefficients
fig=fig+1;
figure('position', [xfig+fig*dfig yfig-fig*dfig xfigw yfigw]);
figure(fig);
h = hfir_subband_coeff;
NL = ctrl_pfir_subband.nof_coefficients;
N = ctrl_pfir_subband.nof_polyphases;
L = ctrl_pfir_subband.nof_taps;
hi = 1:NL; % coefficients index
hx = hi / N; % coefficients axis in tap units
if ctrl_pfir_subband.coeff_w == 0
plot(hx, h);
title(['FIR filter coefficients for ', ctrl_pfir_subband.config.design, ' (floating point)']);
else
plot(hx, h);
title(['FIR filter coefficients for ', ctrl_pfir_subband.config.design, ' (', num2str(ctrl_pfir_subband.coeff_w), ' bits)']);
end
ylo = min(h)*1.2;
yhi = max(h)*1.2;
ylim([ylo yhi]);
grid on;
%% Plot subband PFIR transfer function
h = hfir_subband_coeff;
NL = ctrl_pfir_subband.nof_coefficients;
L = ctrl_pfir_subband.nof_taps;
hf_abs = abs(fftshift(fft(h / sum(h), NL)));
hi = 1:NL; % coefficients index
fx = (hi - NL/2-1) / L; % frequency axis in subband units
fig=fig+1;
figure('position', [xfig+fig*dfig yfig-fig*dfig xfigw yfigw]);
figure(fig);
plot(fx, db(hf_abs)); % db() = 20*log10 for voltage
%xlim([-3 3]);
grid on;
title(['Subband PFIR filter transfer function for ', ctrl_pfir_subband.config.design, ' (nof taps = ', int2str(ctrl_pfir_subband.nof_taps), ')']);
xlabel('Frequency [channels]');
ylabel('Magnitude [dB]');
%% Plot subband PFIR output
fig=fig+1;
figure('position', [xfig+fig*dfig yfig-fig*dfig xfigw yfigw]);
figure(fig);
data = data_pfir_subband.';
plot(ts, data(:))
title(sprintf('Subband PFIR filter output - FFT input data (WG subband %6.3f)', tb.subband_wg));
ylim([-3 3]); % Delay tracking step when tb.subband_wg is .5 causes double range
xlabel(sprintf('Time 0:%d [Tsub]', tb.nof_tsub-1));
ylabel('Voltage');
grid on;
%% Plot PFFT subbands spectrum and phase for all tb.nof_tsub in one plot
sub_ampl = abs(data_pfft_subband);
sub_ampl_max = max(sub_ampl(:));
sub_phase = angle(data_pfft_subband)*180/pi;
x = sub_ampl < 0.1*sub_ampl_max;
sub_phase(x) = 0; % force phase of too small signals to 0
fig=fig+1;
figure('position', [xfig+fig*dfig yfig-fig*dfig xfigw yfigw]);
figure(fig);
subplot(3,1,1);
data = sub_ampl.';
data = data(:);
plot(tsub_all, data, 'k', tsub_all(sub_I), data(sub_I), 'ko', tsub_all(sub_Iplus1), data(sub_Iplus1), 'kx');
title(sprintf('Subband data - amplitude (o,x = subband %d,%d for WG subband = %6.3f)', tb.subband_i, tb.subband_iplus1, tb.subband_wg));
xlabel(sprintf('Subbands 0:%d at time 0:%d [Tsub]', tb.nof_subbands-1, tb.nof_tsub-1));
ylabel('Voltage');
grid on;
% Plot subrange of Tsub after impulse response at low level scale to zoom
% in on spurious levels, see also plot subband spectrogram
subplot(3,1,2);
data = sub_ampl.';
data = data(:);
plot(tsub_all, data, 'k', tsub_all(sub_I), data(sub_I), 'ko', tsub_all(sub_Iplus1), data(sub_Iplus1), 'kx');
xlim([19.9 23.1])
ylim([-0.001 0.005])
title(sprintf('Subband data - spurious levels (for WG subband = %6.3f)', tb.subband_wg));
xlabel(sprintf('Subbands 0:%d at time 0:%d [Tsub]', tb.nof_subbands-1, tb.nof_tsub-1));
ylabel('Voltage');
grid on;
subplot(3,1,3);
data = sub_phase.';
data = data(:);
plot(tsub_all, data, 'k', tsub_all(sub_I), data(sub_I), 'ko', tsub_all(sub_Iplus1), data(sub_Iplus1), 'kx');
ylim([-180 180])
title(sprintf('Subband data - phase (o,x = subband %d,%d for WG subband = %6.3f)', tb.subband_i, tb.subband_iplus1, tb.subband_wg));
xlabel(sprintf('Subbands 0:%d at time 0:%d [Tsub]', tb.nof_subbands-1, tb.nof_tsub-1));
ylabel('Phase [degrees]');
grid on;
%% Plot phase for the subband that is set in the WG
wg_sub_phase = angle(data_pfft_subband(:, tb.subband_I))*180/pi;
fig=fig+1;
figure('position', [xfig+fig*dfig yfig-fig*dfig xfigw yfigw]);
figure(fig);
plot(tsub_one, wg_sub_phase, '-o')
ylim([-180 180])
title(sprintf('Subband phase for subband %d in range 0:%d', tb.subband_i, tb.nof_subbands-1));
xlabel(sprintf('Time 0:%d [Tsub]', tb.nof_tsub-1));
ylabel('Phase [degrees]');
grid on;
%% Plot PFFT subbands spectrum and phase for all tb.nof_tsub in separate plots
sub_ampl = abs(data_pfft_subband);
sub_ampl_max = max(sub_ampl(:));
sub_phase = angle(data_pfft_subband)*180/pi;
x = sub_ampl < 0.1*sub_ampl_max;
sub_phase(x) = 0; % force phase of too small signals to 0
fig=fig+1;
figure('position', [xfig+fig*dfig yfig-fig*dfig xfigw yfigw]);
figure(fig);
if tb.plot_per_block
for bi = 0:tb.nof_tsub-1
bI = bi+1;
subplot(2,1,1);
data = sub_ampl(bI, :);
plot(0:tb.nof_subbands-1, data, 'k', tb.subband_i, data(tb.subband_I), 'ko', tb.subband_iplus1, data(tb.subband_Iplus1), 'kx');
ylim([0 sub_ampl_max])
title(sprintf('Subband data - amplitude (o,x = subband %d,%d for WG subband = %6.3f)', tb.subband_i, tb.subband_iplus1, tb.subband_wg));
xlabel(sprintf('Subbands 0:%d at time %d [Tsub]', tb.nof_subbands-1, bi));
ylabel('Voltage');
grid on;
subplot(2,1,2);
data = sub_phase(bI, :);
plot(0:tb.nof_subbands-1, data, 'k', tb.subband_i, data(tb.subband_I), 'ko', tb.subband_iplus1, data(tb.subband_Iplus1), 'kx');
ylim([-180 180])
title(sprintf('Subband data - amplitude (o,x = subband index %d,%d for WG subband = %6.3f)', tb.subband_i, tb.subband_iplus1, tb.subband_wg));
xlabel(sprintf('Subbands 0:%d at time %d [Tsub]', tb.nof_subbands-1, bi));
ylabel('Phase [degrees]');
grid on;
drawnow;
%pause(0.3)
end
end
%% Plot subband spectrogram for all tb.nof_tsub in one plot
data = db(sub_ampl); % no need to scale data, range is already normalized
if strcmp(tb.model_quantization, 'floating point')
db_low = -150;
else
db_low = -20 - floor(6.02 * ctrl_pfft_subband.data_w);
end
%db_low = floor(min(data(data~=-Inf)))
data(data<db_low) = db_low;
mymap = jet(-db_low);
%mymap(1,:) = [0 0 0]; % force black for dB(0)
colormap(mymap);
imagesc(data',[db_low 0]);
colorbar;
title(sprintf('Subband spectogram (max value = %f = %.2f dB)', sub_ampl_max, db(sub_ampl_max)));
xlabel(sprintf('Time 0:%d [Tsub]', tb.nof_tsub-1));
ylabel(sprintf('Subbands 0:%d', tb.nof_subbands-1));