4fbe6e34c89e27ebd381c0d9f4278a7bf35a716a
[debian/gnuradio] / gr-utils / src / python / gr_filter_design.py
1 #!/usr/bin/env python
2
3 try:
4     import scipy
5     from scipy import fftpack
6 except ImportError:
7     print "Please install SciPy to run this script (http://www.scipy.org/)"
8     raise SystemExit, 1
9
10 import sys, os
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
16
17 from pyqt_filter import Ui_MainWindow
18
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)
24
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)
31
32         self.connect(self.gui.designButton,
33                      Qt.SIGNAL("released()"),
34                      self.design)
35
36         self.connect(self.gui.tabGroup,
37                      Qt.SIGNAL("currentChanged(int)"),
38                      self.tab_changed)
39
40         self.connect(self.gui.nfftEdit,
41                      Qt.SIGNAL("textEdited(QString)"),
42                      self.nfft_edit_changed)
43
44         self.gui.designButton.setShortcut(QtCore.Qt.Key_Return)
45
46         self.taps = []
47         self.fftdB = []
48         self.fftDeg = []
49         self.groupDelay = []
50         self.nfftpts = int(10000)
51         self.gui.nfftEdit.setText(Qt.QString("%1").arg(self.nfftpts))
52
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)
61                 
62         # Initialize to LPF
63         self.gui.filterTypeWidget.setCurrentWidget(self.gui.firlpfPage)
64
65         # Set Axis labels
66         self.gui.freqPlot.setAxisTitle(self.gui.freqPlot.xBottom,
67                                        "Frequency (Hz)")
68         self.gui.freqPlot.setAxisTitle(self.gui.freqPlot.yLeft,
69                                        "Magnitude (dB)")
70         self.gui.timePlot.setAxisTitle(self.gui.timePlot.xBottom,
71                                        "Tap number")
72         self.gui.timePlot.setAxisTitle(self.gui.timePlot.yLeft,
73                                        "Amplitude")
74         self.gui.phasePlot.setAxisTitle(self.gui.phasePlot.xBottom,
75                                         "Frequency (Hz)")
76         self.gui.phasePlot.setAxisTitle(self.gui.phasePlot.yLeft,
77                                         "Phase (Radians)")
78         self.gui.groupPlot.setAxisTitle(self.gui.groupPlot.xBottom,
79                                         "Frequency (Hz)")
80         self.gui.groupPlot.setAxisTitle(self.gui.groupPlot.yLeft,
81                                         "Delay (sec)")
82
83         # Set up plot curves
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)
88
89         self.freqcurve = Qwt.QwtPlotCurve("PSD")
90         self.freqcurve.attach(self.gui.freqPlot)
91
92         self.phasecurve = Qwt.QwtPlotCurve("Phase")
93         self.phasecurve.attach(self.gui.phasePlot)
94
95         self.groupcurve = Qwt.QwtPlotCurve("Group Delay")
96         self.groupcurve.attach(self.gui.groupPlot)
97
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())
104
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())
110
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())
116
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())
122
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))
130
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)
161
162         self.gui.nTapsEdit.setText("0")
163
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}
170
171         self.show()
172
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)
189
190         self.design()
191         
192     def changed_filter_design_type(self, design):
193         if(design == "Equiripple"):
194             self.set_equiripple()
195         else:
196             self.set_windowed()
197             
198         self.design()
199
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)
210         
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)
221         
222     def design(self):
223         ret = True
224         fs,r = self.gui.sampleRateEdit.text().toDouble()
225         ret = r and ret
226         gain,r = self.gui.filterGainEdit.text().toDouble()
227         ret = r and ret
228
229         if(ret):
230             winstr = str(self.gui.filterDesignTypeComboBox.currentText().toAscii())
231             ftype = str(self.gui.filterTypeComboBox.currentText().toAscii())
232
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)
240
241             else:
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)
251
252             if(r):
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()
259
260                 self.gui.nTapsEdit.setText(Qt.QString("%1").arg(self.taps.size))
261
262
263     # Filter design functions using a window
264     def design_win_lpf(self, fs, gain, wintype):
265         ret = True
266         pb,r = self.gui.endofLpfPassBandEdit.text().toDouble()
267         ret = r and ret
268         sb,r = self.gui.startofLpfStopBandEdit.text().toDouble()
269         ret = r and ret
270         atten,r = self.gui.lpfStopBandAttenEdit.text().toDouble()
271         ret = r and ret
272
273         if(ret):
274             tb = sb - pb
275             
276             taps = gr.firdes.low_pass_2(gain, fs, pb, tb,
277                                         atten, wintype)
278             return (taps, ret)
279         else:
280             return ([], ret)
281     
282     def design_win_bpf(self, fs, gain, wintype):
283         ret = True
284         pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
285         ret = r and ret
286         pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
287         ret = r and ret
288         tb,r  = self.gui.bpfTransitionEdit.text().toDouble()
289         ret = r and ret
290         atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
291         ret = r and ret
292
293         if(r):
294             taps = gr.firdes.band_pass_2(gain, fs, pb1, pb2, tb,
295                                          atten, wintype)
296             return (taps,r)
297         else:
298             return ([],r)
299
300     def design_win_cbpf(self, fs, gain, wintype):
301         ret = True
302         pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
303         ret = r and ret
304         pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
305         ret = r and ret
306         tb,r  = self.gui.bpfTransitionEdit.text().toDouble()
307         ret = r and ret
308         atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
309         ret = r and ret
310
311         if(r):
312             taps = gr.firdes.complex_band_pass_2(gain, fs, pb1, pb2, tb,
313                                                  atten, wintype)
314             return (taps,r)
315         else:
316             return ([],r)
317
318     def design_win_bnf(self, fs, gain, wintype):
319         ret = True
320         pb1,r = self.gui.startofBnfStopBandEdit.text().toDouble()
321         ret = r and ret
322         pb2,r = self.gui.endofBnfStopBandEdit.text().toDouble()
323         ret = r and ret
324         tb,r  = self.gui.bnfTransitionEdit.text().toDouble()
325         ret = r and ret
326         atten,r = self.gui.bnfStopBandAttenEdit.text().toDouble()
327         ret = r and ret
328
329         if(r):
330             taps = gr.firdes.band_reject_2(gain, fs, pb1, pb2, tb,
331                                            atten, wintype)
332             return (taps,r)
333         else:
334             return ([],r)
335
336     def design_win_hpf(self, fs, gain, wintype):
337         ret = True
338         sb,r = self.gui.endofHpfStopBandEdit.text().toDouble()
339         ret = r and ret
340         pb,r = self.gui.startofHpfPassBandEdit.text().toDouble()
341         ret = r and ret
342         atten,r = self.gui.hpfStopBandAttenEdit.text().toDouble()
343         ret = r and ret
344
345         if(r):
346             tb = pb - sb
347             taps = gr.firdes.high_pass_2(gain, fs, pb, tb,
348                                          atten, wintype)            
349             return (taps,r)
350         else:
351             return ([],r)
352
353     def design_win_rrc(self, fs, gain, wintype):
354         ret = True
355         sr,r = self.gui.rrcSymbolRateEdit.text().toDouble()
356         ret = r and ret
357         alpha,r = self.gui.rrcAlphaEdit.text().toDouble()
358         ret = r and ret
359         ntaps,r = self.gui.rrcNumTapsEdit.text().toInt()
360         ret = r and ret
361
362         if(r):
363             taps = gr.firdes.root_raised_cosine(gain, fs, sr,
364                                                 alpha, ntaps)
365             return (taps,r)
366         else:
367             return ([],r)
368
369     def design_win_gaus(self, fs, gain, wintype):
370         ret = True
371         sr,r = self.gui.gausSymbolRateEdit.text().toDouble()
372         ret = r and ret
373         bt,r = self.gui.gausBTEdit.text().toDouble()
374         ret = r and ret
375         ntaps,r = self.gui.gausNumTapsEdit.text().toInt()
376         ret = r and ret
377
378         if(r):
379             spb = fs / sr
380             taps = gr.firdes.gaussian(gain, spb, bt, ntaps)
381             return (taps,r)
382         else:
383             return ([],r)
384
385     # Design Functions for Equiripple Filters
386     def design_opt_lpf(self, fs, gain):
387         ret = True
388         pb,r = self.gui.endofLpfPassBandEdit.text().toDouble()
389         ret = r and ret
390         sb,r = self.gui.startofLpfStopBandEdit.text().toDouble()
391         ret = r and ret
392         atten,r = self.gui.lpfStopBandAttenEdit.text().toDouble()
393         ret = r and ret
394         ripple,r = self.gui.lpfPassBandRippleEdit.text().toDouble()
395         ret = r and ret
396
397         if(ret):
398             taps = blks2.optfir.low_pass(gain, fs, pb, sb,
399                                          ripple, atten)
400             return (taps, ret)
401         else:
402             return ([], ret)
403     
404     def design_opt_bpf(self, fs, gain):
405         ret = True
406         pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
407         ret = r and ret
408         pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
409         ret = r and ret
410         tb,r  = self.gui.bpfTransitionEdit.text().toDouble()
411         ret = r and ret
412         atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
413         ret = r and ret
414         ripple,r = self.gui.bpfPassBandRippleEdit.text().toDouble()
415         ret = r and ret
416
417         if(r):
418             sb1 = pb1 - tb
419             sb2 = pb2 + tb
420             taps = blks2.optfir.band_pass(gain, fs, sb1, pb1, pb2, sb2,
421                                           ripple, atten)
422             return (taps,r)
423         else:
424             return ([],r)
425
426     def design_opt_cbpf(self, fs, gain):
427         ret = True
428         pb1,r = self.gui.startofBpfPassBandEdit.text().toDouble()
429         ret = r and ret
430         pb2,r = self.gui.endofBpfPassBandEdit.text().toDouble()
431         ret = r and ret
432         tb,r  = self.gui.bpfTransitionEdit.text().toDouble()
433         ret = r and ret
434         atten,r = self.gui.bpfStopBandAttenEdit.text().toDouble()
435         ret = r and ret
436         ripple,r = self.gui.bpfPassBandRippleEdit.text().toDouble()
437         ret = r and ret
438
439         if(r):
440             sb1 = pb1 - tb
441             sb2 = pb2 + tb
442             taps = blks2.optfir.complex_band_pass(gain, fs, sb1, pb1, pb2, sb2,
443                                                   ripple, atten)
444             return (taps,r)
445         else:
446             return ([],r)
447
448     def design_opt_bnf(self, fs, gain):
449         ret = True
450         sb1,r = self.gui.startofBnfStopBandEdit.text().toDouble()
451         ret = r and ret
452         sb2,r = self.gui.endofBnfStopBandEdit.text().toDouble()
453         ret = r and ret
454         tb,r  = self.gui.bnfTransitionEdit.text().toDouble()
455         ret = r and ret
456         atten,r = self.gui.bnfStopBandAttenEdit.text().toDouble()
457         ret = r and ret
458         ripple,r = self.gui.bnfPassBandRippleEdit.text().toDouble()
459         ret = r and ret
460
461         if(r):
462             pb1 = sb1 - tb
463             pb2 = sb2 + tb
464             taps = blks2.optfir.band_reject(gain, fs, pb1, sb1, sb2, pb2,
465                                             ripple, atten)
466             return (taps,r)
467         else:
468             return ([],r)
469
470     def design_opt_hpf(self, fs, gain):
471         ret = True
472         sb,r = self.gui.endofHpfStopBandEdit.text().toDouble()
473         ret = r and ret
474         pb,r = self.gui.startofHpfPassBandEdit.text().toDouble()
475         ret = r and ret
476         atten,r = self.gui.hpfStopBandAttenEdit.text().toDouble()
477         ret = r and ret
478         ripple,r = self.gui.hpfPassBandRippleEdit.text().toDouble()
479         ret = r and ret
480
481         if(r):
482             taps = blks2.optfir.high_pass(gain, fs, sb, pb,
483                                           atten, ripple)
484             return (taps,r)
485         else:
486             return ([],r)
487
488     def nfft_edit_changed(self, nfft):
489         infft,r = nfft.toInt()
490         if(r and (infft != self.nfftpts)):
491             self.nfftpts = infft
492             self.update_freq_curves()
493
494     def tab_changed(self, tab):
495         if(tab == 0):
496             self.update_freq_curves()
497         if(tab == 1):
498             self.update_time_curves()
499         if(tab == 2):
500             self.update_phase_curves()
501         if(tab == 3):
502             self.update_group_curves()
503         
504     def get_fft(self, fs, taps, Npts):
505         Ts = 1.0/fs
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)
511         
512     def update_time_curves(self):
513         ntaps = len(self.taps)
514         if(ntaps > 0):
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)
518             else:
519                 self.rcurve.setData(scipy.arange(ntaps), self.taps)
520
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,
525                                            0, ntaps)
526             self.gui.timePlot.setAxisScale(self.gui.timePlot.yLeft,
527                                            ymin, ymax)
528             
529             # Set the zoomer base to unzoom to the new axis
530             self.timeZoomer.setZoomBase()
531             
532             self.gui.timePlot.replot()
533         
534     def update_freq_curves(self):
535         npts = len(self.fftdB)
536         if(npts > 0):
537             self.freqcurve.setData(self.freq, self.fftdB)
538             
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]
543             xmin = self.freq[0]
544             self.gui.freqPlot.setAxisScale(self.gui.freqPlot.xBottom,
545                                            xmin, xmax)
546             self.gui.freqPlot.setAxisScale(self.gui.freqPlot.yLeft,
547                                            ymin, ymax)
548             
549             # Set the zoomer base to unzoom to the new axis
550             self.freqZoomer.setZoomBase()
551             
552             self.gui.freqPlot.replot()
553
554
555     def update_phase_curves(self):
556         npts = len(self.fftDeg)
557         if(npts > 0):
558             self.phasecurve.setData(self.freq, self.fftDeg)
559             
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]
564             xmin = self.freq[0]
565             self.gui.phasePlot.setAxisScale(self.gui.phasePlot.xBottom,
566                                             xmin, xmax)
567             self.gui.phasePlot.setAxisScale(self.gui.phasePlot.yLeft,
568                                             ymin, ymax)
569             
570             # Set the zoomer base to unzoom to the new axis
571             self.phaseZoomer.setZoomBase()
572             
573             self.gui.phasePlot.replot()
574
575     def update_group_curves(self):
576         npts = len(self.groupDelay)
577         if(npts > 0):
578             self.groupcurve.setData(self.freq, self.groupDelay)
579             
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]
584             xmin = self.freq[0]
585             self.gui.groupPlot.setAxisScale(self.gui.groupPlot.xBottom,
586                                             xmin, xmax)
587             self.gui.groupPlot.setAxisScale(self.gui.groupPlot.yLeft,
588                                             ymin, ymax)
589             
590             # Set the zoomer base to unzoom to the new axis
591             self.groupZoomer.setZoomBase()
592             
593             self.gui.groupPlot.replot()
594
595
596 def setup_options():
597     usage="%prog: [options] (input_filename)"
598     description = ""
599
600     parser = OptionParser(conflict_handler="resolve",
601                           usage=usage, description=description)
602     return parser
603
604 def main(args):
605     parser = setup_options()
606     (options, args) = parser.parse_args ()
607
608     app = Qt.QApplication(args)
609     gplt = gr_plot_filter(app, options)
610     app.exec_()
611
612 if __name__ == '__main__':
613     main(sys.argv)
614