Merge r6461:6464 from jcorgan/t162-staging into trunk.
[debian/gnuradio] / gnuradio-core / src / python / gnuradio / blks2impl / filterbank.py
1 #
2 # Copyright 2005,2007 Free Software Foundation, Inc.
3
4 # This file is part of GNU Radio
5
6 # GNU Radio is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3, or (at your option)
9 # any later version.
10
11 # GNU Radio is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with GNU Radio; see the file COPYING.  If not, write to
18 # the Free Software Foundation, Inc., 51 Franklin Street,
19 # Boston, MA 02110-1301, USA.
20
21
22 import sys
23 from gnuradio import gr, gru
24
25 def _generate_synthesis_taps(mpoints):
26     return []   # FIXME
27
28
29 def _split_taps(taps, mpoints):
30     assert (len(taps) % mpoints) == 0
31     result = [list() for x in range(mpoints)]
32     for i in xrange(len(taps)):
33         (result[i % mpoints]).append(taps[i])
34     return [tuple(x) for x in result]
35
36
37 class synthesis_filterbank(gr.hier_block2):
38     """
39     Uniformly modulated polyphase DFT filter bank: synthesis
40
41     See http://cnx.rice.edu/content/m10424/latest
42     """
43     def __init__(self, mpoints, taps=None):
44         """
45         Takes M complex streams in, produces single complex stream out
46         that runs at M times the input sample rate
47
48         @param mpoints: number of freq bins/interpolation factor/subbands
49         @param taps:    filter taps for subband filter
50
51         The channel spacing is equal to the input sample rate.
52         The total bandwidth and output sample rate are equal the input
53         sample rate * nchannels.
54
55         Output stream to frequency mapping:
56         
57           channel zero is at zero frequency.
58
59           if mpoints is odd:
60             
61             Channels with increasing positive frequencies come from
62             channels 1 through (N-1)/2.
63
64             Channel (N+1)/2 is the maximum negative frequency, and
65             frequency increases through N-1 which is one channel lower
66             than the zero frequency.
67
68           if mpoints is even:
69
70             Channels with increasing positive frequencies come from
71             channels 1 through (N/2)-1.
72
73             Channel (N/2) is evenly split between the max positive and
74             negative bins.
75
76             Channel (N/2)+1 is the maximum negative frequency, and
77             frequency increases through N-1 which is one channel lower
78             than the zero frequency.
79
80             Channels near the frequency extremes end up getting cut
81             off by subsequent filters and therefore have diminished
82             utility.
83         """
84         item_size = gr.sizeof_gr_complex
85         gr.hier_block2.__init__(self, "synthesis_filterbank",
86                                 gr.io_signature(mpoints, mpoints, item_size), # Input signature
87                                 gr.io_signature(1, 1, item_size))             # Output signature
88
89
90         if taps is None:
91             taps = _generate_synthesis_taps(mpoints)
92
93         # pad taps to multiple of mpoints
94         r = len(taps) % mpoints
95         if r != 0:
96             taps = taps + (mpoints - r) * (0,)
97
98         # split in mpoints separate set of taps
99         sub_taps = _split_taps(taps, mpoints)
100
101         self.ss2v = gr.streams_to_vector(item_size, mpoints)
102         self.ifft = gr.fft_vcc(mpoints, False, [])
103         self.v2ss = gr.vector_to_streams(item_size, mpoints)
104         # mpoints filters go in here...
105         self.ss2s = gr.streams_to_stream(item_size, mpoints)
106
107         for i in range(mpoints):
108             self.connect((self, i), (self.ss2v, i))
109             
110         self.connect(self.ss2v, self.ifft, self.v2ss, self)
111
112         # build mpoints fir filters...
113         for i in range(mpoints):
114             f = gr.fft_filter_ccc(1, sub_taps[i])
115             self.connect((self.v2ss, i), f)
116             self.connect(f, (self.ss2s, i))
117
118
119 class analysis_filterbank(gr.hier_block2):
120     """
121     Uniformly modulated polyphase DFT filter bank: analysis
122
123     See http://cnx.rice.edu/content/m10424/latest
124     """
125     def __init__(self, mpoints, taps=None):
126         """
127         Takes 1 complex stream in, produces M complex streams out
128         that runs at 1/M times the input sample rate
129
130         @param mpoints: number of freq bins/interpolation factor/subbands
131         @param taps:    filter taps for subband filter
132
133         Same channel to frequency mapping as described above.
134         """
135         item_size = gr.sizeof_gr_complex
136         gr.hier_block2.__init__(self, "analysis_filterbank",
137                                 gr.io_signature(1, 1, item_size),             # Input signature
138                                 gr.io_signature(mpoints, mpoints, item_size)) # Output signature
139         
140         if taps is None:
141             taps = _generate_synthesis_taps(mpoints)
142
143         # pad taps to multiple of mpoints
144         r = len(taps) % mpoints
145         if r != 0:
146             taps = taps + (mpoints - r) * (0,)
147         
148         # split in mpoints separate set of taps
149         sub_taps = _split_taps(taps, mpoints)
150
151         # print >> sys.stderr, "mpoints =", mpoints, "len(sub_taps) =", len(sub_taps) 
152         
153         self.s2ss = gr.stream_to_streams(item_size, mpoints)
154         # filters here
155         self.ss2v = gr.streams_to_vector(item_size, mpoints)
156         self.fft = gr.fft_vcc(mpoints, True, [])
157         self.v2ss = gr.vector_to_streams(item_size, mpoints)
158
159         self.connect(self, self.s2ss)
160         
161         # build mpoints fir filters...
162         for i in range(mpoints):
163             f = gr.fft_filter_ccc(1, sub_taps[mpoints-i-1])
164             self.connect((self.s2ss, i), f)
165             self.connect(f, (self.ss2v, i))
166             self.connect((self.v2ss, i), (self, i))
167             
168         self.connect(self.ss2v, self.fft, self.v2ss)