summaryrefslogtreecommitdiff
path: root/docs/doxygen/other/pfb_intro.dox
blob: 83313e956858b2426f681049ce8cbd9a9b971db5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# Copyright (C) 2017 Free Software Foundation, Inc.
#
# Permission is granted to copy, distribute and/or modify this document
# under the terms of the GNU Free Documentation License, Version 1.3
# or any later version published by the Free Software Foundation;
# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
# A copy of the license is included in the section entitled "GNU
# Free Documentation License".

/*! \page page_pfb Polyphase Filterbanks

\section pfb_introduction Introduction

Polyphase filterbanks (PFB) are a very powerful set of filtering tools
that can efficiently perform many multi-rate signal processing
tasks. GNU Radio has a set of polyphase filterbank blocks to be used
in all sorts of applications.

\section pfb_Usage

See the documentation for the individual blocks for details about what
they can do and how they should be used. Furthermore, there are
examples for these blocks in <b>gr-filter/examples</b>.

The main issue when using the PFB blocks is defining the prototype
filter, which is passed to all of the blocks as a vector of \p
taps. The taps from the prototype filter which get partitioned among
the \p N channels of the channelizer.

An example of creating a set of filter taps for a PFB channelizer is
found on line 49 of <b>gr-filter/examples/channelizer.py</b>
and reproduced below. Notice that the sample rate is the sample rate
at the input to the channelizer while the bandwidth and transition
width are defined for the channel bandwidths. This makes a fairly long
filter that is then split up between the \p N channels of the PFB.

\code
    self._fs = 9000          # input sample rate
    self._M = 9              # Number of channels to channelize

    self._taps = filter.firdes.low_pass_2(1, self._fs, 475.50, 50,
                                          attenuation_dB=100,
			                  window=filter.firdes.WIN_BLACKMAN_hARRIS)
\endcode

In this example, the signal into the channelizer is sampled at 9 ksps
(complex, so 9 kHz of bandwidth). The filter uses 9 channels, so each
output channel will have a bandwidth and sample rate of 1 kHz. We want
to pass most of the channel, so we define the channel bandwidth to be
a low pass filter with a bandwidth of 475.5 Hz and a transition
bandwidth of 50 Hz, but we have defined this using a sample rate of
the original 9 kHz. The prototype filter has 819 taps to be divided up
between the 9 channels, so each channel uses 91 taps. This is probably
over-kill for a channelizer, and we could reduce the amount of taps
per channel to a couple of dozen with no ill effects.

The basic rule when defining a set of taps for a PFB block is to think
about the filter running at the highest rate it will see while the
bandwidth is defined for the size of the channels. In the channelizer
case, the highest rate is defined as the rate of the incoming signal,
but in other PFB blocks, this is not so obvious.

Two very useful blocks to use are the arbitrary resampler and the
clock synchronizer (for PAM signals). These PFBs are defined with a
set number of filters based on the fidelity required from them, not
the rate changes. By default, the \p filter_size is set to 32 for
these blocks, which is a reasonable default for most tasks. Because
the PFB uses this number of filters in the filterbank, the maximum
rate of the bank is defined from this (see the theory of a polyphase
interpolator for a justification of this). So the prototype filter is
defined to use a sample rate of \p filter_size times the signal's
sampling rate.

A helpful wrapper for the arbitrary resampler is found in
<b>gr-filter/python/pfb.py</b>,
which is exposed in Python as <b>filter.pfb.arb_resampler_ccf</b> and
<b>filter.pfb.arb_resampler_fff</b>. This block is set up so that the
user only needs to pass it the real number \p rate as the resampling
rate. With just this information, this hierarchical block
automatically creates a filter that fully passes the signal bandwidth
being resampled but does not pass any out-of-band noise. See the code
for this block for details of how the filter is constructed.

Of course, a user can create his or her own taps and use them in the
arbitrary resampler for more specific requirements. Some of the UHD
examples (<b>gr-uhd/examples</b>) use this ability to create a
received matched filter or channel filter that also resamples the
signal.

\section pfb_examples Examples

The following is an example of the using the channelizer. It creates
the appropriate filter to channelizer 9 channels out of an original
signal that is 9000 Hz wide, so each output channel is now 1000
Hz. The code then plots the PSD of the original signal to see the
signals in the origina spectrum and then makes 9 plots for each of the
channels.

NOTE: you need the Scipy and Matplotlib Python modules installed to
run this example.

\include gr-filter/examples/channelize.py


\section pfb_arb_resampler The PFB Arbitrary Resampler Kernel

GNU Radio has a PFB arbitrary resampler block that can be used to
resample a signal to any arbitrary and real resampling rate. The
resampling feature is one that could easily be useful to other blocks,
and so we have extracted the kernel of the resampler into its own
class that can be used as such.

The PFB arbitrary resampler is defined in pfb_arb_resampler.h and has
the following constructor:

\code
namespace gr {
  namespace filter {
    namespace kernel {

        pfb_arb_resampler_XXX(float rate,
                              const std::vector<float> &taps,
                              unsigned int filter_size);

    } /* namespace kernel */
  } /* namespace filter */
} /* namespace gr */
\endcode

Currently, only a 'ccf' and 'fff' version are defined. This kernel,
like the block itself, takes in the resampling \p rate as a floating
point number. The \p taps are passed as the baseband prototype filter,
and the quantization error of the filter is determined by the \p
filter_size parameter.

The prototype taps are generated like all other PFB filter
taps. Specifically, we construct them generally as a lowpass filter at
the maximum rate of the filter. In the case of these resamplers, the
maximum rate is actually the number of filters.

A simple example follows. We construct a filter that will pass the
entire passband of the original signal to be resampled. To make it
easy, we work in normalized sample rates for this. The gain of the
filter is set to filter_size to compensate for the upsampling, the
sampling rate itself is also set to filter_size, which is assuming
that the incoming signal is at a sampling rate of 1.0. We defined the
passband to be 0.5 to pass the entire width of the original signal and
set a transition band to 0.1. Note that this causes a bit of roll-off
outside of the original passband and could lead to introducing some
aliasing. More care should be taken to construct the passband and
transition width of the filter for the given signal while keeping the
total number of taps small. A stopband attenuation of 60 dB was used
here, and again, this is a parameter we can adjust to alter the
performance and size of the filter.

\code
  firdes.low_pass_2(filter_size, filter_size, 0.5, 0.1, 60)
\endcode

As is typical with the PFB filters, a filter size of 32 is generally
an appropriate trade-off of accuracy, performance, and memory. This
should provide an error roughly equivalent to the quanization error of
using 16-bit fixed point representation. Generally, increasing over 32
provides some accuracy benefits without a huge increase in
computational demands.

*/