User messages if PyQt and PyQwt are not installed (or found).
[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.gui.lpfPassBandRippleLabel.setVisible(False)
68         self.gui.lpfPassBandRippleEdit.setVisible(False)
69         self.gui.bpfPassBandRippleLabel.setVisible(False)
70         self.gui.bpfPassBandRippleEdit.setVisible(False)
71         self.gui.bnfPassBandRippleLabel.setVisible(False)
72         self.gui.bnfPassBandRippleEdit.setVisible(False)
73         self.gui.hpfPassBandRippleLabel.setVisible(False)
74         self.gui.hpfPassBandRippleEdit.setVisible(False)
75                 
76         # Initialize to LPF
77         self.gui.filterTypeWidget.setCurrentWidget(self.gui.firlpfPage)
78
79         # Set Axis labels
80         self.gui.freqPlot.setAxisTitle(self.gui.freqPlot.xBottom,
81                                        "Frequency (Hz)")
82         self.gui.freqPlot.setAxisTitle(self.gui.freqPlot.yLeft,
83                                        "Magnitude (dB)")
84         self.gui.timePlot.setAxisTitle(self.gui.timePlot.xBottom,
85                                        "Tap number")
86         self.gui.timePlot.setAxisTitle(self.gui.timePlot.yLeft,
87                                        "Amplitude")
88         self.gui.phasePlot.setAxisTitle(self.gui.phasePlot.xBottom,
89                                         "Frequency (Hz)")
90         self.gui.phasePlot.setAxisTitle(self.gui.phasePlot.yLeft,
91                                         "Phase (Radians)")
92         self.gui.groupPlot.setAxisTitle(self.gui.groupPlot.xBottom,
93                                         "Frequency (Hz)")
94         self.gui.groupPlot.setAxisTitle(self.gui.groupPlot.yLeft,
95                                         "Delay (sec)")
96
97         # Set up plot curves
98         self.rcurve = Qwt.QwtPlotCurve("Real")
99         self.rcurve.attach(self.gui.timePlot)
100         self.icurve = Qwt.QwtPlotCurve("Imag")
101         self.icurve.attach(self.gui.timePlot)
102
103         self.freqcurve = Qwt.QwtPlotCurve("PSD")
104         self.freqcurve.attach(self.gui.freqPlot)
105
106         self.phasecurve = Qwt.QwtPlotCurve("Phase")
107         self.phasecurve.attach(self.gui.phasePlot)
108
109         self.groupcurve = Qwt.QwtPlotCurve("Group Delay")
110         self.groupcurve.attach(self.gui.groupPlot)
111
112         # Create zoom functionality for the plots
113         self.timeZoomer = Qwt.QwtPlotZoomer(self.gui.timePlot.xBottom,
114                                             self.gui.timePlot.yLeft,
115                                             Qwt.QwtPicker.PointSelection,
116                                             Qwt.QwtPicker.AlwaysOn,
117                                             self.gui.timePlot.canvas())
118
119         self.freqZoomer = Qwt.QwtPlotZoomer(self.gui.freqPlot.xBottom,
120                                             self.gui.freqPlot.yLeft,
121                                             Qwt.QwtPicker.PointSelection,
122                                             Qwt.QwtPicker.AlwaysOn,
123                                             self.gui.freqPlot.canvas())
124
125         self.phaseZoomer = Qwt.QwtPlotZoomer(self.gui.phasePlot.xBottom,
126                                              self.gui.phasePlot.yLeft,
127                                              Qwt.QwtPicker.PointSelection,
128                                              Qwt.QwtPicker.AlwaysOn,
129                                              self.gui.phasePlot.canvas())
130
131         self.groupZoomer = Qwt.QwtPlotZoomer(self.gui.groupPlot.xBottom,
132                                              self.gui.groupPlot.yLeft,
133                                              Qwt.QwtPicker.PointSelection,
134                                              Qwt.QwtPicker.AlwaysOn,
135                                              self.gui.groupPlot.canvas())
136
137         # Set up pen for colors and line width
138         blue = QtGui.qRgb(0x00, 0x00, 0xFF)
139         blueBrush = Qt.QBrush(Qt.QColor(blue))
140         self.freqcurve.setPen(Qt.QPen(blueBrush, 2))
141         self.rcurve.setPen(Qt.QPen(blueBrush, 2))
142         self.phasecurve.setPen(Qt.QPen(blueBrush, 2))
143         self.groupcurve.setPen(Qt.QPen(blueBrush, 2))
144
145         # Set up validators for edit boxes
146         self.intVal = Qt.QIntValidator(None)
147         self.dblVal = Qt.QDoubleValidator(None)
148         self.gui.nfftEdit.setValidator(self.intVal)
149         self.gui.sampleRateEdit.setValidator(self.dblVal)
150         self.gui.filterGainEdit.setValidator(self.dblVal)
151         self.gui.endofLpfPassBandEdit.setValidator(self.dblVal)
152         self.gui.startofLpfStopBandEdit.setValidator(self.dblVal)
153         self.gui.lpfStopBandAttenEdit.setValidator(self.dblVal)
154         self.gui.lpfPassBandRippleEdit.setValidator(self.dblVal)
155         self.gui.startofBpfPassBandEdit.setValidator(self.dblVal)
156         self.gui.endofBpfPassBandEdit.setValidator(self.dblVal)
157         self.gui.bpfTransitionEdit.setValidator(self.dblVal)
158         self.gui.bpfStopBandAttenEdit.setValidator(self.dblVal)
159         self.gui.bpfPassBandRippleEdit.setValidator(self.dblVal)
160         self.gui.startofBnfStopBandEdit.setValidator(self.dblVal)
161         self.gui.endofBnfStopBandEdit.setValidator(self.dblVal)
162         self.gui.bnfTransitionEdit.setValidator(self.dblVal)
163         self.gui.bnfStopBandAttenEdit.setValidator(self.dblVal)
164         self.gui.bnfPassBandRippleEdit.setValidator(self.dblVal)
165         self.gui.endofHpfStopBandEdit.setValidator(self.dblVal)
166         self.gui.startofHpfPassBandEdit.setValidator(self.dblVal)
167         self.gui.hpfStopBandAttenEdit.setValidator(self.dblVal)
168         self.gui.hpfPassBandRippleEdit.setValidator(self.dblVal)
169         self.gui.rrcSymbolRateEdit.setValidator(self.dblVal)
170         self.gui.rrcAlphaEdit.setValidator(self.dblVal)
171         self.gui.rrcNumTapsEdit.setValidator(self.dblVal)
172         self.gui.gausSymbolRateEdit.setValidator(self.dblVal)
173         self.gui.gausBTEdit.setValidator(self.dblVal)
174         self.gui.gausNumTapsEdit.setValidator(self.dblVal)
175
176         self.gui.nTapsEdit.setText("0")
177
178         self.filterWindows = {"Hamming Window" : gr.firdes.WIN_HAMMING,
179                               "Hann Window" : gr.firdes.WIN_HANN,
180                               "Blackman Window" : gr.firdes.WIN_BLACKMAN,
181                               "Rectangular Window" : gr.firdes.WIN_RECTANGULAR,
182                               "Kaiser Window" : gr.firdes.WIN_KAISER,
183                               "Blackman-harris Window" : gr.firdes.WIN_BLACKMAN_hARRIS}
184
185         self.show()
186
187     def changed_filter_type(self, ftype):
188         strftype = str(ftype.toAscii())
189         if(ftype == "Low Pass"):
190             self.gui.filterTypeWidget.setCurrentWidget(self.gui.firlpfPage)
191         elif(ftype == "Band Pass"):
192             self.gui.filterTypeWidget.setCurrentWidget(self.gui.firbpfPage)
193         elif(ftype == "Complex Band Pass"):
194             self.gui.filterTypeWidget.setCurrentWidget(self.gui.firbpfPage)
195         elif(ftype == "Band Notch"):
196             self.gui.filterTypeWidget.setCurrentWidget(self.gui.firbnfPage)
197         elif(ftype == "High Pass"):
198             self.gui.filterTypeWidget.setCurrentWidget(self.gui.firhpfPage)
199         elif(ftype == "Root Raised Cosine"):
200             self.gui.filterTypeWidget.setCurrentWidget(self.gui.rrcPage)
201         elif(ftype == "Gaussian"):
202             self.gui.filterTypeWidget.setCurrentWidget(self.gui.gausPage)
203
204         self.design()
205         
206     def changed_filter_design_type(self, design):
207         if(design == "Equiripple"):
208             self.set_equiripple()
209         else:
210             self.set_windowed()
211             
212         self.design()
213
214     def set_equiripple(self):
215         self.equiripple = True
216         self.gui.lpfPassBandRippleLabel.setVisible(True)
217         self.gui.lpfPassBandRippleEdit.setVisible(True)
218         self.gui.bpfPassBandRippleLabel.setVisible(True)
219         self.gui.bpfPassBandRippleEdit.setVisible(True)
220         self.gui.bnfPassBandRippleLabel.setVisible(True)
221         self.gui.bnfPassBandRippleEdit.setVisible(True)
222         self.gui.hpfPassBandRippleLabel.setVisible(True)
223         self.gui.hpfPassBandRippleEdit.setVisible(True)
224         
225     def set_windowed(self):
226         self.equiripple = False
227         self.gui.lpfPassBandRippleLabel.setVisible(False)
228         self.gui.lpfPassBandRippleEdit.setVisible(False)
229         self.gui.bpfPassBandRippleLabel.setVisible(False)
230         self.gui.bpfPassBandRippleEdit.setVisible(False)
231         self.gui.bnfPassBandRippleLabel.setVisible(False)
232         self.gui.bnfPassBandRippleEdit.setVisible(False)
233         self.gui.hpfPassBandRippleLabel.setVisible(False)
234         self.gui.hpfPassBandRippleEdit.setVisible(False)
235         
236     def design(self):
237         ret = True
238         fs,r = self.gui.sampleRateEdit.text().toDouble()
239         ret = r and ret
240         gain,r = self.gui.filterGainEdit.text().toDouble()
241         ret = r and ret
242
243         if(ret):
244             winstr = str(self.gui.filterDesignTypeComboBox.currentText().toAscii())
245             ftype = str(self.gui.filterTypeComboBox.currentText().toAscii())
246
247             if(winstr == "Equiripple"):
248                 designer = {"Low Pass" : self.design_opt_lpf,
249                             "Band Pass" : self.design_opt_bpf,
250                             "Complex Band Pass" : self.design_opt_cbpf,
251                             "Band Notch" : self.design_opt_bnf,
252                             "High Pass" :  self.design_opt_hpf}
253                 taps,r = designer[ftype](fs, gain)
254
255             else:
256                 designer = {"Low Pass" : self.design_win_lpf,
257                             "Band Pass" : self.design_win_bpf,
258                             "Complex Band Pass" : self.design_win_cbpf,
259                             "Band Notch" : self.design_win_bnf,
260                             "High Pass" :  self.design_win_hpf,
261                             "Root Raised Cosine" :  self.design_win_rrc,
262                             "Gaussian" :  self.design_win_gaus}
263                 wintype = self.filterWindows[winstr]
264                 taps,r = designer[ftype](fs, gain, wintype)
265
266             if(r):
267                 self.taps = scipy.array(taps)
268                 self.get_fft(fs, self.taps, self.nfftpts)
269                 self.update_time_curves()
270                 self.update_freq_curves()
271                 self.update_phase_curves()
272                 self.update_group_curves()
273
274                 self.gui.nTapsEdit.setText(Qt.QString("%1").arg(self.taps.size))
275
276
277     # Filter design functions using a window
278     def design_win_lpf(self, fs, gain, wintype):
279         ret = True
280         pb,r = self.gui.endofLpfPassBandEdit.text().toDouble()
281         ret = r and ret
282         sb,r = self.gui.startofLpfStopBandEdit.text().toDouble()
283         ret = r and ret
284         atten,r = self.gui.lpfStopBandAttenEdit.text().toDouble()
285         ret = r and ret
286
287         if(ret):
288             tb = sb - pb
289             
290             taps = gr.firdes.low_pass_2(gain, fs, pb, tb,
291                                         atten, wintype)
292             return (taps, ret)
293         else:
294             return ([], ret)
295     
296     def design_win_bpf(self, fs, gain, wintype):
297         ret = True
298         pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
299         ret = r and ret
300         pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
301         ret = r and ret
302         tb,r  = self.gui.bpfTransitionEdit.text().toDouble()
303         ret = r and ret
304         atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
305         ret = r and ret
306
307         if(r):
308             taps = gr.firdes.band_pass_2(gain, fs, pb1, pb2, tb,
309                                          atten, wintype)
310             return (taps,r)
311         else:
312             return ([],r)
313
314     def design_win_cbpf(self, fs, gain, wintype):
315         ret = True
316         pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
317         ret = r and ret
318         pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
319         ret = r and ret
320         tb,r  = self.gui.bpfTransitionEdit.text().toDouble()
321         ret = r and ret
322         atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
323         ret = r and ret
324
325         if(r):
326             taps = gr.firdes.complex_band_pass_2(gain, fs, pb1, pb2, tb,
327                                                  atten, wintype)
328             return (taps,r)
329         else:
330             return ([],r)
331
332     def design_win_bnf(self, fs, gain, wintype):
333         ret = True
334         pb1,r = self.gui.startofBnfStopBandEdit.text().toDouble()
335         ret = r and ret
336         pb2,r = self.gui.endofBnfStopBandEdit.text().toDouble()
337         ret = r and ret
338         tb,r  = self.gui.bnfTransitionEdit.text().toDouble()
339         ret = r and ret
340         atten,r = self.gui.bnfStopBandAttenEdit.text().toDouble()
341         ret = r and ret
342
343         if(r):
344             taps = gr.firdes.band_reject_2(gain, fs, pb1, pb2, tb,
345                                            atten, wintype)
346             return (taps,r)
347         else:
348             return ([],r)
349
350     def design_win_hpf(self, fs, gain, wintype):
351         ret = True
352         sb,r = self.gui.endofHpfStopBandEdit.text().toDouble()
353         ret = r and ret
354         pb,r = self.gui.startofHpfPassBandEdit.text().toDouble()
355         ret = r and ret
356         atten,r = self.gui.hpfStopBandAttenEdit.text().toDouble()
357         ret = r and ret
358
359         if(r):
360             tb = pb - sb
361             taps = gr.firdes.high_pass_2(gain, fs, pb, tb,
362                                          atten, wintype)            
363             return (taps,r)
364         else:
365             return ([],r)
366
367     def design_win_rrc(self, fs, gain, wintype):
368         ret = True
369         sr,r = self.gui.rrcSymbolRateEdit.text().toDouble()
370         ret = r and ret
371         alpha,r = self.gui.rrcAlphaEdit.text().toDouble()
372         ret = r and ret
373         ntaps,r = self.gui.rrcNumTapsEdit.text().toInt()
374         ret = r and ret
375
376         if(r):
377             taps = gr.firdes.root_raised_cosine(gain, fs, sr,
378                                                 alpha, ntaps)
379             return (taps,r)
380         else:
381             return ([],r)
382
383     def design_win_gaus(self, fs, gain, wintype):
384         ret = True
385         sr,r = self.gui.gausSymbolRateEdit.text().toDouble()
386         ret = r and ret
387         bt,r = self.gui.gausBTEdit.text().toDouble()
388         ret = r and ret
389         ntaps,r = self.gui.gausNumTapsEdit.text().toInt()
390         ret = r and ret
391
392         if(r):
393             spb = fs / sr
394             taps = gr.firdes.gaussian(gain, spb, bt, ntaps)
395             return (taps,r)
396         else:
397             return ([],r)
398
399     # Design Functions for Equiripple Filters
400     def design_opt_lpf(self, fs, gain):
401         ret = True
402         pb,r = self.gui.endofLpfPassBandEdit.text().toDouble()
403         ret = r and ret
404         sb,r = self.gui.startofLpfStopBandEdit.text().toDouble()
405         ret = r and ret
406         atten,r = self.gui.lpfStopBandAttenEdit.text().toDouble()
407         ret = r and ret
408         ripple,r = self.gui.lpfPassBandRippleEdit.text().toDouble()
409         ret = r and ret
410
411         if(ret):
412             taps = blks2.optfir.low_pass(gain, fs, pb, sb,
413                                          ripple, atten)
414             return (taps, ret)
415         else:
416             return ([], ret)
417     
418     def design_opt_bpf(self, fs, gain):
419         ret = True
420         pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
421         ret = r and ret
422         pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
423         ret = r and ret
424         tb,r  = self.gui.bpfTransitionEdit.text().toDouble()
425         ret = r and ret
426         atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
427         ret = r and ret
428         ripple,r = self.gui.bpfPassBandRippleEdit.text().toDouble()
429         ret = r and ret
430
431         if(r):
432             sb1 = pb1 - tb
433             sb2 = pb2 + tb
434             taps = blks2.optfir.band_pass(gain, fs, sb1, pb1, pb2, sb2,
435                                           ripple, atten)
436             return (taps,r)
437         else:
438             return ([],r)
439
440     def design_opt_cbpf(self, fs, gain):
441         ret = True
442         pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
443         ret = r and ret
444         pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
445         ret = r and ret
446         tb,r  = self.gui.bpfTransitionEdit.text().toDouble()
447         ret = r and ret
448         atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
449         ret = r and ret
450         ripple,r = self.gui.bpfPassBandRippleEdit.text().toDouble()
451         ret = r and ret
452
453         if(r):
454             sb1 = pb1 - tb
455             sb2 = pb2 + tb
456             taps = blks2.optfir.complex_band_pass(gain, fs, sb1, pb1, pb2, sb2,
457                                                   ripple, atten)
458             return (taps,r)
459         else:
460             return ([],r)
461
462     def design_opt_bnf(self, fs, gain):
463         ret = True
464         sb1,r = self.gui.startofBnfStopBandEdit.text().toDouble()
465         ret = r and ret
466         sb2,r = self.gui.endofBnfStopBandEdit.text().toDouble()
467         ret = r and ret
468         tb,r  = self.gui.bnfTransitionEdit.text().toDouble()
469         ret = r and ret
470         atten,r = self.gui.bnfStopBandAttenEdit.text().toDouble()
471         ret = r and ret
472         ripple,r = self.gui.bnfPassBandRippleEdit.text().toDouble()
473         ret = r and ret
474
475         if(r):
476             pb1 = sb1 - tb
477             pb2 = sb2 + tb
478             taps = blks2.optfir.band_reject(gain, fs, pb1, sb1, sb2, pb2,
479                                             ripple, atten)
480             return (taps,r)
481         else:
482             return ([],r)
483
484     def design_opt_hpf(self, fs, gain):
485         ret = True
486         sb,r = self.gui.endofHpfStopBandEdit.text().toDouble()
487         ret = r and ret
488         pb,r = self.gui.startofHpfPassBandEdit.text().toDouble()
489         ret = r and ret
490         atten,r = self.gui.hpfStopBandAttenEdit.text().toDouble()
491         ret = r and ret
492         ripple,r = self.gui.hpfPassBandRippleEdit.text().toDouble()
493         ret = r and ret
494
495         if(r):
496             taps = blks2.optfir.high_pass(gain, fs, sb, pb,
497                                           atten, ripple)
498             return (taps,r)
499         else:
500             return ([],r)
501
502     def nfft_edit_changed(self, nfft):
503         infft,r = nfft.toInt()
504         if(r and (infft != self.nfftpts)):
505             self.nfftpts = infft
506             self.update_freq_curves()
507
508     def tab_changed(self, tab):
509         if(tab == 0):
510             self.update_freq_curves()
511         if(tab == 1):
512             self.update_time_curves()
513         if(tab == 2):
514             self.update_phase_curves()
515         if(tab == 3):
516             self.update_group_curves()
517         
518     def get_fft(self, fs, taps, Npts):
519         Ts = 1.0/fs
520         fftpts = fftpack.fft(taps, Npts)
521         self.freq = scipy.arange(0, fs, 1.0/(Npts*Ts))        
522         self.fftdB = 20.0*scipy.log10(abs(fftpts))
523         self.fftDeg = scipy.unwrap(scipy.angle(fftpts))
524         self.groupDelay = -scipy.diff(self.fftDeg)
525         
526     def update_time_curves(self):
527         ntaps = len(self.taps)
528         if(ntaps > 0):
529             if(type(self.taps[0]) == scipy.complex128):
530                 self.rcurve.setData(scipy.arange(ntaps), self.taps.real)
531                 self.icurve.setData(scipy.arange(ntaps), self.taps.imag)
532             else:
533                 self.rcurve.setData(scipy.arange(ntaps), self.taps)
534
535             # Reset the x-axis to the new time scale
536             ymax = 1.5 * max(self.taps)
537             ymin = 1.5 * min(self.taps)
538             self.gui.timePlot.setAxisScale(self.gui.timePlot.xBottom,
539                                            0, ntaps)
540             self.gui.timePlot.setAxisScale(self.gui.timePlot.yLeft,
541                                            ymin, ymax)
542             
543             # Set the zoomer base to unzoom to the new axis
544             self.timeZoomer.setZoomBase()
545             
546             self.gui.timePlot.replot()
547         
548     def update_freq_curves(self):
549         npts = len(self.fftdB)
550         if(npts > 0):
551             self.freqcurve.setData(self.freq, self.fftdB)
552             
553             # Reset the x-axis to the new time scale
554             ymax = 1.5 * max(self.fftdB[0:npts/2])
555             ymin = 1.1 * min(self.fftdB[0:npts/2])
556             xmax = self.freq[npts/2]
557             xmin = self.freq[0]
558             self.gui.freqPlot.setAxisScale(self.gui.freqPlot.xBottom,
559                                            xmin, xmax)
560             self.gui.freqPlot.setAxisScale(self.gui.freqPlot.yLeft,
561                                            ymin, ymax)
562             
563             # Set the zoomer base to unzoom to the new axis
564             self.freqZoomer.setZoomBase()
565             
566             self.gui.freqPlot.replot()
567
568
569     def update_phase_curves(self):
570         npts = len(self.fftDeg)
571         if(npts > 0):
572             self.phasecurve.setData(self.freq, self.fftDeg)
573             
574             # Reset the x-axis to the new time scale
575             ymax = 1.5 * max(self.fftDeg[0:npts/2])
576             ymin = 1.1 * min(self.fftDeg[0:npts/2])
577             xmax = self.freq[npts/2]
578             xmin = self.freq[0]
579             self.gui.phasePlot.setAxisScale(self.gui.phasePlot.xBottom,
580                                             xmin, xmax)
581             self.gui.phasePlot.setAxisScale(self.gui.phasePlot.yLeft,
582                                             ymin, ymax)
583             
584             # Set the zoomer base to unzoom to the new axis
585             self.phaseZoomer.setZoomBase()
586             
587             self.gui.phasePlot.replot()
588
589     def update_group_curves(self):
590         npts = len(self.groupDelay)
591         if(npts > 0):
592             self.groupcurve.setData(self.freq, self.groupDelay)
593             
594             # Reset the x-axis to the new time scale
595             ymax = 1.5 * max(self.groupDelay[0:npts/2])
596             ymin = 1.1 * min(self.groupDelay[0:npts/2])
597             xmax = self.freq[npts/2]
598             xmin = self.freq[0]
599             self.gui.groupPlot.setAxisScale(self.gui.groupPlot.xBottom,
600                                             xmin, xmax)
601             self.gui.groupPlot.setAxisScale(self.gui.groupPlot.yLeft,
602                                             ymin, ymax)
603             
604             # Set the zoomer base to unzoom to the new axis
605             self.groupZoomer.setZoomBase()
606             
607             self.gui.groupPlot.replot()
608
609
610 def setup_options():
611     usage="%prog: [options] (input_filename)"
612     description = ""
613
614     parser = OptionParser(conflict_handler="resolve",
615                           usage=usage, description=description)
616     return parser
617
618 def main(args):
619     parser = setup_options()
620     (options, args) = parser.parse_args ()
621
622     app = Qt.QApplication(args)
623     gplt = gr_plot_filter(app, options)
624     app.exec_()
625
626 if __name__ == '__main__':
627     main(sys.argv)
628