5 from scipy import fftpack
7 print "Please install SciPy to run this script (http://www.scipy.org/)"
11 from PyQt4 import Qt, QtCore, QtGui
12 import PyQt4.Qwt5 as Qwt
13 from optparse import OptionParser
14 from gnuradio import gr, blks2, eng_notation
15 from scipy import fftpack
17 from pyqt_filter import Ui_MainWindow
18 from pyqt_filter_firlpf import Ui_firlpfForm
19 from pyqt_filter_firhpf import Ui_firhpfForm
21 class gr_plot_filter(QtGui.QMainWindow):
22 def __init__(self, qapp, options):
23 QtGui.QWidget.__init__(self, None)
24 self.gui = Ui_MainWindow()
25 self.gui.setupUi(self)
27 self.connect(self.gui.filterTypeComboBox,
28 Qt.SIGNAL("currentIndexChanged(const QString&)"),
29 self.changed_filter_type)
30 self.connect(self.gui.filterDesignTypeComboBox,
31 Qt.SIGNAL("currentIndexChanged(const QString&)"),
32 self.changed_filter_design_type)
34 self.connect(self.gui.designButton,
35 Qt.SIGNAL("released()"),
38 self.connect(self.gui.tabGroup,
39 Qt.SIGNAL("currentChanged(int)"),
42 self.connect(self.gui.nfftEdit,
43 Qt.SIGNAL("textEdited(QString)"),
44 self.nfft_edit_changed)
46 self.gui.designButton.setShortcut("Return")
52 self.nfftpts = int(10000)
53 self.gui.nfftEdit.setText(Qt.QString("%1").arg(self.nfftpts))
55 self.gui.lpfPassBandRippleLabel.setVisible(False)
56 self.gui.lpfPassBandRippleEdit.setVisible(False)
57 self.gui.bpfPassBandRippleLabel.setVisible(False)
58 self.gui.bpfPassBandRippleEdit.setVisible(False)
59 self.gui.hpfPassBandRippleLabel.setVisible(False)
60 self.gui.hpfPassBandRippleEdit.setVisible(False)
63 self.gui.filterTypeWidget.setCurrentWidget(self.gui.firlpfPage)
66 self.gui.freqPlot.setAxisTitle(self.gui.freqPlot.xBottom,
68 self.gui.freqPlot.setAxisTitle(self.gui.freqPlot.yLeft,
70 self.gui.timePlot.setAxisTitle(self.gui.timePlot.xBottom,
72 self.gui.timePlot.setAxisTitle(self.gui.timePlot.yLeft,
74 self.gui.phasePlot.setAxisTitle(self.gui.phasePlot.xBottom,
76 self.gui.phasePlot.setAxisTitle(self.gui.phasePlot.yLeft,
78 self.gui.groupPlot.setAxisTitle(self.gui.groupPlot.xBottom,
80 self.gui.groupPlot.setAxisTitle(self.gui.groupPlot.yLeft,
84 self.rcurve = Qwt.QwtPlotCurve("Real")
85 self.rcurve.attach(self.gui.timePlot)
86 self.icurve = Qwt.QwtPlotCurve("Imag")
87 self.icurve.attach(self.gui.timePlot)
89 self.freqcurve = Qwt.QwtPlotCurve("PSD")
90 self.freqcurve.attach(self.gui.freqPlot)
92 self.phasecurve = Qwt.QwtPlotCurve("Phase")
93 self.phasecurve.attach(self.gui.phasePlot)
95 self.groupcurve = Qwt.QwtPlotCurve("Group Delay")
96 self.groupcurve.attach(self.gui.groupPlot)
98 # Create zoom functionality for the plots
99 self.timeZoomer = Qwt.QwtPlotZoomer(self.gui.timePlot.xBottom,
100 self.gui.timePlot.yLeft,
101 Qwt.QwtPicker.PointSelection,
102 Qwt.QwtPicker.AlwaysOn,
103 self.gui.timePlot.canvas())
105 self.freqZoomer = Qwt.QwtPlotZoomer(self.gui.freqPlot.xBottom,
106 self.gui.freqPlot.yLeft,
107 Qwt.QwtPicker.PointSelection,
108 Qwt.QwtPicker.AlwaysOn,
109 self.gui.freqPlot.canvas())
111 self.phaseZoomer = Qwt.QwtPlotZoomer(self.gui.phasePlot.xBottom,
112 self.gui.phasePlot.yLeft,
113 Qwt.QwtPicker.PointSelection,
114 Qwt.QwtPicker.AlwaysOn,
115 self.gui.phasePlot.canvas())
117 self.groupZoomer = Qwt.QwtPlotZoomer(self.gui.groupPlot.xBottom,
118 self.gui.groupPlot.yLeft,
119 Qwt.QwtPicker.PointSelection,
120 Qwt.QwtPicker.AlwaysOn,
121 self.gui.groupPlot.canvas())
123 # Set up pen for colors and line width
124 blue = QtGui.qRgb(0x00, 0x00, 0xFF)
125 blueBrush = Qt.QBrush(Qt.QColor(blue))
126 self.freqcurve.setPen(Qt.QPen(blueBrush, 2))
127 self.rcurve.setPen(Qt.QPen(blueBrush, 2))
128 self.phasecurve.setPen(Qt.QPen(blueBrush, 2))
129 self.groupcurve.setPen(Qt.QPen(blueBrush, 2))
131 self.filterWindows = {"Hamming Window" : gr.firdes.WIN_HAMMING,
132 "Hann Window" : gr.firdes.WIN_HANN,
133 "Blackman Window" : gr.firdes.WIN_BLACKMAN,
134 "Rectangular Window" : gr.firdes.WIN_RECTANGULAR,
135 "Kaiser Window" : gr.firdes.WIN_KAISER,
136 "Blackman-harris Window" : gr.firdes.WIN_BLACKMAN_hARRIS}
140 def changed_filter_type(self, ftype):
141 strftype = str(ftype.toAscii())
142 if(ftype == "Low Pass"):
143 self.gui.filterTypeWidget.setCurrentWidget(self.gui.firlpfPage)
144 elif(ftype == "Band Pass"):
145 self.gui.filterTypeWidget.setCurrentWidget(self.gui.firbpfPage)
146 elif(ftype == "Complex Band Pass"):
147 self.gui.filterTypeWidget.setCurrentWidget(self.gui.firbpfPage)
148 elif(ftype == "Band Notch"):
149 self.gui.filterTypeWidget.setCurrentWidget(self.gui.firbnfPage)
150 elif(ftype == "High Pass"):
151 self.gui.filterTypeWidget.setCurrentWidget(self.gui.firhpfPage)
152 elif(ftype == "Root Raised Cosine"):
153 self.gui.filterTypeWidget.setCurrentWidget(self.gui.rrcPage)
157 def changed_filter_design_type(self, design):
158 if(design == "Equiripple"):
159 self.set_equiripple()
165 def set_equiripple(self):
166 self.equiripple = True
167 self.gui.lpfPassBandRippleLabel.setVisible(True)
168 self.gui.lpfPassBandRippleEdit.setVisible(True)
169 self.gui.bpfPassBandRippleLabel.setVisible(True)
170 self.gui.bpfPassBandRippleEdit.setVisible(True)
171 self.gui.hpfPassBandRippleLabel.setVisible(True)
172 self.gui.hpfPassBandRippleEdit.setVisible(True)
174 def set_windowed(self):
175 self.equiripple = False
176 self.gui.lpfPassBandRippleLabel.setVisible(False)
177 self.gui.lpfPassBandRippleEdit.setVisible(False)
178 self.gui.bpfPassBandRippleLabel.setVisible(False)
179 self.gui.bpfPassBandRippleEdit.setVisible(False)
180 self.gui.hpfPassBandRippleLabel.setVisible(False)
181 self.gui.hpfPassBandRippleEdit.setVisible(False)
185 fs,r = self.gui.sampleRateEdit.text().toDouble()
187 gain,r = self.gui.filterGainEdit.text().toDouble()
191 winstr = str(self.gui.filterDesignTypeComboBox.currentText().toAscii())
192 ftype = str(self.gui.filterTypeComboBox.currentText().toAscii())
194 if(winstr == "Equiripple"):
195 designer = {"Low Pass" : self.design_opt_lpf,
196 "Band Pass" : self.design_opt_bpf,
197 "High Pass" : self.design_opt_hpf}
198 taps,r = designer[ftype](fs, gain)
201 designer = {"Low Pass" : self.design_win_lpf,
202 "Band Pass" : self.design_win_bpf,
203 "Complex Band Pass" : self.design_win_cbpf,
204 "Band Notch" : self.design_win_bnf,
205 "High Pass" : self.design_win_hpf,
206 "Root Raised Cosine" : self.design_win_rrc}
207 wintype = self.filterWindows[winstr]
208 taps,r = designer[ftype](fs, gain, wintype)
211 self.taps = scipy.array(taps)
212 self.get_fft(fs, self.taps, self.nfftpts)
213 self.update_time_curves()
214 self.update_freq_curves()
215 self.update_phase_curves()
216 self.update_group_curves()
218 # Filter design functions using a window
219 def design_win_lpf(self, fs, gain, wintype):
221 pb,r = self.gui.endofLpfPassBandEdit.text().toDouble()
223 sb,r = self.gui.startofLpfStopBandEdit.text().toDouble()
225 atten,r = self.gui.lpfStopBandAttenEdit.text().toDouble()
231 taps = gr.firdes.low_pass_2(gain, fs, pb, tb,
237 def design_win_bpf(self, fs, gain, wintype):
239 pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
241 pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
243 tb,r = self.gui.bpfTransitionEdit.text().toDouble()
245 atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
249 taps = gr.firdes.band_pass_2(gain, fs, pb1, pb2, tb,
255 def design_win_cbpf(self, fs, gain, wintype):
257 pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
259 pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
261 tb,r = self.gui.bpfTransitionEdit.text().toDouble()
263 atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
267 taps = gr.firdes.complex_band_pass_2(gain, fs, pb1, pb2, tb,
273 def design_win_bnf(self, fs, gain, wintype):
275 pb1,r = self.gui.startofBnfStopBandEdit.text().toDouble()
277 pb2,r = self.gui.endofBnfStopBandEdit.text().toDouble()
279 tb,r = self.gui.bnfTransitionEdit.text().toDouble()
281 atten,r = self.gui.bnfStopBandAttenEdit.text().toDouble()
285 taps = gr.firdes.band_reject_2(gain, fs, pb1, pb2, tb,
291 def design_win_hpf(self, fs, gain, wintype):
293 sb,r = self.gui.endofHpfStopBandEdit.text().toDouble()
295 pb,r = self.gui.startofHpfPassBandEdit.text().toDouble()
297 atten,r = self.gui.hpfStopBandAttenEdit.text().toDouble()
302 taps = gr.firdes.high_pass_2(gain, fs, pb, tb,
308 def design_win_rrc(self, fs, gain, wintype):
310 sr,r = self.gui.rrcSymbolRateEdit.text().toDouble()
312 alpha,r = self.gui.rrcAlphaEdit.text().toDouble()
314 ntaps,r = self.gui.rrcNumTapsEdit.text().toInt()
318 taps = gr.firdes.root_raised_cosine(gain, fs, sr,
324 # Design Functions for Equiripple Filters
325 def design_opt_lpf(self, fs, gain, wintype=None):
327 pb,r = self.gui.endofLpfPassBandEdit.text().toDouble()
329 sb,r = self.gui.startofLpfStopBandEdit.text().toDouble()
331 atten,r = self.gui.lpfStopBandAttenEdit.text().toDouble()
333 ripple,r = self.gui.lpfPassBandRippleEdit.text().toDouble()
337 taps = blks2.optfir.low_pass(gain, fs, pb, sb,
343 def design_opt_bpf(self, fs, gain, wintype=None):
345 pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
347 pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
349 tb,r = self.gui.bpfTransitionEdit.text().toDouble()
351 atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
353 ripple,r = self.gui.bpfPassBandRippleEdit.text().toDouble()
359 taps = blks2.optfir.band_pass(gain, fs, sb1, pb1, pb2, sb2,
365 def design_opt_hpf(self, fs, gain, wintype=None):
367 sb,r = self.gui.endofHpfStopBandEdit.text().toDouble()
369 pb,r = self.gui.startofHpfPassBandEdit.text().toDouble()
371 atten,r = self.gui.hpfStopBandAttenEdit.text().toDouble()
373 ripple,r = self.gui.hpfPassBandRippleEdit.text().toDouble()
377 taps = blks2.optfir.high_pass(gain, fs, sb, pb,
383 def nfft_edit_changed(self, nfft):
384 infft,r = nfft.toInt()
385 if(r and (infft != self.nfftpts)):
387 self.update_freq_curves()
389 def tab_changed(self, tab):
391 self.update_freq_curves()
393 self.update_time_curves()
395 self.update_phase_curves()
397 self.update_group_curves()
399 def get_fft(self, fs, taps, Npts):
401 fftpts = fftpack.fft(taps, Npts)
402 self.freq = scipy.arange(0, fs, 1.0/(Npts*Ts))
403 self.fftdB = 20.0*scipy.log10(abs(fftpts))
404 self.fftDeg = scipy.unwrap(scipy.angle(fftpts))
405 self.groupDelay = -scipy.diff(self.fftDeg)
407 def update_time_curves(self):
408 ntaps = len(self.taps)
410 if(type(self.taps[0]) == scipy.complex128):
411 self.rcurve.setData(scipy.arange(ntaps), self.taps.real)
412 self.icurve.setData(scipy.arange(ntaps), self.taps.imag)
414 self.rcurve.setData(scipy.arange(ntaps), self.taps)
416 # Reset the x-axis to the new time scale
417 ymax = 1.5 * max(self.taps)
418 ymin = 1.5 * min(self.taps)
419 self.gui.timePlot.setAxisScale(self.gui.timePlot.xBottom,
421 self.gui.timePlot.setAxisScale(self.gui.timePlot.yLeft,
424 # Set the zoomer base to unzoom to the new axis
425 self.timeZoomer.setZoomBase()
427 self.gui.timePlot.replot()
429 def update_freq_curves(self):
430 npts = len(self.fftdB)
432 self.freqcurve.setData(self.freq, self.fftdB)
434 # Reset the x-axis to the new time scale
435 ymax = 1.5 * max(self.fftdB[0:npts/2])
436 ymin = 1.1 * min(self.fftdB[0:npts/2])
437 xmax = self.freq[npts/2]
439 self.gui.freqPlot.setAxisScale(self.gui.freqPlot.xBottom,
441 self.gui.freqPlot.setAxisScale(self.gui.freqPlot.yLeft,
444 # Set the zoomer base to unzoom to the new axis
445 self.freqZoomer.setZoomBase()
447 self.gui.freqPlot.replot()
450 def update_phase_curves(self):
451 npts = len(self.fftDeg)
453 self.phasecurve.setData(self.freq, self.fftDeg)
455 # Reset the x-axis to the new time scale
456 ymax = 1.5 * max(self.fftDeg[0:npts/2])
457 ymin = 1.1 * min(self.fftDeg[0:npts/2])
458 xmax = self.freq[npts/2]
460 self.gui.phasePlot.setAxisScale(self.gui.phasePlot.xBottom,
462 self.gui.phasePlot.setAxisScale(self.gui.phasePlot.yLeft,
465 # Set the zoomer base to unzoom to the new axis
466 self.phaseZoomer.setZoomBase()
468 self.gui.phasePlot.replot()
470 def update_group_curves(self):
471 npts = len(self.groupDelay)
473 self.groupcurve.setData(self.freq, self.groupDelay)
475 # Reset the x-axis to the new time scale
476 ymax = 1.5 * max(self.groupDelay[0:npts/2])
477 ymin = 1.1 * min(self.groupDelay[0:npts/2])
478 xmax = self.freq[npts/2]
480 self.gui.groupPlot.setAxisScale(self.gui.groupPlot.xBottom,
482 self.gui.groupPlot.setAxisScale(self.gui.groupPlot.yLeft,
485 # Set the zoomer base to unzoom to the new axis
486 self.groupZoomer.setZoomBase()
488 self.gui.groupPlot.replot()
492 usage="%prog: [options] (input_filename)"
495 parser = OptionParser(conflict_handler="resolve",
496 usage=usage, description=description)
500 parser = setup_options()
501 (options, args) = parser.parse_args ()
503 app = Qt.QApplication(args)
504 gplt = gr_plot_filter(app, options)
507 if __name__ == '__main__':