Fixed slider behavior when the end of the file is reached.
[debian/gnuradio] / gr-utils / src / python / gr_filter_design.py
1 #!/usr/bin/env python
2
3 import sys, os
4 from optparse import OptionParser
5 from gnuradio import gr, blks2, eng_notation
6
7 try:
8     import scipy
9     from scipy import fftpack
10 except ImportError:
11     print "Please install SciPy to run this script (http://www.scipy.org/)"
12     raise SystemExit, 1
13
14 try:
15     from PyQt4 import Qt, QtCore, QtGui
16 except ImportError:
17     print "Please install PyQt4 to run this script (http://www.riverbankcomputing.co.uk/software/pyqt/download)"
18     raise SystemExit, 1
19
20 try:
21     import PyQt4.Qwt5 as Qwt
22 except ImportError:
23     print "Please install PyQwt5 to run this script (http://pyqwt.sourceforge.net/)"
24     raise SystemExit, 1
25
26 try:
27     from pyqt_filter import Ui_MainWindow
28 except ImportError:
29     print "Could not import from pyqt_filter. Please build with \"pyuic4 pyqt_filter.ui -o pyqt_filter.py\""
30     raise SystemExit, 1
31
32
33 class gr_plot_filter(QtGui.QMainWindow):
34     def __init__(self, qapp, options):
35         QtGui.QWidget.__init__(self, None)
36         self.gui = Ui_MainWindow()
37         self.gui.setupUi(self)
38
39         self.connect(self.gui.filterTypeComboBox,
40                      Qt.SIGNAL("currentIndexChanged(const QString&)"),
41                      self.changed_filter_type)
42         self.connect(self.gui.filterDesignTypeComboBox,
43                      Qt.SIGNAL("currentIndexChanged(const QString&)"),
44                      self.changed_filter_design_type)
45
46         self.connect(self.gui.designButton,
47                      Qt.SIGNAL("released()"),
48                      self.design)
49
50         self.connect(self.gui.tabGroup,
51                      Qt.SIGNAL("currentChanged(int)"),
52                      self.tab_changed)
53
54         self.connect(self.gui.nfftEdit,
55                      Qt.SIGNAL("textEdited(QString)"),
56                      self.nfft_edit_changed)
57
58         self.gui.designButton.setShortcut(QtCore.Qt.Key_Return)
59
60         self.taps = []
61         self.fftdB = []
62         self.fftDeg = []
63         self.groupDelay = []
64         self.nfftpts = int(10000)
65         self.gui.nfftEdit.setText(Qt.QString("%1").arg(self.nfftpts))
66
67         self.firFilters = ("Low Pass", "Band Pass", "Complex Band Pass", "Band Notch",
68                            "High Pass", "Root Raised Cosine", "Gaussian")
69         self.optFilters = ("Low Pass", "Band Pass", "Complex Band Pass",
70                            "Band Notch", "High Pass")
71         
72         self.set_windowed()
73                 
74         # Initialize to LPF
75         self.gui.filterTypeWidget.setCurrentWidget(self.gui.firlpfPage)
76
77         # Set Axis labels
78         self.gui.freqPlot.setAxisTitle(self.gui.freqPlot.xBottom,
79                                        "Frequency (Hz)")
80         self.gui.freqPlot.setAxisTitle(self.gui.freqPlot.yLeft,
81                                        "Magnitude (dB)")
82         self.gui.timePlot.setAxisTitle(self.gui.timePlot.xBottom,
83                                        "Tap number")
84         self.gui.timePlot.setAxisTitle(self.gui.timePlot.yLeft,
85                                        "Amplitude")
86         self.gui.phasePlot.setAxisTitle(self.gui.phasePlot.xBottom,
87                                         "Frequency (Hz)")
88         self.gui.phasePlot.setAxisTitle(self.gui.phasePlot.yLeft,
89                                         "Phase (Radians)")
90         self.gui.groupPlot.setAxisTitle(self.gui.groupPlot.xBottom,
91                                         "Frequency (Hz)")
92         self.gui.groupPlot.setAxisTitle(self.gui.groupPlot.yLeft,
93                                         "Delay (sec)")
94
95         # Set up plot curves
96         self.rcurve = Qwt.QwtPlotCurve("Real")
97         self.rcurve.attach(self.gui.timePlot)
98         self.icurve = Qwt.QwtPlotCurve("Imag")
99         self.icurve.attach(self.gui.timePlot)
100
101         self.freqcurve = Qwt.QwtPlotCurve("PSD")
102         self.freqcurve.attach(self.gui.freqPlot)
103
104         self.phasecurve = Qwt.QwtPlotCurve("Phase")
105         self.phasecurve.attach(self.gui.phasePlot)
106
107         self.groupcurve = Qwt.QwtPlotCurve("Group Delay")
108         self.groupcurve.attach(self.gui.groupPlot)
109
110         # Create zoom functionality for the plots
111         self.timeZoomer = Qwt.QwtPlotZoomer(self.gui.timePlot.xBottom,
112                                             self.gui.timePlot.yLeft,
113                                             Qwt.QwtPicker.PointSelection,
114                                             Qwt.QwtPicker.AlwaysOn,
115                                             self.gui.timePlot.canvas())
116
117         self.freqZoomer = Qwt.QwtPlotZoomer(self.gui.freqPlot.xBottom,
118                                             self.gui.freqPlot.yLeft,
119                                             Qwt.QwtPicker.PointSelection,
120                                             Qwt.QwtPicker.AlwaysOn,
121                                             self.gui.freqPlot.canvas())
122
123         self.phaseZoomer = Qwt.QwtPlotZoomer(self.gui.phasePlot.xBottom,
124                                              self.gui.phasePlot.yLeft,
125                                              Qwt.QwtPicker.PointSelection,
126                                              Qwt.QwtPicker.AlwaysOn,
127                                              self.gui.phasePlot.canvas())
128
129         self.groupZoomer = Qwt.QwtPlotZoomer(self.gui.groupPlot.xBottom,
130                                              self.gui.groupPlot.yLeft,
131                                              Qwt.QwtPicker.PointSelection,
132                                              Qwt.QwtPicker.AlwaysOn,
133                                              self.gui.groupPlot.canvas())
134
135         # Set up pen for colors and line width
136         blue = QtGui.qRgb(0x00, 0x00, 0xFF)
137         blueBrush = Qt.QBrush(Qt.QColor(blue))
138         red = QtGui.qRgb(0xFF, 0x00, 0x00)
139         redBrush = Qt.QBrush(Qt.QColor(red))
140         self.freqcurve.setPen(Qt.QPen(blueBrush, 2))
141         self.rcurve.setPen(Qt.QPen(blueBrush, 2))
142         self.icurve.setPen(Qt.QPen(redBrush, 2))
143         self.phasecurve.setPen(Qt.QPen(blueBrush, 2))
144         self.groupcurve.setPen(Qt.QPen(blueBrush, 2))
145
146         # Set up validators for edit boxes
147         self.intVal = Qt.QIntValidator(None)
148         self.dblVal = Qt.QDoubleValidator(None)
149         self.gui.nfftEdit.setValidator(self.intVal)
150         self.gui.sampleRateEdit.setValidator(self.dblVal)
151         self.gui.filterGainEdit.setValidator(self.dblVal)
152         self.gui.endofLpfPassBandEdit.setValidator(self.dblVal)
153         self.gui.startofLpfStopBandEdit.setValidator(self.dblVal)
154         self.gui.lpfStopBandAttenEdit.setValidator(self.dblVal)
155         self.gui.lpfPassBandRippleEdit.setValidator(self.dblVal)
156         self.gui.startofBpfPassBandEdit.setValidator(self.dblVal)
157         self.gui.endofBpfPassBandEdit.setValidator(self.dblVal)
158         self.gui.bpfTransitionEdit.setValidator(self.dblVal)
159         self.gui.bpfStopBandAttenEdit.setValidator(self.dblVal)
160         self.gui.bpfPassBandRippleEdit.setValidator(self.dblVal)
161         self.gui.startofBnfStopBandEdit.setValidator(self.dblVal)
162         self.gui.endofBnfStopBandEdit.setValidator(self.dblVal)
163         self.gui.bnfTransitionEdit.setValidator(self.dblVal)
164         self.gui.bnfStopBandAttenEdit.setValidator(self.dblVal)
165         self.gui.bnfPassBandRippleEdit.setValidator(self.dblVal)
166         self.gui.endofHpfStopBandEdit.setValidator(self.dblVal)
167         self.gui.startofHpfPassBandEdit.setValidator(self.dblVal)
168         self.gui.hpfStopBandAttenEdit.setValidator(self.dblVal)
169         self.gui.hpfPassBandRippleEdit.setValidator(self.dblVal)
170         self.gui.rrcSymbolRateEdit.setValidator(self.dblVal)
171         self.gui.rrcAlphaEdit.setValidator(self.dblVal)
172         self.gui.rrcNumTapsEdit.setValidator(self.dblVal)
173         self.gui.gausSymbolRateEdit.setValidator(self.dblVal)
174         self.gui.gausBTEdit.setValidator(self.dblVal)
175         self.gui.gausNumTapsEdit.setValidator(self.dblVal)
176
177         self.gui.nTapsEdit.setText("0")
178
179         self.filterWindows = {"Hamming Window" : gr.firdes.WIN_HAMMING,
180                               "Hann Window" : gr.firdes.WIN_HANN,
181                               "Blackman Window" : gr.firdes.WIN_BLACKMAN,
182                               "Rectangular Window" : gr.firdes.WIN_RECTANGULAR,
183                               "Kaiser Window" : gr.firdes.WIN_KAISER,
184                               "Blackman-harris Window" : gr.firdes.WIN_BLACKMAN_hARRIS}
185
186         self.show()
187
188     def changed_filter_type(self, ftype):
189         strftype = str(ftype.toAscii())
190         if(ftype == "Low Pass"):
191             self.gui.filterTypeWidget.setCurrentWidget(self.gui.firlpfPage)
192         elif(ftype == "Band Pass"):
193             self.gui.filterTypeWidget.setCurrentWidget(self.gui.firbpfPage)
194         elif(ftype == "Complex Band Pass"):
195             self.gui.filterTypeWidget.setCurrentWidget(self.gui.firbpfPage)
196         elif(ftype == "Band Notch"):
197             self.gui.filterTypeWidget.setCurrentWidget(self.gui.firbnfPage)
198         elif(ftype == "High Pass"):
199             self.gui.filterTypeWidget.setCurrentWidget(self.gui.firhpfPage)
200         elif(ftype == "Root Raised Cosine"):
201             self.gui.filterTypeWidget.setCurrentWidget(self.gui.rrcPage)
202         elif(ftype == "Gaussian"):
203             self.gui.filterTypeWidget.setCurrentWidget(self.gui.gausPage)
204
205         self.design()
206         
207     def changed_filter_design_type(self, design):
208         if(design == "Equiripple"):
209             self.set_equiripple()
210         else:
211             self.set_windowed()
212             
213         self.design()
214
215     def set_equiripple(self):
216         # Stop sending the signal for this function
217         self.gui.filterTypeComboBox.blockSignals(True)
218         
219         self.equiripple = True
220         self.gui.lpfPassBandRippleLabel.setVisible(True)
221         self.gui.lpfPassBandRippleEdit.setVisible(True)
222         self.gui.bpfPassBandRippleLabel.setVisible(True)
223         self.gui.bpfPassBandRippleEdit.setVisible(True)
224         self.gui.bnfPassBandRippleLabel.setVisible(True)
225         self.gui.bnfPassBandRippleEdit.setVisible(True)
226         self.gui.hpfPassBandRippleLabel.setVisible(True)
227         self.gui.hpfPassBandRippleEdit.setVisible(True)
228
229         # Save current type and repopulate the combo box for
230         # filters this window type can handle
231         currenttype = self.gui.filterTypeComboBox.currentText()
232         items = self.gui.filterTypeComboBox.count()
233         for i in xrange(items):
234             self.gui.filterTypeComboBox.removeItem(0)
235         self.gui.filterTypeComboBox.addItems(self.optFilters)
236
237         # If the last filter type was valid for this window type,
238         # go back to it; otherwise, reset
239         try:
240             index = self.optFilters.index(currenttype)
241             self.gui.filterTypeComboBox.setCurrentIndex(index)
242         except ValueError:
243             pass
244
245         # Tell gui its ok to start sending this signal again
246         self.gui.filterTypeComboBox.blockSignals(False)
247         
248     def set_windowed(self):
249         # Stop sending the signal for this function
250         self.gui.filterTypeComboBox.blockSignals(True)
251         
252         self.equiripple = False
253         self.gui.lpfPassBandRippleLabel.setVisible(False)
254         self.gui.lpfPassBandRippleEdit.setVisible(False)
255         self.gui.bpfPassBandRippleLabel.setVisible(False)
256         self.gui.bpfPassBandRippleEdit.setVisible(False)
257         self.gui.bnfPassBandRippleLabel.setVisible(False)
258         self.gui.bnfPassBandRippleEdit.setVisible(False)
259         self.gui.hpfPassBandRippleLabel.setVisible(False)
260         self.gui.hpfPassBandRippleEdit.setVisible(False)
261
262         # Save current type and repopulate the combo box for
263         # filters this window type can handle
264         currenttype = self.gui.filterTypeComboBox.currentText()
265         items = self.gui.filterTypeComboBox.count()
266         for i in xrange(items):
267             self.gui.filterTypeComboBox.removeItem(0)
268         self.gui.filterTypeComboBox.addItems(self.firFilters)
269
270         # If the last filter type was valid for this window type,
271         # go back to it; otherwise, reset
272         try:
273             index = self.optFilters.index(currenttype)
274             self.gui.filterTypeComboBox.setCurrentIndex(index)
275         except ValueError:
276             pass
277
278         # Tell gui its ok to start sending this signal again
279         self.gui.filterTypeComboBox.blockSignals(False)
280
281     def design(self):
282         ret = True
283         fs,r = self.gui.sampleRateEdit.text().toDouble()
284         ret = r and ret
285         gain,r = self.gui.filterGainEdit.text().toDouble()
286         ret = r and ret
287
288         if(ret):
289             winstr = str(self.gui.filterDesignTypeComboBox.currentText().toAscii())
290             ftype = str(self.gui.filterTypeComboBox.currentText().toAscii())
291
292             if(winstr == "Equiripple"):
293                 designer = {"Low Pass" : self.design_opt_lpf,
294                             "Band Pass" : self.design_opt_bpf,
295                             "Complex Band Pass" : self.design_opt_cbpf,
296                             "Band Notch" : self.design_opt_bnf,
297                             "High Pass" :  self.design_opt_hpf}
298                 taps,r = designer[ftype](fs, gain)
299
300             else:
301                 designer = {"Low Pass" : self.design_win_lpf,
302                             "Band Pass" : self.design_win_bpf,
303                             "Complex Band Pass" : self.design_win_cbpf,
304                             "Band Notch" : self.design_win_bnf,
305                             "High Pass" :  self.design_win_hpf,
306                             "Root Raised Cosine" :  self.design_win_rrc,
307                             "Gaussian" :  self.design_win_gaus}
308                 wintype = self.filterWindows[winstr]
309                 taps,r = designer[ftype](fs, gain, wintype)
310
311             if(r):
312                 self.taps = scipy.array(taps)
313                 self.get_fft(fs, self.taps, self.nfftpts)
314                 self.update_time_curves()
315                 self.update_freq_curves()
316                 self.update_phase_curves()
317                 self.update_group_curves()
318
319                 self.gui.nTapsEdit.setText(Qt.QString("%1").arg(self.taps.size))
320
321
322     # Filter design functions using a window
323     def design_win_lpf(self, fs, gain, wintype):
324         ret = True
325         pb,r = self.gui.endofLpfPassBandEdit.text().toDouble()
326         ret = r and ret
327         sb,r = self.gui.startofLpfStopBandEdit.text().toDouble()
328         ret = r and ret
329         atten,r = self.gui.lpfStopBandAttenEdit.text().toDouble()
330         ret = r and ret
331
332         if(ret):
333             tb = sb - pb
334             
335             taps = gr.firdes.low_pass_2(gain, fs, pb, tb,
336                                         atten, wintype)
337             return (taps, ret)
338         else:
339             return ([], ret)
340     
341     def design_win_bpf(self, fs, gain, wintype):
342         ret = True
343         pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
344         ret = r and ret
345         pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
346         ret = r and ret
347         tb,r  = self.gui.bpfTransitionEdit.text().toDouble()
348         ret = r and ret
349         atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
350         ret = r and ret
351
352         if(r):
353             taps = gr.firdes.band_pass_2(gain, fs, pb1, pb2, tb,
354                                          atten, wintype)
355             return (taps,r)
356         else:
357             return ([],r)
358
359     def design_win_cbpf(self, fs, gain, wintype):
360         ret = True
361         pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
362         ret = r and ret
363         pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
364         ret = r and ret
365         tb,r  = self.gui.bpfTransitionEdit.text().toDouble()
366         ret = r and ret
367         atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
368         ret = r and ret
369
370         if(r):
371             taps = gr.firdes.complex_band_pass_2(gain, fs, pb1, pb2, tb,
372                                                  atten, wintype)
373             return (taps,r)
374         else:
375             return ([],r)
376
377     def design_win_bnf(self, fs, gain, wintype):
378         ret = True
379         pb1,r = self.gui.startofBnfStopBandEdit.text().toDouble()
380         ret = r and ret
381         pb2,r = self.gui.endofBnfStopBandEdit.text().toDouble()
382         ret = r and ret
383         tb,r  = self.gui.bnfTransitionEdit.text().toDouble()
384         ret = r and ret
385         atten,r = self.gui.bnfStopBandAttenEdit.text().toDouble()
386         ret = r and ret
387
388         if(r):
389             taps = gr.firdes.band_reject_2(gain, fs, pb1, pb2, tb,
390                                            atten, wintype)
391             return (taps,r)
392         else:
393             return ([],r)
394
395     def design_win_hpf(self, fs, gain, wintype):
396         ret = True
397         sb,r = self.gui.endofHpfStopBandEdit.text().toDouble()
398         ret = r and ret
399         pb,r = self.gui.startofHpfPassBandEdit.text().toDouble()
400         ret = r and ret
401         atten,r = self.gui.hpfStopBandAttenEdit.text().toDouble()
402         ret = r and ret
403
404         if(r):
405             tb = pb - sb
406             taps = gr.firdes.high_pass_2(gain, fs, pb, tb,
407                                          atten, wintype)            
408             return (taps,r)
409         else:
410             return ([],r)
411
412     def design_win_rrc(self, fs, gain, wintype):
413         ret = True
414         sr,r = self.gui.rrcSymbolRateEdit.text().toDouble()
415         ret = r and ret
416         alpha,r = self.gui.rrcAlphaEdit.text().toDouble()
417         ret = r and ret
418         ntaps,r = self.gui.rrcNumTapsEdit.text().toInt()
419         ret = r and ret
420
421         if(r):
422             taps = gr.firdes.root_raised_cosine(gain, fs, sr,
423                                                 alpha, ntaps)
424             return (taps,r)
425         else:
426             return ([],r)
427
428     def design_win_gaus(self, fs, gain, wintype):
429         ret = True
430         sr,r = self.gui.gausSymbolRateEdit.text().toDouble()
431         ret = r and ret
432         bt,r = self.gui.gausBTEdit.text().toDouble()
433         ret = r and ret
434         ntaps,r = self.gui.gausNumTapsEdit.text().toInt()
435         ret = r and ret
436
437         if(r):
438             spb = fs / sr
439             taps = gr.firdes.gaussian(gain, spb, bt, ntaps)
440             return (taps,r)
441         else:
442             return ([],r)
443
444     # Design Functions for Equiripple Filters
445     def design_opt_lpf(self, fs, gain):
446         ret = True
447         pb,r = self.gui.endofLpfPassBandEdit.text().toDouble()
448         ret = r and ret
449         sb,r = self.gui.startofLpfStopBandEdit.text().toDouble()
450         ret = r and ret
451         atten,r = self.gui.lpfStopBandAttenEdit.text().toDouble()
452         ret = r and ret
453         ripple,r = self.gui.lpfPassBandRippleEdit.text().toDouble()
454         ret = r and ret
455
456         if(ret):
457             taps = blks2.optfir.low_pass(gain, fs, pb, sb,
458                                          ripple, atten)
459             return (taps, ret)
460         else:
461             return ([], ret)
462     
463     def design_opt_bpf(self, fs, gain):
464         ret = True
465         pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
466         ret = r and ret
467         pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
468         ret = r and ret
469         tb,r  = self.gui.bpfTransitionEdit.text().toDouble()
470         ret = r and ret
471         atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
472         ret = r and ret
473         ripple,r = self.gui.bpfPassBandRippleEdit.text().toDouble()
474         ret = r and ret
475
476         if(r):
477             sb1 = pb1 - tb
478             sb2 = pb2 + tb
479             taps = blks2.optfir.band_pass(gain, fs, sb1, pb1, pb2, sb2,
480                                           ripple, atten)
481             return (taps,r)
482         else:
483             return ([],r)
484
485     def design_opt_cbpf(self, fs, gain):
486         ret = True
487         pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
488         ret = r and ret
489         pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
490         ret = r and ret
491         tb,r  = self.gui.bpfTransitionEdit.text().toDouble()
492         ret = r and ret
493         atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
494         ret = r and ret
495         ripple,r = self.gui.bpfPassBandRippleEdit.text().toDouble()
496         ret = r and ret
497
498         if(r):
499             sb1 = pb1 - tb
500             sb2 = pb2 + tb
501             taps = blks2.optfir.complex_band_pass(gain, fs, sb1, pb1, pb2, sb2,
502                                                   ripple, atten)
503             return (taps,r)
504         else:
505             return ([],r)
506
507     def design_opt_bnf(self, fs, gain):
508         ret = True
509         sb1,r = self.gui.startofBnfStopBandEdit.text().toDouble()
510         ret = r and ret
511         sb2,r = self.gui.endofBnfStopBandEdit.text().toDouble()
512         ret = r and ret
513         tb,r  = self.gui.bnfTransitionEdit.text().toDouble()
514         ret = r and ret
515         atten,r = self.gui.bnfStopBandAttenEdit.text().toDouble()
516         ret = r and ret
517         ripple,r = self.gui.bnfPassBandRippleEdit.text().toDouble()
518         ret = r and ret
519
520         if(r):
521             pb1 = sb1 - tb
522             pb2 = sb2 + tb
523             taps = blks2.optfir.band_reject(gain, fs, pb1, sb1, sb2, pb2,
524                                             ripple, atten)
525             return (taps,r)
526         else:
527             return ([],r)
528
529     def design_opt_hpf(self, fs, gain):
530         ret = True
531         sb,r = self.gui.endofHpfStopBandEdit.text().toDouble()
532         ret = r and ret
533         pb,r = self.gui.startofHpfPassBandEdit.text().toDouble()
534         ret = r and ret
535         atten,r = self.gui.hpfStopBandAttenEdit.text().toDouble()
536         ret = r and ret
537         ripple,r = self.gui.hpfPassBandRippleEdit.text().toDouble()
538         ret = r and ret
539
540         if(r):
541             taps = blks2.optfir.high_pass(gain, fs, sb, pb,
542                                           atten, ripple)
543             return (taps,r)
544         else:
545             return ([],r)
546
547     def nfft_edit_changed(self, nfft):
548         infft,r = nfft.toInt()
549         if(r and (infft != self.nfftpts)):
550             self.nfftpts = infft
551             self.update_freq_curves()
552
553     def tab_changed(self, tab):
554         if(tab == 0):
555             self.update_freq_curves()
556         if(tab == 1):
557             self.update_time_curves()
558         if(tab == 2):
559             self.update_phase_curves()
560         if(tab == 3):
561             self.update_group_curves()
562         
563     def get_fft(self, fs, taps, Npts):
564         Ts = 1.0/fs
565         fftpts = fftpack.fft(taps, Npts)
566         self.freq = scipy.arange(0, fs, 1.0/(Npts*Ts))        
567         self.fftdB = 20.0*scipy.log10(abs(fftpts))
568         self.fftDeg = scipy.unwrap(scipy.angle(fftpts))
569         self.groupDelay = -scipy.diff(self.fftDeg)
570         
571     def update_time_curves(self):
572         ntaps = len(self.taps)
573         if(ntaps > 0):
574             if(type(self.taps[0]) == scipy.complex128):
575                 self.rcurve.setData(scipy.arange(ntaps), self.taps.real)
576                 self.icurve.setData(scipy.arange(ntaps), self.taps.imag)
577             else:
578                 self.rcurve.setData(scipy.arange(ntaps), self.taps)
579
580             # Reset the x-axis to the new time scale
581             ymax = 1.5 * max(self.taps)
582             ymin = 1.5 * min(self.taps)
583             self.gui.timePlot.setAxisScale(self.gui.timePlot.xBottom,
584                                            0, ntaps)
585             self.gui.timePlot.setAxisScale(self.gui.timePlot.yLeft,
586                                            ymin, ymax)
587             
588             # Set the zoomer base to unzoom to the new axis
589             self.timeZoomer.setZoomBase()
590             
591             self.gui.timePlot.replot()
592         
593     def update_freq_curves(self):
594         npts = len(self.fftdB)
595         if(npts > 0):
596             self.freqcurve.setData(self.freq, self.fftdB)
597             
598             # Reset the x-axis to the new time scale
599             ymax = 1.5 * max(self.fftdB[0:npts/2])
600             ymin = 1.1 * min(self.fftdB[0:npts/2])
601             xmax = self.freq[npts/2]
602             xmin = self.freq[0]
603             self.gui.freqPlot.setAxisScale(self.gui.freqPlot.xBottom,
604                                            xmin, xmax)
605             self.gui.freqPlot.setAxisScale(self.gui.freqPlot.yLeft,
606                                            ymin, ymax)
607             
608             # Set the zoomer base to unzoom to the new axis
609             self.freqZoomer.setZoomBase()
610             
611             self.gui.freqPlot.replot()
612
613
614     def update_phase_curves(self):
615         npts = len(self.fftDeg)
616         if(npts > 0):
617             self.phasecurve.setData(self.freq, self.fftDeg)
618             
619             # Reset the x-axis to the new time scale
620             ymax = 1.5 * max(self.fftDeg[0:npts/2])
621             ymin = 1.1 * min(self.fftDeg[0:npts/2])
622             xmax = self.freq[npts/2]
623             xmin = self.freq[0]
624             self.gui.phasePlot.setAxisScale(self.gui.phasePlot.xBottom,
625                                             xmin, xmax)
626             self.gui.phasePlot.setAxisScale(self.gui.phasePlot.yLeft,
627                                             ymin, ymax)
628             
629             # Set the zoomer base to unzoom to the new axis
630             self.phaseZoomer.setZoomBase()
631             
632             self.gui.phasePlot.replot()
633
634     def update_group_curves(self):
635         npts = len(self.groupDelay)
636         if(npts > 0):
637             self.groupcurve.setData(self.freq, self.groupDelay)
638             
639             # Reset the x-axis to the new time scale
640             ymax = 1.5 * max(self.groupDelay[0:npts/2])
641             ymin = 1.1 * min(self.groupDelay[0:npts/2])
642             xmax = self.freq[npts/2]
643             xmin = self.freq[0]
644             self.gui.groupPlot.setAxisScale(self.gui.groupPlot.xBottom,
645                                             xmin, xmax)
646             self.gui.groupPlot.setAxisScale(self.gui.groupPlot.yLeft,
647                                             ymin, ymax)
648             
649             # Set the zoomer base to unzoom to the new axis
650             self.groupZoomer.setZoomBase()
651             
652             self.gui.groupPlot.replot()
653
654
655 def setup_options():
656     usage="%prog: [options] (input_filename)"
657     description = ""
658
659     parser = OptionParser(conflict_handler="resolve",
660                           usage=usage, description=description)
661     return parser
662
663 def main(args):
664     parser = setup_options()
665     (options, args) = parser.parse_args ()
666
667     app = Qt.QApplication(args)
668     gplt = gr_plot_filter(app, options)
669     app.exec_()
670
671 if __name__ == '__main__':
672     main(sys.argv)
673