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
19 class gr_plot_filter(QtGui.QMainWindow):
20 def __init__(self, qapp, options):
21 QtGui.QWidget.__init__(self, None)
22 self.gui = Ui_MainWindow()
23 self.gui.setupUi(self)
25 self.connect(self.gui.filterTypeComboBox,
26 Qt.SIGNAL("currentIndexChanged(const QString&)"),
27 self.changed_filter_type)
28 self.connect(self.gui.filterDesignTypeComboBox,
29 Qt.SIGNAL("currentIndexChanged(const QString&)"),
30 self.changed_filter_design_type)
32 self.connect(self.gui.designButton,
33 Qt.SIGNAL("released()"),
36 self.connect(self.gui.tabGroup,
37 Qt.SIGNAL("currentChanged(int)"),
40 self.connect(self.gui.nfftEdit,
41 Qt.SIGNAL("textEdited(QString)"),
42 self.nfft_edit_changed)
44 self.gui.designButton.setShortcut(QtCore.Qt.Key_Return)
50 self.nfftpts = int(10000)
51 self.gui.nfftEdit.setText(Qt.QString("%1").arg(self.nfftpts))
53 self.gui.lpfPassBandRippleLabel.setVisible(False)
54 self.gui.lpfPassBandRippleEdit.setVisible(False)
55 self.gui.bpfPassBandRippleLabel.setVisible(False)
56 self.gui.bpfPassBandRippleEdit.setVisible(False)
57 self.gui.bnfPassBandRippleLabel.setVisible(False)
58 self.gui.bnfPassBandRippleEdit.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 # Set up validators for edit boxes
132 self.intVal = Qt.QIntValidator(None)
133 self.dblVal = Qt.QDoubleValidator(None)
134 self.gui.nfftEdit.setValidator(self.intVal)
135 self.gui.sampleRateEdit.setValidator(self.dblVal)
136 self.gui.filterGainEdit.setValidator(self.dblVal)
137 self.gui.endofLpfPassBandEdit.setValidator(self.dblVal)
138 self.gui.startofLpfStopBandEdit.setValidator(self.dblVal)
139 self.gui.lpfStopBandAttenEdit.setValidator(self.dblVal)
140 self.gui.lpfPassBandRippleEdit.setValidator(self.dblVal)
141 self.gui.startofBpfPassBandEdit.setValidator(self.dblVal)
142 self.gui.endofBpfPassBandEdit.setValidator(self.dblVal)
143 self.gui.bpfTransitionEdit.setValidator(self.dblVal)
144 self.gui.bpfStopBandAttenEdit.setValidator(self.dblVal)
145 self.gui.bpfPassBandRippleEdit.setValidator(self.dblVal)
146 self.gui.startofBnfStopBandEdit.setValidator(self.dblVal)
147 self.gui.endofBnfStopBandEdit.setValidator(self.dblVal)
148 self.gui.bnfTransitionEdit.setValidator(self.dblVal)
149 self.gui.bnfStopBandAttenEdit.setValidator(self.dblVal)
150 self.gui.bnfPassBandRippleEdit.setValidator(self.dblVal)
151 self.gui.endofHpfStopBandEdit.setValidator(self.dblVal)
152 self.gui.startofHpfPassBandEdit.setValidator(self.dblVal)
153 self.gui.hpfStopBandAttenEdit.setValidator(self.dblVal)
154 self.gui.hpfPassBandRippleEdit.setValidator(self.dblVal)
155 self.gui.rrcSymbolRateEdit.setValidator(self.dblVal)
156 self.gui.rrcAlphaEdit.setValidator(self.dblVal)
157 self.gui.rrcNumTapsEdit.setValidator(self.dblVal)
158 self.gui.gausSymbolRateEdit.setValidator(self.dblVal)
159 self.gui.gausBTEdit.setValidator(self.dblVal)
160 self.gui.gausNumTapsEdit.setValidator(self.dblVal)
162 self.gui.nTapsEdit.setText("0")
164 self.filterWindows = {"Hamming Window" : gr.firdes.WIN_HAMMING,
165 "Hann Window" : gr.firdes.WIN_HANN,
166 "Blackman Window" : gr.firdes.WIN_BLACKMAN,
167 "Rectangular Window" : gr.firdes.WIN_RECTANGULAR,
168 "Kaiser Window" : gr.firdes.WIN_KAISER,
169 "Blackman-harris Window" : gr.firdes.WIN_BLACKMAN_hARRIS}
173 def changed_filter_type(self, ftype):
174 strftype = str(ftype.toAscii())
175 if(ftype == "Low Pass"):
176 self.gui.filterTypeWidget.setCurrentWidget(self.gui.firlpfPage)
177 elif(ftype == "Band Pass"):
178 self.gui.filterTypeWidget.setCurrentWidget(self.gui.firbpfPage)
179 elif(ftype == "Complex Band Pass"):
180 self.gui.filterTypeWidget.setCurrentWidget(self.gui.firbpfPage)
181 elif(ftype == "Band Notch"):
182 self.gui.filterTypeWidget.setCurrentWidget(self.gui.firbnfPage)
183 elif(ftype == "High Pass"):
184 self.gui.filterTypeWidget.setCurrentWidget(self.gui.firhpfPage)
185 elif(ftype == "Root Raised Cosine"):
186 self.gui.filterTypeWidget.setCurrentWidget(self.gui.rrcPage)
187 elif(ftype == "Gaussian"):
188 self.gui.filterTypeWidget.setCurrentWidget(self.gui.gausPage)
192 def changed_filter_design_type(self, design):
193 if(design == "Equiripple"):
194 self.set_equiripple()
200 def set_equiripple(self):
201 self.equiripple = True
202 self.gui.lpfPassBandRippleLabel.setVisible(True)
203 self.gui.lpfPassBandRippleEdit.setVisible(True)
204 self.gui.bpfPassBandRippleLabel.setVisible(True)
205 self.gui.bpfPassBandRippleEdit.setVisible(True)
206 self.gui.bnfPassBandRippleLabel.setVisible(True)
207 self.gui.bnfPassBandRippleEdit.setVisible(True)
208 self.gui.hpfPassBandRippleLabel.setVisible(True)
209 self.gui.hpfPassBandRippleEdit.setVisible(True)
211 def set_windowed(self):
212 self.equiripple = False
213 self.gui.lpfPassBandRippleLabel.setVisible(False)
214 self.gui.lpfPassBandRippleEdit.setVisible(False)
215 self.gui.bpfPassBandRippleLabel.setVisible(False)
216 self.gui.bpfPassBandRippleEdit.setVisible(False)
217 self.gui.bnfPassBandRippleLabel.setVisible(False)
218 self.gui.bnfPassBandRippleEdit.setVisible(False)
219 self.gui.hpfPassBandRippleLabel.setVisible(False)
220 self.gui.hpfPassBandRippleEdit.setVisible(False)
224 fs,r = self.gui.sampleRateEdit.text().toDouble()
226 gain,r = self.gui.filterGainEdit.text().toDouble()
230 winstr = str(self.gui.filterDesignTypeComboBox.currentText().toAscii())
231 ftype = str(self.gui.filterTypeComboBox.currentText().toAscii())
233 if(winstr == "Equiripple"):
234 designer = {"Low Pass" : self.design_opt_lpf,
235 "Band Pass" : self.design_opt_bpf,
236 "Complex Band Pass" : self.design_opt_cbpf,
237 "Band Notch" : self.design_opt_bnf,
238 "High Pass" : self.design_opt_hpf}
239 taps,r = designer[ftype](fs, gain)
242 designer = {"Low Pass" : self.design_win_lpf,
243 "Band Pass" : self.design_win_bpf,
244 "Complex Band Pass" : self.design_win_cbpf,
245 "Band Notch" : self.design_win_bnf,
246 "High Pass" : self.design_win_hpf,
247 "Root Raised Cosine" : self.design_win_rrc,
248 "Gaussian" : self.design_win_gaus}
249 wintype = self.filterWindows[winstr]
250 taps,r = designer[ftype](fs, gain, wintype)
253 self.taps = scipy.array(taps)
254 self.get_fft(fs, self.taps, self.nfftpts)
255 self.update_time_curves()
256 self.update_freq_curves()
257 self.update_phase_curves()
258 self.update_group_curves()
260 self.gui.nTapsEdit.setText(Qt.QString("%1").arg(self.taps.size))
263 # Filter design functions using a window
264 def design_win_lpf(self, fs, gain, wintype):
266 pb,r = self.gui.endofLpfPassBandEdit.text().toDouble()
268 sb,r = self.gui.startofLpfStopBandEdit.text().toDouble()
270 atten,r = self.gui.lpfStopBandAttenEdit.text().toDouble()
276 taps = gr.firdes.low_pass_2(gain, fs, pb, tb,
282 def design_win_bpf(self, fs, gain, wintype):
284 pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
286 pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
288 tb,r = self.gui.bpfTransitionEdit.text().toDouble()
290 atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
294 taps = gr.firdes.band_pass_2(gain, fs, pb1, pb2, tb,
300 def design_win_cbpf(self, fs, gain, wintype):
302 pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
304 pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
306 tb,r = self.gui.bpfTransitionEdit.text().toDouble()
308 atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
312 taps = gr.firdes.complex_band_pass_2(gain, fs, pb1, pb2, tb,
318 def design_win_bnf(self, fs, gain, wintype):
320 pb1,r = self.gui.startofBnfStopBandEdit.text().toDouble()
322 pb2,r = self.gui.endofBnfStopBandEdit.text().toDouble()
324 tb,r = self.gui.bnfTransitionEdit.text().toDouble()
326 atten,r = self.gui.bnfStopBandAttenEdit.text().toDouble()
330 taps = gr.firdes.band_reject_2(gain, fs, pb1, pb2, tb,
336 def design_win_hpf(self, fs, gain, wintype):
338 sb,r = self.gui.endofHpfStopBandEdit.text().toDouble()
340 pb,r = self.gui.startofHpfPassBandEdit.text().toDouble()
342 atten,r = self.gui.hpfStopBandAttenEdit.text().toDouble()
347 taps = gr.firdes.high_pass_2(gain, fs, pb, tb,
353 def design_win_rrc(self, fs, gain, wintype):
355 sr,r = self.gui.rrcSymbolRateEdit.text().toDouble()
357 alpha,r = self.gui.rrcAlphaEdit.text().toDouble()
359 ntaps,r = self.gui.rrcNumTapsEdit.text().toInt()
363 taps = gr.firdes.root_raised_cosine(gain, fs, sr,
369 def design_win_gaus(self, fs, gain, wintype):
371 sr,r = self.gui.gausSymbolRateEdit.text().toDouble()
373 bt,r = self.gui.gausBTEdit.text().toDouble()
375 ntaps,r = self.gui.gausNumTapsEdit.text().toInt()
380 taps = gr.firdes.gaussian(gain, spb, bt, ntaps)
385 # Design Functions for Equiripple Filters
386 def design_opt_lpf(self, fs, gain):
388 pb,r = self.gui.endofLpfPassBandEdit.text().toDouble()
390 sb,r = self.gui.startofLpfStopBandEdit.text().toDouble()
392 atten,r = self.gui.lpfStopBandAttenEdit.text().toDouble()
394 ripple,r = self.gui.lpfPassBandRippleEdit.text().toDouble()
398 taps = blks2.optfir.low_pass(gain, fs, pb, sb,
404 def design_opt_bpf(self, fs, gain):
406 pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
408 pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
410 tb,r = self.gui.bpfTransitionEdit.text().toDouble()
412 atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
414 ripple,r = self.gui.bpfPassBandRippleEdit.text().toDouble()
420 taps = blks2.optfir.band_pass(gain, fs, sb1, pb1, pb2, sb2,
426 def design_opt_cbpf(self, fs, gain):
428 pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
430 pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
432 tb,r = self.gui.bpfTransitionEdit.text().toDouble()
434 atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
436 ripple,r = self.gui.bpfPassBandRippleEdit.text().toDouble()
442 taps = blks2.optfir.complex_band_pass(gain, fs, sb1, pb1, pb2, sb2,
448 def design_opt_bnf(self, fs, gain):
450 sb1,r = self.gui.startofBnfStopBandEdit.text().toDouble()
452 sb2,r = self.gui.endofBnfStopBandEdit.text().toDouble()
454 tb,r = self.gui.bnfTransitionEdit.text().toDouble()
456 atten,r = self.gui.bnfStopBandAttenEdit.text().toDouble()
458 ripple,r = self.gui.bnfPassBandRippleEdit.text().toDouble()
464 taps = blks2.optfir.band_reject(gain, fs, pb1, sb1, sb2, pb2,
470 def design_opt_hpf(self, fs, gain):
472 sb,r = self.gui.endofHpfStopBandEdit.text().toDouble()
474 pb,r = self.gui.startofHpfPassBandEdit.text().toDouble()
476 atten,r = self.gui.hpfStopBandAttenEdit.text().toDouble()
478 ripple,r = self.gui.hpfPassBandRippleEdit.text().toDouble()
482 taps = blks2.optfir.high_pass(gain, fs, sb, pb,
488 def nfft_edit_changed(self, nfft):
489 infft,r = nfft.toInt()
490 if(r and (infft != self.nfftpts)):
492 self.update_freq_curves()
494 def tab_changed(self, tab):
496 self.update_freq_curves()
498 self.update_time_curves()
500 self.update_phase_curves()
502 self.update_group_curves()
504 def get_fft(self, fs, taps, Npts):
506 fftpts = fftpack.fft(taps, Npts)
507 self.freq = scipy.arange(0, fs, 1.0/(Npts*Ts))
508 self.fftdB = 20.0*scipy.log10(abs(fftpts))
509 self.fftDeg = scipy.unwrap(scipy.angle(fftpts))
510 self.groupDelay = -scipy.diff(self.fftDeg)
512 def update_time_curves(self):
513 ntaps = len(self.taps)
515 if(type(self.taps[0]) == scipy.complex128):
516 self.rcurve.setData(scipy.arange(ntaps), self.taps.real)
517 self.icurve.setData(scipy.arange(ntaps), self.taps.imag)
519 self.rcurve.setData(scipy.arange(ntaps), self.taps)
521 # Reset the x-axis to the new time scale
522 ymax = 1.5 * max(self.taps)
523 ymin = 1.5 * min(self.taps)
524 self.gui.timePlot.setAxisScale(self.gui.timePlot.xBottom,
526 self.gui.timePlot.setAxisScale(self.gui.timePlot.yLeft,
529 # Set the zoomer base to unzoom to the new axis
530 self.timeZoomer.setZoomBase()
532 self.gui.timePlot.replot()
534 def update_freq_curves(self):
535 npts = len(self.fftdB)
537 self.freqcurve.setData(self.freq, self.fftdB)
539 # Reset the x-axis to the new time scale
540 ymax = 1.5 * max(self.fftdB[0:npts/2])
541 ymin = 1.1 * min(self.fftdB[0:npts/2])
542 xmax = self.freq[npts/2]
544 self.gui.freqPlot.setAxisScale(self.gui.freqPlot.xBottom,
546 self.gui.freqPlot.setAxisScale(self.gui.freqPlot.yLeft,
549 # Set the zoomer base to unzoom to the new axis
550 self.freqZoomer.setZoomBase()
552 self.gui.freqPlot.replot()
555 def update_phase_curves(self):
556 npts = len(self.fftDeg)
558 self.phasecurve.setData(self.freq, self.fftDeg)
560 # Reset the x-axis to the new time scale
561 ymax = 1.5 * max(self.fftDeg[0:npts/2])
562 ymin = 1.1 * min(self.fftDeg[0:npts/2])
563 xmax = self.freq[npts/2]
565 self.gui.phasePlot.setAxisScale(self.gui.phasePlot.xBottom,
567 self.gui.phasePlot.setAxisScale(self.gui.phasePlot.yLeft,
570 # Set the zoomer base to unzoom to the new axis
571 self.phaseZoomer.setZoomBase()
573 self.gui.phasePlot.replot()
575 def update_group_curves(self):
576 npts = len(self.groupDelay)
578 self.groupcurve.setData(self.freq, self.groupDelay)
580 # Reset the x-axis to the new time scale
581 ymax = 1.5 * max(self.groupDelay[0:npts/2])
582 ymin = 1.1 * min(self.groupDelay[0:npts/2])
583 xmax = self.freq[npts/2]
585 self.gui.groupPlot.setAxisScale(self.gui.groupPlot.xBottom,
587 self.gui.groupPlot.setAxisScale(self.gui.groupPlot.yLeft,
590 # Set the zoomer base to unzoom to the new axis
591 self.groupZoomer.setZoomBase()
593 self.gui.groupPlot.replot()
597 usage="%prog: [options] (input_filename)"
600 parser = OptionParser(conflict_handler="resolve",
601 usage=usage, description=description)
605 parser = setup_options()
606 (options, args) = parser.parse_args ()
608 app = Qt.QApplication(args)
609 gplt = gr_plot_filter(app, options)
612 if __name__ == '__main__':