commit-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Commit-gnuradio] r4681 - in gnuradio/trunk: config gnuradio-examples/py


From: jcorgan
Subject: [Commit-gnuradio] r4681 - in gnuradio/trunk: config gnuradio-examples/python/hier gnuradio-examples/python/hier/networking gnuradio-examples/python/hier/sounder gnuradio-examples/python/hier/usrp gr-wxgui/src/python
Date: Thu, 1 Mar 2007 19:49:48 -0700 (MST)

Author: jcorgan
Date: 2007-03-01 19:49:48 -0700 (Thu, 01 Mar 2007)
New Revision: 4681

Added:
   gnuradio/trunk/gnuradio-examples/python/hier/networking/Makefile.am
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/Makefile.am
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/sounder_rx.py
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/sounder_tx.py
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sink.py
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sounder_rx.py
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sounder_tx.py
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_source.py
   gnuradio/trunk/gnuradio-examples/python/hier/usrp/
   gnuradio/trunk/gnuradio-examples/python/hier/usrp/Makefile.am
   gnuradio/trunk/gnuradio-examples/python/hier/usrp/usrp_fft.py
   gnuradio/trunk/gr-wxgui/src/python/fftsink2.py
   gnuradio/trunk/gr-wxgui/src/python/scopesink2.py
   gnuradio/trunk/gr-wxgui/src/python/stdgui2.py
   gnuradio/trunk/gr-wxgui/src/python/waterfallsink2.py
Removed:
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/Makefile.am
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/sounder_rx.py
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/sounder_tx.py
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sink.py
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sounder_rx.py
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sounder_tx.py
   gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_source.py
   gnuradio/trunk/gnuradio-examples/python/hier/usrp/Makefile.am
   gnuradio/trunk/gnuradio-examples/python/hier/usrp/usrp_fft.py
Modified:
   gnuradio/trunk/config/grc_gnuradio_examples.m4
   gnuradio/trunk/gnuradio-examples/python/hier/Makefile.am
   gnuradio/trunk/gnuradio-examples/python/hier/networking/
   gnuradio/trunk/gr-wxgui/src/python/Makefile.am
Log:
Merged r4671:4680 from jcorgan/est into trunk.  Pulls in gr-wxgui updates for 
new hierarchical blocks and work-in-progress channel sounder example.

Modified: gnuradio/trunk/config/grc_gnuradio_examples.m4
===================================================================
--- gnuradio/trunk/config/grc_gnuradio_examples.m4      2007-03-02 02:29:46 UTC 
(rev 4680)
+++ gnuradio/trunk/config/grc_gnuradio_examples.m4      2007-03-02 02:49:48 UTC 
(rev 4681)
@@ -33,7 +33,10 @@
         gnuradio-examples/python/digital_voice/Makefile \
        gnuradio-examples/python/hier/Makefile \
        gnuradio-examples/python/hier/audio/Makefile \
+       gnuradio-examples/python/hier/networking/Makefile \
        gnuradio-examples/python/hier/ofdm/Makefile \
+       gnuradio-examples/python/hier/sounder/Makefile \
+       gnuradio-examples/python/hier/usrp/Makefile \
         gnuradio-examples/python/multi-antenna/Makefile \
         gnuradio-examples/python/multi_usrp/Makefile \
         gnuradio-examples/python/networking/Makefile \

Modified: gnuradio/trunk/gnuradio-examples/python/hier/Makefile.am
===================================================================
--- gnuradio/trunk/gnuradio-examples/python/hier/Makefile.am    2007-03-02 
02:29:46 UTC (rev 4680)
+++ gnuradio/trunk/gnuradio-examples/python/hier/Makefile.am    2007-03-02 
02:49:48 UTC (rev 4681)
@@ -21,4 +21,7 @@
 
 SUBDIRS = \
     audio \
-    ofdm
+    ofdm \
+    networking \
+    sounder \
+    usrp


Property changes on: gnuradio/trunk/gnuradio-examples/python/hier/networking
___________________________________________________________________
Name: svn:ignore
   + Makefile
Makefile.in


Copied: gnuradio/trunk/gnuradio-examples/python/hier/networking/Makefile.am 
(from rev 4680, 
gnuradio/branches/developers/jcorgan/est/gnuradio-examples/python/hier/networking/Makefile.am)
===================================================================
--- gnuradio/trunk/gnuradio-examples/python/hier/networking/Makefile.am         
                (rev 0)
+++ gnuradio/trunk/gnuradio-examples/python/hier/networking/Makefile.am 
2007-03-02 02:49:48 UTC (rev 4681)
@@ -0,0 +1,30 @@
+#
+# Copyright 2007 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+EXTRA_DIST =                   \
+       audio_sink.py           \
+       audio_source.py         \
+       dial_tone_sink.py       \
+       dial_tone_source.py     \
+       vector_sink.py          \
+       vector_source.py
+
+MOSTLYCLEANFILES = *.pyc *~

Copied: gnuradio/trunk/gnuradio-examples/python/hier/sounder (from rev 4680, 
gnuradio/branches/developers/jcorgan/est/gnuradio-examples/python/hier/sounder)


Property changes on: gnuradio/trunk/gnuradio-examples/python/hier/sounder
___________________________________________________________________
Name: svn:ignore
   + Makefile
Makefile.in
*.pyc


Deleted: gnuradio/trunk/gnuradio-examples/python/hier/sounder/Makefile.am

Copied: gnuradio/trunk/gnuradio-examples/python/hier/sounder/Makefile.am (from 
rev 4680, 
gnuradio/branches/developers/jcorgan/est/gnuradio-examples/python/hier/sounder/Makefile.am)
===================================================================
--- gnuradio/trunk/gnuradio-examples/python/hier/sounder/Makefile.am            
                (rev 0)
+++ gnuradio/trunk/gnuradio-examples/python/hier/sounder/Makefile.am    
2007-03-02 02:49:48 UTC (rev 4681)
@@ -0,0 +1,30 @@
+#
+# Copyright 2007 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+EXTRA_DIST =                   \
+       usrp_sounder_rx.py      \
+       usrp_sounder_tx.py      \
+       usrp_source.py          \
+       usrp_sink.py            \
+       sounder_rx.py           \
+       sounder_tx.py
+
+MOSTLYCLEANFILES = *.pyc *~

Deleted: gnuradio/trunk/gnuradio-examples/python/hier/sounder/sounder_rx.py

Copied: gnuradio/trunk/gnuradio-examples/python/hier/sounder/sounder_rx.py 
(from rev 4680, 
gnuradio/branches/developers/jcorgan/est/gnuradio-examples/python/hier/sounder/sounder_rx.py)
===================================================================
--- gnuradio/trunk/gnuradio-examples/python/hier/sounder/sounder_rx.py          
                (rev 0)
+++ gnuradio/trunk/gnuradio-examples/python/hier/sounder/sounder_rx.py  
2007-03-02 02:49:48 UTC (rev 4681)
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+from gnuradio import gr, usrp, eng_notation
+from usrp_source import usrp_source_c
+import math
+
+n2s = eng_notation.num_to_str
+
+class sounder_rx(gr.hier_block2):
+    """
+    Creates a top-level channel sounder block with the given parameters.
+    """
+
+    def __init__(self, subdev_spec, freq, cal, verbose, max_delay, chip_rate, 
gain):
+
+        # Call hierarchical block constructor
+        # Top-level blocks have no inputs or outputs
+        gr.hier_block2.__init__(self,
+                                "sounder_rx",           # Block typename
+                                gr.io_signature(0,0,0), # Input signature
+                                gr.io_signature(0,0,0)) # Output signature
+        self._freq = freq
+        self._cal = cal
+        self._verbose = verbose
+        self._max_delay = max_delay
+
+        self._u = usrp_source_c(0, subdev_spec, gain, chip_rate, self._freq, 
self._cal, verbose)
+        self.define_component("usrp", self._u)
+
+        self._chip_rate = self._u._if_rate
+        self._resolution = 1.0/self._chip_rate
+
+        min_chips = int(math.ceil(2.0*self._max_delay * self._chip_rate))
+        degree = int(math.ceil(math.log(min_chips)/math.log(2)))
+        self._length = 2**degree-1
+        self._seq_per_sec = self._chip_rate/self._length
+        self._tap = 0.0001
+
+        if self._verbose:
+            print "Actual chip rate is", n2s(self._chip_rate), "chips/sec"
+            print "Resolution is", n2s(self._resolution), "sec"
+            print "Using specified maximum delay spread of", self._max_delay, 
"sec"
+            print "Mininum sequence length needed is", n2s(min_chips), "chips"
+            print "Using PN sequence of degree", degree, "length", self._length
+            print "Sequences per second is", self._seq_per_sec
+            print "IIR tap is", self._tap
+        
+        self.define_component("s2v", gr.stream_to_vector(gr.sizeof_gr_complex, 
self._length))
+        self.define_component("fft", gr.fft_vcc(self._length, True, ())) # No 
window needed
+        self.define_component("avg", gr.single_pole_iir_filter_cc(self._tap, 
self._length))
+        self.define_component("keep", 
gr.keep_one_in_n(gr.sizeof_gr_complex*self._length, int(self._seq_per_sec)))
+        self.define_component("sink", 
gr.file_sink(gr.sizeof_gr_complex*self._length, "FFT.dat"))
+
+        self.connect("usrp", 0, "s2v", 0)
+        self.connect("s2v", 0, "fft", 0)
+        self.connect("fft", 0, "avg", 0)
+        self.connect("avg", 0, "keep", 0)
+        self.connect("keep", 0, "sink", 0)

Deleted: gnuradio/trunk/gnuradio-examples/python/hier/sounder/sounder_tx.py

Copied: gnuradio/trunk/gnuradio-examples/python/hier/sounder/sounder_tx.py 
(from rev 4680, 
gnuradio/branches/developers/jcorgan/est/gnuradio-examples/python/hier/sounder/sounder_tx.py)
===================================================================
--- gnuradio/trunk/gnuradio-examples/python/hier/sounder/sounder_tx.py          
                (rev 0)
+++ gnuradio/trunk/gnuradio-examples/python/hier/sounder/sounder_tx.py  
2007-03-02 02:49:48 UTC (rev 4681)
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+from gnuradio import gr, usrp, eng_notation
+from usrp_sink import usrp_sink_c
+import math
+
+n2s = eng_notation.num_to_str
+
+class sounder_tx(gr.hier_block2):
+    """
+    Creates a top-level channel sounder block with the given parameters.
+    """
+
+    def __init__(self, subdev_spec, freq, cal, verbose, max_delay, chip_rate, 
amplitude):
+
+        # Call hierarchical block constructor
+        # Top-level blocks have no inputs or outputs
+        gr.hier_block2.__init__(self,
+                                "sounder_tx",           # Block typename
+                                gr.io_signature(0,0,0), # Input signature
+                                gr.io_signature(0,0,0)) # Output signature
+        self._freq = freq
+        self._cal = cal
+        self._verbose = verbose
+        self._max_delay = max_delay
+
+        self._u = usrp_sink_c(0, subdev_spec, chip_rate, self._freq, 
self._cal, verbose)
+        self._chip_rate = self._u._if_rate
+        self._resolution = 1.0/self._chip_rate
+
+        min_chips = int(math.ceil(2.0*self._max_delay * self._chip_rate))
+        degree = int(math.ceil(math.log(min_chips)/math.log(2)))
+        self._length = 2**degree-1
+
+        self._glfsr = gr.glfsr_source_b(degree)
+        self._mapper = gr.chunks_to_symbols_bc((-amplitude+0j, amplitude+0j), 
1)
+        
+        if self._verbose:
+            print "Actual chip rate is", n2s(self._chip_rate), "chips/sec"
+            print "Resolution is", n2s(self._resolution), "sec"
+            print "Using specified maximum delay spread of", self._max_delay, 
"sec"
+            print "Mininum sequence length needed is", n2s(min_chips), "chips"
+            print "Using PN sequence of degree", degree, "length", self._length
+            print "Output amplitude is", amplitude
+            
+        self.define_component("glfsr", self._glfsr)
+        self.define_component("mapper", self._mapper)
+        self.define_component("usrp", self._u)
+        self.connect("glfsr", 0, "mapper", 0)
+        self.connect("mapper", 0, "usrp", 0)

Deleted: gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sink.py

Copied: gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sink.py (from 
rev 4680, 
gnuradio/branches/developers/jcorgan/est/gnuradio-examples/python/hier/sounder/usrp_sink.py)
===================================================================
--- gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sink.py           
                (rev 0)
+++ gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sink.py   
2007-03-02 02:49:48 UTC (rev 4681)
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+from gnuradio import gr, usrp, eng_notation
+n2s = eng_notation.num_to_str
+
+# Hierarchical block implementing a USRP sink for complex floats,
+# with convenience functions for tuning, interpolation, etc.
+#
+class usrp_sink_c(gr.hier_block2):
+    """
+    Create a USRP sink object accepting complex floats.
+    """
+    def __init__(self, which=0, subdev_spec=None, if_rate=None,
+                 freq=0.0, calibration=0.0, verbose=False):
+       # Call hierarchical block constructor
+       gr.hier_block2.__init__(self, 
+                                "usrp_sink_c",                             # 
Block typename
+                               gr.io_signature(1,1,gr.sizeof_gr_complex), # 
Input signature
+                               gr.io_signature(0,0,0))                    # 
Output signature
+
+        self._verbose = verbose
+        self._u = usrp.sink_c(which)
+        if self._verbose:
+            print 'DAC sample rate is', n2s(self._u.dac_rate()), "sps"
+        self.set_subdev(subdev_spec)
+        self.set_if_rate(if_rate)
+        self.set_calibration(calibration)
+        self.tune(freq)
+
+        self.define_component("usrp", self._u)
+        self.connect("self", 0, "usrp", 0)
+
+
+    def set_subdev(self, subdev_spec):
+        if subdev_spec is None:
+            subdev_spec = self.pick_subdevice()
+        self._subdev = usrp.selected_subdev(self._u, subdev_spec)
+        self._u.set_mux(usrp.determine_tx_mux_value(self._u, subdev_spec))
+        if self._verbose:
+            print 'TX using', self._subdev.name(), 'daughterboard'
+
+    def pick_subdevice(self):
+        """
+        The user didn't specify a subdevice.
+        If there's a daughterboard on A, select A.
+        If there's a daughterboard on B, select B.
+        Otherwise, select A.
+        """
+        if self._u.db[0][0].dbid() >= 0:       # dbid is < 0 if there's no 
d'board or a problem
+            return (0, 0)
+        if self._u.db[1][0].dbid() >= 0:
+            return (1, 0)
+        return (0, 0)
+
+    def set_if_rate(self, if_rate):
+        # If no IF rate specified, set to maximum interpolation
+        if if_rate is None:
+            self._interp = 512
+        else:
+            self._interp = 4*int(self._u.dac_rate()/(4.0*if_rate)+0.5)
+            
+
+        self._if_rate = self._u.dac_rate()/self._interp
+        self._u.set_interp_rate(self._interp)
+
+        if self._verbose:
+            print "USRP interpolation rate is", self._interp
+            print "USRP IF rate is", n2s(self._if_rate), "sps"
+
+    def set_calibration(self, calibration):
+        self._cal = calibration
+        if self._verbose:
+            print "Using frequency calibration offset of", n2s(calibration), 
"Hz"
+
+    def tune(self, freq):
+        """
+        Set the center frequency we're interested in.
+
+        @param target_freq: frequency in Hz
+        @type: bool
+
+        Tuning is a two step process.  First we ask the front-end to
+        tune as close to the desired frequency as it can.  Then we use
+        the result of that operation and our target_frequency to
+        determine the value for the digital down converter.
+        """
+        self._tune_result = self._u.tune(self._subdev._which, self._subdev, 
freq+self._cal)
+        if self._tune_result:
+            if self._verbose:
+                print "Baseband frequency is", 
n2s(self._tune_result.baseband_freq), "Hz"
+                print "DXC frequency is", n2s(self._tune_result.dxc_freq), "Hz"
+                print "Center frequency is", n2s(freq), "Hz"
+                print "Residual frequency is", 
n2s(self._tune_result.residual_freq), "Hz"
+            return True
+        
+        return False
+
+if __name__ == '__main__':
+    sink = usrp_sink_c(verbose=True)
+    

Deleted: gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sounder_rx.py

Copied: gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sounder_rx.py 
(from rev 4680, 
gnuradio/branches/developers/jcorgan/est/gnuradio-examples/python/hier/sounder/usrp_sounder_rx.py)
===================================================================
--- gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sounder_rx.py     
                        (rev 0)
+++ gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sounder_rx.py     
2007-03-02 02:49:48 UTC (rev 4681)
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+from gnuradio import gr, eng_notation
+from gnuradio.eng_option import eng_option
+from optparse import OptionParser
+from sounder_rx import sounder_rx
+
+def main():
+       parser = OptionParser(option_class=eng_option)
+
+       # Receive path options
+        parser.add_option("-R", "--rx-subdev-spec", type="subdev", 
default=None,
+                          help="select USRP Rx side A or B (default=first 
found)")
+        parser.add_option("-f", "--freq", type="eng_float", default=0.0,
+                          help="set center frequency (default=%default)")
+        parser.add_option("-c", "--cal", type="eng_float", default=0.0,
+                          help="set frequency calibration offset 
(default=%default)")
+       parser.add_option("-v", "--verbose", action="store_true", default=False,
+                         help="print extra debugging info")
+       parser.add_option("-d", "--max-delay", type="eng_float", default=10e-6,
+                         help="set maximum delay spread (default=%default)")
+       parser.add_option("-r", "--chip-rate", type="eng_float", default=8e6,
+                         help="set sounder chip rate (default=%default)")
+       parser.add_option("-g", "--gain", type="eng_float", default=None,
+                         help="set output amplitude (default=%default)")
+        (options, args) = parser.parse_args()
+
+       if len(args) != 0:
+            parser.print_help()
+            sys.exit(1)
+
+       # Create an instance of a hierarchical block
+       top_block = sounder_rx(options.rx_subdev_spec, options.freq, 
options.cal,
+                              options.verbose, options.max_delay, 
options.chip_rate,
+                              options.gain)
+                             
+       # Create an instance of a runtime, passing it the top block
+       # to process
+       runtime = gr.runtime(top_block)
+
+       try:    
+            # Run forever
+            runtime.run()
+       except KeyboardInterrupt:
+            # Ctrl-C exits
+            pass
+
+if __name__ == '__main__':
+    main ()

Deleted: gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sounder_tx.py

Copied: gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sounder_tx.py 
(from rev 4680, 
gnuradio/branches/developers/jcorgan/est/gnuradio-examples/python/hier/sounder/usrp_sounder_tx.py)
===================================================================
--- gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sounder_tx.py     
                        (rev 0)
+++ gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_sounder_tx.py     
2007-03-02 02:49:48 UTC (rev 4681)
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+from gnuradio import gr, eng_notation
+from gnuradio.eng_option import eng_option
+from optparse import OptionParser
+from sounder_tx import sounder_tx
+
+def main():
+       parser = OptionParser(option_class=eng_option)
+
+       # Transmit path options
+        parser.add_option("-T", "--tx-subdev-spec", type="subdev", 
default=None,
+                          help="select USRP Rx side A or B (default=first 
found)")
+        parser.add_option("-f", "--freq", type="eng_float", default=0.0,
+                          help="set center frequency (default=%default)")
+        parser.add_option("-c", "--cal", type="eng_float", default=0.0,
+                          help="set frequency calibration offset 
(default=%default)")
+       parser.add_option("-v", "--verbose", action="store_true", default=False,
+                         help="print extra debugging info")
+       parser.add_option("-d", "--max-delay", type="eng_float", default=10e-6,
+                         help="set maximum delay spread (default=%default)")
+       parser.add_option("-r", "--chip-rate", type="eng_float", default=8e6,
+                         help="set sounder chip rate (default=%default)")
+       parser.add_option("-g", "--amplitude", type="eng_float", 
default=32000.0,
+                         help="set output amplitude (default=%default)")
+        (options, args) = parser.parse_args()
+
+       if len(args) != 0:
+            parser.print_help()
+            sys.exit(1)
+
+       # Create an instance of a hierarchical block
+       top_block = sounder_tx(options.tx_subdev_spec, options.freq, 
options.cal,
+                              options.verbose, options.max_delay, 
options.chip_rate,
+                              options.amplitude)
+                             
+       # Create an instance of a runtime, passing it the top block
+       # to process
+       runtime = gr.runtime(top_block)
+
+       try:    
+            # Run forever
+            runtime.run()
+       except KeyboardInterrupt:
+            # Ctrl-C exits
+            pass
+
+if __name__ == '__main__':
+    main ()

Deleted: gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_source.py

Copied: gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_source.py 
(from rev 4680, 
gnuradio/branches/developers/jcorgan/est/gnuradio-examples/python/hier/sounder/usrp_source.py)
===================================================================
--- gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_source.py         
                (rev 0)
+++ gnuradio/trunk/gnuradio-examples/python/hier/sounder/usrp_source.py 
2007-03-02 02:49:48 UTC (rev 4681)
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+from gnuradio import gr, usrp, eng_notation
+n2s = eng_notation.num_to_str
+
+# Hierarchical block implementing a USRP source for complex floats,
+# with convenience functions for gain, tune, decimation, etc.
+#
+class usrp_source_c(gr.hier_block2):
+    """
+    Create a USRP source object supplying complex floats.
+    """
+    def __init__(self, which=0, subdev_spec=None, gain=None, if_rate=None,
+                 freq=0.0, calibration=0.0, verbose=False):
+       # Call hierarchical block constructor
+       gr.hier_block2.__init__(self,
+                                "usrp_source_c",                           # 
Block typename
+                               gr.io_signature(0,0,0),                    # 
Input signature
+                               gr.io_signature(1,1,gr.sizeof_gr_complex)) # 
Output signature
+
+        self._verbose = verbose
+        self._u = usrp.source_c(which)
+        if self._verbose:
+            print 'ADC sample rate is', n2s(self._u.adc_rate()), "sps"
+        self.set_subdev(subdev_spec)
+        self.set_if_rate(if_rate)
+        self.set_gain(gain)
+        self.set_calibration(calibration)
+        self.tune(freq)
+
+        self.define_component("usrp", self._u)
+        self.connect("usrp", 0, "self", 0)
+
+    def set_subdev(self, subdev_spec):
+        if subdev_spec is None:
+            subdev_spec = self.pick_subdevice()
+        self._subdev = usrp.selected_subdev(self._u, subdev_spec)
+        self._u.set_mux(usrp.determine_rx_mux_value(self._u, subdev_spec))
+        if self._verbose:
+            print 'RX using', self._subdev.name(), 'daughterboard'
+            
+    def pick_subdevice(self):
+        """
+        The user didn't specify a subdevice.
+        If there's a daughterboard on A, select A.
+        If there's a daughterboard on B, select B.
+        Otherwise, select A.
+        """
+        if self._u.db[0][0].dbid() >= 0:       # dbid is < 0 if there's no 
d'board or a problem
+            return (0, 0)
+        if self._u.db[1][0].dbid() >= 0:
+            return (1, 0)
+        return (0, 0)
+
+    def set_if_rate(self, if_rate):
+        # If no IF rate specified, set to maximum decimation
+        if if_rate is None:
+            self._decim = 256
+        else:
+            self._decim = int(self._u.adc_rate()/if_rate)
+
+        self._u.set_decim_rate(self._decim)
+        self._if_rate = self._u.adc_rate()/self._decim
+
+        if self._verbose:
+            print "USRP decimation rate is", self._decim
+            print "USRP IF rate is", n2s(self._if_rate), "sps"
+            
+    def set_gain(self, gain):
+       # If no gain specified, set to midrange
+       if gain is None:
+           g = self._subdev.gain_range()
+           gain = (g[0]+g[1])/2.0
+        self._gain = gain
+        self._subdev.set_gain(self._gain)
+
+    def set_calibration(self, calibration):
+        self._cal = calibration
+        if self._verbose:
+            print "Using frequency calibration offset of", n2s(calibration), 
"Hz"
+            
+    def tune(self, freq):
+        """
+        Set the center frequency we're interested in.
+
+        @param target_freq: frequency in Hz
+        @type: bool
+
+        Tuning is a two step process.  First we ask the front-end to
+        tune as close to the desired frequency as it can.  Then we use
+        the result of that operation and our target_frequency to
+        determine the value for the digital down converter.
+        """
+        self._tune_result = usrp.tune(self._u, 0, self._subdev, freq+self._cal)
+        if self._tune_result:
+            if self._verbose:
+                print "Baseband frequency is", 
n2s(self._tune_result.baseband_freq), "Hz"
+                print "DXC frequency is", n2s(self._tune_result.dxc_freq), "Hz"
+                print "Center frequency is", n2s(freq), "Hz"
+                print "Residual frequency is", 
n2s(self._tune_result.residual_freq), "Hz"
+            return True
+        
+        return False
+
+if __name__ == '__main__':
+    src = usrp_source_c(verbose=True)

Copied: gnuradio/trunk/gnuradio-examples/python/hier/usrp (from rev 4680, 
gnuradio/branches/developers/jcorgan/est/gnuradio-examples/python/hier/usrp)


Property changes on: gnuradio/trunk/gnuradio-examples/python/hier/usrp
___________________________________________________________________
Name: svn:ignore
   + Makefile
Makefile.in


Deleted: gnuradio/trunk/gnuradio-examples/python/hier/usrp/Makefile.am

Copied: gnuradio/trunk/gnuradio-examples/python/hier/usrp/Makefile.am (from rev 
4680, 
gnuradio/branches/developers/jcorgan/est/gnuradio-examples/python/hier/usrp/Makefile.am)
===================================================================
--- gnuradio/trunk/gnuradio-examples/python/hier/usrp/Makefile.am               
                (rev 0)
+++ gnuradio/trunk/gnuradio-examples/python/hier/usrp/Makefile.am       
2007-03-02 02:49:48 UTC (rev 4681)
@@ -0,0 +1,25 @@
+#
+# Copyright 2007 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+EXTRA_DIST =                   \
+       usrp_fft.py
+
+MOSTLYCLEANFILES = *.pyc *~

Deleted: gnuradio/trunk/gnuradio-examples/python/hier/usrp/usrp_fft.py

Copied: gnuradio/trunk/gnuradio-examples/python/hier/usrp/usrp_fft.py (from rev 
4680, 
gnuradio/branches/developers/jcorgan/est/gnuradio-examples/python/hier/usrp/usrp_fft.py)
===================================================================
--- gnuradio/trunk/gnuradio-examples/python/hier/usrp/usrp_fft.py               
                (rev 0)
+++ gnuradio/trunk/gnuradio-examples/python/hier/usrp/usrp_fft.py       
2007-03-02 02:49:48 UTC (rev 4681)
@@ -0,0 +1,253 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2005,2007 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+from gnuradio import gr, gru
+from gnuradio import usrp
+from gnuradio import eng_notation
+from gnuradio.eng_option import eng_option
+from gnuradio.wxgui import stdgui2, fftsink2, waterfallsink2, scopesink2, 
form, slider
+from optparse import OptionParser
+import wx
+import sys
+
+
+def pick_subdevice(u):
+    """
+    The user didn't specify a subdevice on the command line.
+    If there's a daughterboard on A, select A.
+    If there's a daughterboard on B, select B.
+    Otherwise, select A.
+    """
+    if u.db[0][0].dbid() >= 0:       # dbid is < 0 if there's no d'board or a 
problem
+        return (0, 0)
+    if u.db[1][0].dbid() >= 0:
+        return (1, 0)
+    return (0, 0)
+
+
+class app_top_block(stdgui2.std_top_block):
+    def __init__(self, frame, panel, vbox, argv):
+        stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv)
+
+        self.frame = frame
+        self.panel = panel
+        
+        parser = OptionParser(option_class=eng_option)
+        parser.add_option("-R", "--rx-subdev-spec", type="subdev", 
default=None,
+                          help="select USRP Rx side A or B (default=first one 
with a daughterboard)")
+        parser.add_option("-d", "--decim", type="int", default=16,
+                          help="set fgpa decimation rate to DECIM 
[default=%default]")
+        parser.add_option("-f", "--freq", type="eng_float", default=None,
+                          help="set frequency to FREQ", metavar="FREQ")
+        parser.add_option("-g", "--gain", type="eng_float", default=None,
+                          help="set gain in dB (default is midpoint)")
+        parser.add_option("-W", "--waterfall", action="store_true", 
default=False,
+                          help="Enable waterfall display")
+        parser.add_option("-8", "--width-8", action="store_true", 
default=False,
+                          help="Enable 8-bit samples across USB")
+        parser.add_option("-S", "--oscilloscope", action="store_true", 
default=False,
+                          help="Enable oscilloscope display")
+        (options, args) = parser.parse_args()
+        if len(args) != 0:
+            parser.print_help()
+            sys.exit(1)
+
+        self.show_debug_info = True
+        
+        self.u = usrp.source_c(decim_rate=options.decim)
+        self.define_component("usrp", self.u)
+        if options.rx_subdev_spec is None:
+            options.rx_subdev_spec = pick_subdevice(self.u)
+        self.u.set_mux(usrp.determine_rx_mux_value(self.u, 
options.rx_subdev_spec))
+
+        if options.width_8:
+            width = 8
+            shift = 8
+            format = self.u.make_format(width, shift)
+            print "format =", hex(format)
+            r = self.u.set_format(format)
+            print "set_format =", r
+            
+        # determine the daughterboard subdevice we're using
+        self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec)
+
+        input_rate = self.u.adc_freq() / self.u.decim_rate()
+
+        
+        if options.waterfall:
+            self.scope = waterfallsink2.waterfall_sink_c (panel, 
fft_size=1024, sample_rate=input_rate)
+        elif options.oscilloscope:
+            self.scope = scopesink2.scope_sink_c(panel, sample_rate=input_rate)
+        else:
+           self.scope = fftsink2.fft_sink_c (panel, fft_size=1024, 
sample_rate=input_rate)
+        self.define_component("scope", self.scope)
+
+        # Ultimately this will be
+        # self.connect("usrp scope")
+        self.connect("usrp", 0, "scope", 0)
+
+        self._build_gui(vbox)
+
+        # set initial values
+
+        if options.gain is None:
+            # if no gain was specified, use the mid-point in dB
+            g = self.subdev.gain_range()
+            options.gain = float(g[0]+g[1])/2
+
+        if options.freq is None:
+            # if no freq was specified, use the mid-point
+            r = self.subdev.freq_range()
+            options.freq = float(r[0]+r[1])/2
+
+        self.set_gain(options.gain)
+
+        if self.show_debug_info:
+            self.myform['decim'].set_value(self.u.decim_rate())
+            self.myform['address@hidden'].set_value(self.u.adc_freq() / 
self.u.decim_rate())
+            self.myform['dbname'].set_value(self.subdev.name())
+            self.myform['baseband'].set_value(0)
+            self.myform['ddc'].set_value(0)
+
+        if not(self.set_freq(options.freq)):
+            self._set_status_msg("Failed to set initial frequency")
+
+    def _set_status_msg(self, msg):
+        self.frame.GetStatusBar().SetStatusText(msg, 0)
+
+    def _build_gui(self, vbox):
+
+        def _form_set_freq(kv):
+            return self.set_freq(kv['freq'])
+            
+        vbox.Add(self.scope.win, 10, wx.EXPAND)
+        
+        # add control area at the bottom
+        self.myform = myform = form.form()
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        hbox.Add((5,0), 0, 0)
+        myform['freq'] = form.float_field(
+            parent=self.panel, sizer=hbox, label="Center freq", weight=1,
+            callback=myform.check_input_and_call(_form_set_freq, 
self._set_status_msg))
+
+        hbox.Add((5,0), 0, 0)
+        g = self.subdev.gain_range()
+        myform['gain'] = form.slider_field(parent=self.panel, sizer=hbox, 
label="Gain",
+                                           weight=3,
+                                           min=int(g[0]), max=int(g[1]),
+                                           callback=self.set_gain)
+
+        hbox.Add((5,0), 0, 0)
+        vbox.Add(hbox, 0, wx.EXPAND)
+
+        self._build_subpanel(vbox)
+
+    def _build_subpanel(self, vbox_arg):
+        # build a secondary information panel (sometimes hidden)
+
+        # FIXME figure out how to have this be a subpanel that is always
+        # created, but has its visibility controlled by foo.Show(True/False)
+        
+        def _form_set_decim(kv):
+            return self.set_decim(kv['decim'])
+
+        if not(self.show_debug_info):
+            return
+
+        panel = self.panel
+        vbox = vbox_arg
+        myform = self.myform
+
+        #panel = wx.Panel(self.panel, -1)
+        #vbox = wx.BoxSizer(wx.VERTICAL)
+
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        hbox.Add((5,0), 0)
+
+        myform['decim'] = form.int_field(
+            parent=panel, sizer=hbox, label="Decim",
+            callback=myform.check_input_and_call(_form_set_decim, 
self._set_status_msg))
+
+        hbox.Add((5,0), 1)
+        myform['address@hidden'] = form.static_float_field(
+            parent=panel, sizer=hbox, label="address@hidden")
+
+        hbox.Add((5,0), 1)
+        myform['dbname'] = form.static_text_field(
+            parent=panel, sizer=hbox)
+
+        hbox.Add((5,0), 1)
+        myform['baseband'] = form.static_float_field(
+            parent=panel, sizer=hbox, label="Analog BB")
+
+        hbox.Add((5,0), 1)
+        myform['ddc'] = form.static_float_field(
+            parent=panel, sizer=hbox, label="DDC")
+
+        hbox.Add((5,0), 0)
+        vbox.Add(hbox, 0, wx.EXPAND)
+
+        
+    def set_freq(self, target_freq):
+        """
+        Set the center frequency we're interested in.
+
+        @param target_freq: frequency in Hz
+        @rypte: bool
+
+        Tuning is a two step process.  First we ask the front-end to
+        tune as close to the desired frequency as it can.  Then we use
+        the result of that operation and our target_frequency to
+        determine the value for the digital down converter.
+        """
+        r = self.u.tune(0, self.subdev, target_freq)
+        
+        if r:
+            self.myform['freq'].set_value(target_freq)     # update displayed 
value
+            if self.show_debug_info:
+                self.myform['baseband'].set_value(r.baseband_freq)
+                self.myform['ddc'].set_value(r.dxc_freq)
+            return True
+
+        return False
+
+    def set_gain(self, gain):
+        self.myform['gain'].set_value(gain)     # update displayed value
+        self.subdev.set_gain(gain)
+
+    def set_decim(self, decim):
+        ok = self.u.set_decim_rate(decim)
+        if not ok:
+            print "set_decim failed"
+        input_rate = self.u.adc_freq() / self.u.decim_rate()
+        self.scope.set_sample_rate(input_rate)
+        if self.show_debug_info:  # update displayed values
+            self.myform['decim'].set_value(self.u.decim_rate())
+            self.myform['address@hidden'].set_value(self.u.adc_freq() / 
self.u.decim_rate())
+        return ok
+
+def main ():
+    app = stdgui2.stdapp(app_top_block, "USRP FFT", nstatus=1)
+    app.MainLoop()
+
+if __name__ == '__main__':
+    main ()

Modified: gnuradio/trunk/gr-wxgui/src/python/Makefile.am
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/Makefile.am      2007-03-02 02:29:46 UTC 
(rev 4680)
+++ gnuradio/trunk/gr-wxgui/src/python/Makefile.am      2007-03-02 02:49:48 UTC 
(rev 4681)
@@ -32,10 +32,14 @@
        __init__.py                     \
        form.py                         \
        fftsink.py                      \
+       fftsink2.py                     \
        plot.py                         \
        powermate.py                    \
        scopesink.py                    \
+       scopesink2.py                   \
        waterfallsink.py                \
+       waterfallsink2.py               \
        slider.py                       \
        stdgui.py                       \
+       stdgui2.py                      \
        numbersink.py           

Copied: gnuradio/trunk/gr-wxgui/src/python/fftsink2.py (from rev 4680, 
gnuradio/branches/developers/jcorgan/est/gr-wxgui/src/python/fftsink2.py)
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/fftsink2.py                              
(rev 0)
+++ gnuradio/trunk/gr-wxgui/src/python/fftsink2.py      2007-03-02 02:49:48 UTC 
(rev 4681)
@@ -0,0 +1,506 @@
+#!/usr/bin/env python
+#
+# Copyright 2003,2004,2005,2006 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+from gnuradio import gr, gru, window
+from gnuradio.wxgui import stdgui2
+import wx
+import gnuradio.wxgui.plot as plot
+import Numeric
+import threading
+import math    
+
+default_fftsink_size = (640,240)
+default_fft_rate = gr.prefs().get_long('wxgui', 'fft_rate', 15)
+
+class fft_sink_base(object):
+    def __init__(self, input_is_real=False, baseband_freq=0, y_per_div=10, 
ref_level=50,
+                 sample_rate=1, fft_size=512,
+                 fft_rate=default_fft_rate,
+                 average=False, avg_alpha=None, title='', peak_hold=False):
+
+        # initialize common attributes
+        self.baseband_freq = baseband_freq
+        self.y_divs = 8
+        self.y_per_div=y_per_div
+        self.ref_level = ref_level
+        self.sample_rate = sample_rate
+        self.fft_size = fft_size
+        self.fft_rate = fft_rate
+        self.average = average
+        if avg_alpha is None:
+            self.avg_alpha = 2.0 / fft_rate
+        else:
+            self.avg_alpha = avg_alpha
+        self.title = title
+        self.peak_hold = peak_hold
+        self.input_is_real = input_is_real
+        self.msgq = gr.msg_queue(2)         # queue that holds a maximum of 2 
messages
+
+    def set_y_per_div(self, y_per_div):
+        self.y_per_div = y_per_div
+
+    def set_ref_level(self, ref_level):
+        self.ref_level = ref_level
+
+    def set_average(self, average):
+        self.average = average
+        if average:
+            self.avg.set_taps(self.avg_alpha)
+            self.set_peak_hold(False)
+        else:
+            self.avg.set_taps(1.0)
+
+    def set_peak_hold(self, enable):
+        self.peak_hold = enable
+        if enable:
+            self.set_average(False)
+        self.win.set_peak_hold(enable)
+
+    def set_avg_alpha(self, avg_alpha):
+        self.avg_alpha = avg_alpha
+
+    def set_baseband_freq(self, baseband_freq):
+        self.baseband_freq = baseband_freq
+
+    def set_sample_rate(self, sample_rate):
+        self.sample_rate = sample_rate
+        self._set_n()
+
+    def _set_n(self):
+        self.one_in_n.set_n(max(1, 
int(self.sample_rate/self.fft_size/self.fft_rate)))
+        
+
+class fft_sink_f(gr.hier_block2, fft_sink_base):
+    def __init__(self, parent, baseband_freq=0,
+                 y_per_div=10, ref_level=50, sample_rate=1, fft_size=512,
+                 fft_rate=default_fft_rate, average=False, avg_alpha=None,
+                 title='', size=default_fftsink_size, peak_hold=False):
+
+        gr.hier_block2.__init__(self, "fft_sink_f",
+                                gr.io_signature(1, 1, gr.sizeof_float),
+                                gr.io_signature(0,0,0))
+
+        fft_sink_base.__init__(self, input_is_real=True, 
baseband_freq=baseband_freq,
+                               y_per_div=y_per_div, ref_level=ref_level,
+                               sample_rate=sample_rate, fft_size=fft_size,
+                               fft_rate=fft_rate,
+                               average=average, avg_alpha=avg_alpha, 
title=title,
+                               peak_hold=peak_hold)
+                               
+        self.define_component("s2p", gr.stream_to_vector(gr.sizeof_float, 
self.fft_size))
+        self.one_in_n = gr.keep_one_in_n(gr.sizeof_float * self.fft_size,
+                                         max(1, 
int(self.sample_rate/self.fft_size/self.fft_rate)))
+        self.define_component("one_in_n", self.one_in_n)
+        
+        mywindow = window.blackmanharris(self.fft_size)
+        self.define_component("fft", gr.fft_vfc(self.fft_size, True, mywindow))
+        power = 0
+        for tap in mywindow:
+            power += tap*tap
+            
+        self.define_component("c2mag", gr.complex_to_mag(self.fft_size))
+        self.avg = gr.single_pole_iir_filter_ff(1.0, self.fft_size)
+        self.define_component("avg", self.avg)
+
+        # FIXME  We need to add 3dB to all bins but the DC bin
+        self.define_component("log", gr.nlog10_ff(20, self.fft_size,
+                                     
-20*math.log10(self.fft_size)-10*math.log10(power/self.fft_size)))
+        self.define_component("sink", gr.message_sink(gr.sizeof_float * 
self.fft_size, self.msgq, True))
+
+
+        # Ultimately this will be
+        # self.connect("self s2p one_in_n fft c2mag avg log sink")
+        self.connect("self", 0, "s2p", 0)
+        self.connect("s2p", 0, "one_in_n", 0)
+        self.connect("one_in_n", 0, "fft", 0)
+        self.connect("fft", 0, "c2mag", 0)
+        self.connect("c2mag", 0, "avg", 0)
+        self.connect("avg", 0, "log", 0)
+        self.connect("log", 0, "sink", 0)
+                              
+        self.win = fft_window(self, parent, size=size)
+        self.set_average(self.average)
+
+
+class fft_sink_c(gr.hier_block2, fft_sink_base):
+    def __init__(self, parent, baseband_freq=0,
+                 y_per_div=10, ref_level=50, sample_rate=1, fft_size=512,
+                 fft_rate=default_fft_rate, average=False, avg_alpha=None,
+                 title='', size=default_fftsink_size, peak_hold=False):
+
+        gr.hier_block2.__init__(self, "fft_sink_c",
+                                gr.io_signature(1, 1, gr.sizeof_gr_complex),
+                                gr.io_signature(0,0,0))
+
+        fft_sink_base.__init__(self, input_is_real=False, 
baseband_freq=baseband_freq,
+                               y_per_div=y_per_div, ref_level=ref_level,
+                               sample_rate=sample_rate, fft_size=fft_size,
+                               fft_rate=fft_rate,
+                               average=average, avg_alpha=avg_alpha, 
title=title,
+                               peak_hold=peak_hold)
+
+        self.define_component("s2p", gr.stream_to_vector(gr.sizeof_gr_complex, 
self.fft_size))
+        self.one_in_n = gr.keep_one_in_n(gr.sizeof_gr_complex * self.fft_size,
+                                         max(1, 
int(self.sample_rate/self.fft_size/self.fft_rate)))
+        self.define_component("one_in_n", self.one_in_n)
+        
+        mywindow = window.blackmanharris(self.fft_size)
+        self.define_component("fft", gr.fft_vcc(self.fft_size, True, mywindow))
+        power = 0
+        for tap in mywindow:
+            power += tap*tap
+            
+        self.define_component("c2mag", gr.complex_to_mag(self.fft_size))
+        self.avg = gr.single_pole_iir_filter_ff(1.0, self.fft_size)
+        self.define_component("avg", self.avg)
+
+        # FIXME  We need to add 3dB to all bins but the DC bin
+        self.define_component("log", gr.nlog10_ff(20, self.fft_size,
+                                     
-20*math.log10(self.fft_size)-10*math.log10(power/self.fft_size)))
+        self.define_component("sink", gr.message_sink(gr.sizeof_float * 
self.fft_size, self.msgq, True))
+
+        # Ultimately this will be
+        # self.connect("self s2p one_in_n fft c2mag avg log sink")
+        self.connect("self", 0, "s2p", 0)
+        self.connect("s2p", 0, "one_in_n", 0)
+        self.connect("one_in_n", 0, "fft", 0)
+        self.connect("fft", 0, "c2mag", 0)
+        self.connect("c2mag", 0, "avg", 0)
+        self.connect("avg", 0, "log", 0)
+        self.connect("log", 0, "sink", 0)
+
+        self.win = fft_window(self, parent, size=size)
+        self.set_average(self.average)
+
+
+# ------------------------------------------------------------------------
+
+myDATA_EVENT = wx.NewEventType()
+EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0)
+
+
+class DataEvent(wx.PyEvent):
+    def __init__(self, data):
+        wx.PyEvent.__init__(self)
+        self.SetEventType (myDATA_EVENT)
+        self.data = data
+
+    def Clone (self): 
+        self.__class__ (self.GetId())
+
+
+class input_watcher (threading.Thread):
+    def __init__ (self, msgq, fft_size, event_receiver, **kwds):
+        threading.Thread.__init__ (self, **kwds)
+        self.setDaemon (1)
+        self.msgq = msgq
+        self.fft_size = fft_size
+        self.event_receiver = event_receiver
+        self.keep_running = True
+        self.start ()
+
+    def run (self):
+        while (self.keep_running):
+            msg = self.msgq.delete_head()  # blocking read of message queue
+            itemsize = int(msg.arg1())
+            nitems = int(msg.arg2())
+
+            s = msg.to_string()            # get the body of the msg as a 
string
+
+            # There may be more than one FFT frame in the message.
+            # If so, we take only the last one
+            if nitems > 1:
+                start = itemsize * (nitems - 1)
+                s = s[start:start+itemsize]
+
+            complex_data = Numeric.fromstring (s, Numeric.Float32)
+            de = DataEvent (complex_data)
+            wx.PostEvent (self.event_receiver, de)
+            del de
+    
+
+class fft_window (plot.PlotCanvas):
+    def __init__ (self, fftsink, parent, id = -1,
+                  pos = wx.DefaultPosition, size = wx.DefaultSize,
+                  style = wx.DEFAULT_FRAME_STYLE, name = ""):
+        plot.PlotCanvas.__init__ (self, parent, id, pos, size, style, name)
+
+        self.y_range = None
+        self.fftsink = fftsink
+        self.peak_hold = False
+        self.peak_vals = None
+
+        self.SetEnableGrid (True)
+        # self.SetEnableZoom (True)
+        # self.SetBackgroundColour ('black')
+        
+        self.build_popup_menu()
+        
+        EVT_DATA_EVENT (self, self.set_data)
+        wx.EVT_CLOSE (self, self.on_close_window)
+        self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
+
+        self.input_watcher = input_watcher(fftsink.msgq, fftsink.fft_size, 
self)
+
+
+    def on_close_window (self, event):
+        print "fft_window:on_close_window"
+        self.keep_running = False
+
+
+    def set_data (self, evt):
+        dB = evt.data
+        L = len (dB)
+
+        if self.peak_hold:
+            if self.peak_vals is None:
+                self.peak_vals = dB
+            else:
+                self.peak_vals = Numeric.maximum(dB, self.peak_vals)
+                dB = self.peak_vals
+
+        x = max(abs(self.fftsink.sample_rate), abs(self.fftsink.baseband_freq))
+        if x >= 1e9:
+            sf = 1e-9
+            units = "GHz"
+        elif x >= 1e6:
+            sf = 1e-6
+            units = "MHz"
+        else:
+            sf = 1e-3
+            units = "kHz"
+
+        if self.fftsink.input_is_real:     # only plot 1/2 the points
+            x_vals = ((Numeric.arrayrange (L/2)
+                       * (self.fftsink.sample_rate * sf / L))
+                      + self.fftsink.baseband_freq * sf)
+            points = Numeric.zeros((len(x_vals), 2), Numeric.Float64)
+            points[:,0] = x_vals
+            points[:,1] = dB[0:L/2]
+        else:
+            # the "negative freqs" are in the second half of the array
+            x_vals = ((Numeric.arrayrange (-L/2, L/2)
+                       * (self.fftsink.sample_rate * sf / L))
+                      + self.fftsink.baseband_freq * sf)
+            points = Numeric.zeros((len(x_vals), 2), Numeric.Float64)
+            points[:,0] = x_vals
+            points[:,1] = Numeric.concatenate ((dB[L/2:], dB[0:L/2]))
+
+
+        lines = plot.PolyLine (points, colour='BLUE')
+
+        graphics = plot.PlotGraphics ([lines],
+                                      title=self.fftsink.title,
+                                      xLabel = units, yLabel = "dB")
+
+        self.Draw (graphics, xAxis=None, yAxis=self.y_range)
+        self.update_y_range ()
+
+    def set_peak_hold(self, enable):
+        self.peak_hold = enable
+        self.peak_vals = None
+
+    def update_y_range (self):
+        ymax = self.fftsink.ref_level
+        ymin = self.fftsink.ref_level - self.fftsink.y_per_div * 
self.fftsink.y_divs
+        self.y_range = self._axisInterval ('min', ymin, ymax)
+
+    def on_average(self, evt):
+        # print "on_average"
+        self.fftsink.set_average(evt.IsChecked())
+
+    def on_peak_hold(self, evt):
+        # print "on_peak_hold"
+        self.fftsink.set_peak_hold(evt.IsChecked())
+
+    def on_incr_ref_level(self, evt):
+        # print "on_incr_ref_level"
+        self.fftsink.set_ref_level(self.fftsink.ref_level
+                                   + self.fftsink.y_per_div)
+
+    def on_decr_ref_level(self, evt):
+        # print "on_decr_ref_level"
+        self.fftsink.set_ref_level(self.fftsink.ref_level
+                                   - self.fftsink.y_per_div)
+
+    def on_incr_y_per_div(self, evt):
+        # print "on_incr_y_per_div"
+        self.fftsink.set_y_per_div(next_up(self.fftsink.y_per_div, 
(1,2,5,10,20)))
+
+    def on_decr_y_per_div(self, evt):
+        # print "on_decr_y_per_div"
+        self.fftsink.set_y_per_div(next_down(self.fftsink.y_per_div, 
(1,2,5,10,20)))
+
+    def on_y_per_div(self, evt):
+        # print "on_y_per_div"
+        Id = evt.GetId()
+        if Id == self.id_y_per_div_1:
+            self.fftsink.set_y_per_div(1)
+        elif Id == self.id_y_per_div_2:
+            self.fftsink.set_y_per_div(2)
+        elif Id == self.id_y_per_div_5:
+            self.fftsink.set_y_per_div(5)
+        elif Id == self.id_y_per_div_10:
+            self.fftsink.set_y_per_div(10)
+        elif Id == self.id_y_per_div_20:
+            self.fftsink.set_y_per_div(20)
+
+        
+    def on_right_click(self, event):
+        menu = self.popup_menu
+        for id, pred in self.checkmarks.items():
+            item = menu.FindItemById(id)
+            item.Check(pred())
+        self.PopupMenu(menu, event.GetPosition())
+
+
+    def build_popup_menu(self):
+        self.id_incr_ref_level = wx.NewId()
+        self.id_decr_ref_level = wx.NewId()
+        self.id_incr_y_per_div = wx.NewId()
+        self.id_decr_y_per_div = wx.NewId()
+        self.id_y_per_div_1 = wx.NewId()
+        self.id_y_per_div_2 = wx.NewId()
+        self.id_y_per_div_5 = wx.NewId()
+        self.id_y_per_div_10 = wx.NewId()
+        self.id_y_per_div_20 = wx.NewId()
+        self.id_average = wx.NewId()
+        self.id_peak_hold = wx.NewId()
+
+        self.Bind(wx.EVT_MENU, self.on_average, id=self.id_average)
+        self.Bind(wx.EVT_MENU, self.on_peak_hold, id=self.id_peak_hold)
+        self.Bind(wx.EVT_MENU, self.on_incr_ref_level, 
id=self.id_incr_ref_level)
+        self.Bind(wx.EVT_MENU, self.on_decr_ref_level, 
id=self.id_decr_ref_level)
+        self.Bind(wx.EVT_MENU, self.on_incr_y_per_div, 
id=self.id_incr_y_per_div)
+        self.Bind(wx.EVT_MENU, self.on_decr_y_per_div, 
id=self.id_decr_y_per_div)
+        self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_1)
+        self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_2)
+        self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_5)
+        self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_10)
+        self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_20)
+
+
+        # make a menu
+        menu = wx.Menu()
+        self.popup_menu = menu
+        menu.AppendCheckItem(self.id_average, "Average")
+        menu.AppendCheckItem(self.id_peak_hold, "Peak Hold")
+        menu.Append(self.id_incr_ref_level, "Incr Ref Level")
+        menu.Append(self.id_decr_ref_level, "Decr Ref Level")
+        # menu.Append(self.id_incr_y_per_div, "Incr dB/div")
+        # menu.Append(self.id_decr_y_per_div, "Decr dB/div")
+        menu.AppendSeparator()
+        # we'd use RadioItems for these, but they're not supported on Mac
+        menu.AppendCheckItem(self.id_y_per_div_1, "1 dB/div")
+        menu.AppendCheckItem(self.id_y_per_div_2, "2 dB/div")
+        menu.AppendCheckItem(self.id_y_per_div_5, "5 dB/div")
+        menu.AppendCheckItem(self.id_y_per_div_10, "10 dB/div")
+        menu.AppendCheckItem(self.id_y_per_div_20, "20 dB/div")
+
+        self.checkmarks = {
+            self.id_average : lambda : self.fftsink.average,
+            self.id_peak_hold : lambda : self.fftsink.peak_hold,
+            self.id_y_per_div_1 : lambda : self.fftsink.y_per_div == 1,
+            self.id_y_per_div_2 : lambda : self.fftsink.y_per_div == 2,
+            self.id_y_per_div_5 : lambda : self.fftsink.y_per_div == 5,
+            self.id_y_per_div_10 : lambda : self.fftsink.y_per_div == 10,
+            self.id_y_per_div_20 : lambda : self.fftsink.y_per_div == 20,
+            }
+
+
+def next_up(v, seq):
+    """
+    Return the first item in seq that is > v.
+    """
+    for s in seq:
+        if s > v:
+            return s
+    return v
+
+def next_down(v, seq):
+    """
+    Return the last item in seq that is < v.
+    """
+    rseq = list(seq[:])
+    rseq.reverse()
+
+    for s in rseq:
+        if s < v:
+            return s
+    return v
+
+
+# ----------------------------------------------------------------
+# Standalone test app
+# ----------------------------------------------------------------
+
+class test_app_block (stdgui2.std_top_block):
+    def __init__(self, frame, panel, vbox, argv):
+        stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv)
+
+        fft_size = 256
+
+        # build our flow graph
+        input_rate = 20.48e3
+
+        # Generate a complex sinusoid
+        #src1 = gr.sig_source_c (input_rate, gr.GR_SIN_WAVE, 2e3, 1)
+        src1 = gr.sig_source_c (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1)
+
+        # We add these throttle blocks so that this demo doesn't
+        # suck down all the CPU available.  Normally you wouldn't use these.
+        thr1 = gr.throttle(gr.sizeof_gr_complex, input_rate)
+
+        sink1 = fft_sink_c (panel, title="Complex Data", fft_size=fft_size,
+                            sample_rate=input_rate, baseband_freq=100e3,
+                            ref_level=0, y_per_div=20)
+        vbox.Add (sink1.win, 1, wx.EXPAND)
+
+        self.define_component("src1", src1)
+        self.define_component("thr1", thr1)
+        self.define_component("sink1", sink1)
+
+        self.connect ("src1", 0, "thr1", 0)
+        self.connect ("thr1", 0, "sink1", 0)
+
+        #src2 = gr.sig_source_f (input_rate, gr.GR_SIN_WAVE, 2e3, 1)
+        src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1)
+        thr2 = gr.throttle(gr.sizeof_float, input_rate)
+        sink2 = fft_sink_f (panel, title="Real Data", fft_size=fft_size*2,
+                            sample_rate=input_rate, baseband_freq=100e3,
+                            ref_level=0, y_per_div=20)
+        vbox.Add (sink2.win, 1, wx.EXPAND)
+
+        self.define_component("src2", src2)
+        self.define_component("thr2", thr2)
+        self.define_component("sink2", sink2)
+        
+        self.connect ("src2", 0, "thr2", 0)
+        self.connect ("thr2", 0, "sink2", 0)
+
+def main ():
+    app = stdgui2.stdapp (test_app_block,
+                         "FFT Sink Test App")
+    app.MainLoop ()
+
+if __name__ == '__main__':
+    main ()

Copied: gnuradio/trunk/gr-wxgui/src/python/scopesink2.py (from rev 4680, 
gnuradio/branches/developers/jcorgan/est/gr-wxgui/src/python/scopesink2.py)
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/scopesink2.py                            
(rev 0)
+++ gnuradio/trunk/gr-wxgui/src/python/scopesink2.py    2007-03-02 02:49:48 UTC 
(rev 4681)
@@ -0,0 +1,659 @@
+#!/usr/bin/env python
+#
+# Copyright 2003,2004,2006,2007 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+from gnuradio import gr, gru, eng_notation
+from gnuradio.wxgui import stdgui2
+import wx
+import gnuradio.wxgui.plot as plot
+import Numeric
+import threading
+import struct
+
+default_scopesink_size = (640, 240)
+default_v_scale = 1000
+default_frame_decim = gr.prefs().get_long('wxgui', 'frame_decim', 1)
+
+class scope_sink_f(gr.hier_block2):
+    def __init__(self, parent, title='', sample_rate=1,
+                 size=default_scopesink_size, frame_decim=default_frame_decim,
+                 v_scale=default_v_scale, t_scale=None):
+
+        gr.hier_block2.__init__(self, "scope_sink_f",
+                                gr.io_signature(1, 1, gr.sizeof_float),
+                                gr.io_signature(0,0,0))
+
+        msgq = gr.msg_queue(2)         # message queue that holds at most 2 
messages
+        self.guts = gr.oscope_sink_f(sample_rate, msgq)
+        self.define_component("guts", self.guts)
+
+        self.connect("self", 0, "guts", 0)
+
+        self.win = scope_window(win_info (msgq, sample_rate, frame_decim,
+                                          v_scale, t_scale, self.guts, title), 
parent)
+
+    def set_sample_rate(self, sample_rate):
+        self.guts.set_sample_rate(sample_rate)
+        self.win.info.set_sample_rate(sample_rate)
+
+class scope_sink_c(gr.hier_block2):
+    def __init__(self, parent, title='', sample_rate=1,
+                 size=default_scopesink_size, frame_decim=default_frame_decim,
+                 v_scale=default_v_scale, t_scale=None):
+
+        gr.hier_block2.__init__(self, "scope_sink_c",
+                                gr.io_signature(1, 1, gr.sizeof_gr_complex),
+                                gr.io_signature(0,0,0))
+
+        msgq = gr.msg_queue(2)         # message queue that holds at most 2 
messages
+        self.define_component("c2f", gr.complex_to_float())
+        self.guts = gr.oscope_sink_f(sample_rate, msgq)
+        self.define_component("guts", self.guts)
+
+        self.connect("self", 0, "c2f", 0)
+        self.connect("c2f", 0, "guts", 0)
+        self.connect("c2f", 1, "guts", 1)
+        
+        self.win = scope_window(win_info(msgq, sample_rate, frame_decim,
+                                         v_scale, t_scale, self.guts, title), 
parent)
+        
+    def set_sample_rate(self, sample_rate):
+        self.guts.set_sample_rate(sample_rate)
+        self.win.info.set_sample_rate(sample_rate)
+
+# ========================================================================
+
+
+time_base_list = [                      # time / division
+    1.0e-7,   # 100ns / div
+    2.5e-7,
+    5.0e-7,
+    1.0e-6,   #   1us / div
+    2.5e-6,
+    5.0e-6,
+    1.0e-5,   #  10us / div
+    2.5e-5,
+    5.0e-5,
+    1.0e-4,   # 100us / div
+    2.5e-4,
+    5.0e-4,
+    1.0e-3,   #   1ms / div
+    2.5e-3,
+    5.0e-3,
+    1.0e-2,   #  10ms / div
+    2.5e-2,
+    5.0e-2
+    ]
+
+v_scale_list = [ # counts / div, LARGER gains are SMALLER /div, appear EARLIER
+    2.0e-3,   # 2m / div, don't call it V/div it's actually counts/div
+    5.0e-3,
+    1.0e-2,
+    2.0e-2,
+    5.0e-2,
+    1.0e-1,
+    2.0e-1,
+    5.0e-1,
+    1.0e+0,
+    2.0e+0,
+    5.0e+0,
+    1.0e+1,
+    2.0e+1,
+    5.0e+1,
+    1.0e+2,
+    2.0e+2,
+    5.0e+2,
+    1.0e+3,
+    2.0e+3,
+    5.0e+3,
+    1.0e+4 # 10000 /div, USRP full scale is -/+ 32767
+    ]
+
+    
+wxDATA_EVENT = wx.NewEventType()
+
+def EVT_DATA_EVENT(win, func):
+    win.Connect(-1, -1, wxDATA_EVENT, func)
+
+class DataEvent(wx.PyEvent):
+    def __init__(self, data):
+        wx.PyEvent.__init__(self)
+        self.SetEventType (wxDATA_EVENT)
+        self.data = data
+
+    def Clone (self): 
+        self.__class__ (self.GetId())
+
+
+class win_info (object):
+    __slots__ = ['msgq', 'sample_rate', 'frame_decim', 'v_scale', 
+                 'scopesink', 'title',
+                 'time_scale_cursor', 'v_scale_cursor', 'marker', 'xy',
+                 'autorange', 'running']
+
+    def __init__ (self, msgq, sample_rate, frame_decim, v_scale, t_scale,
+                  scopesink, title = "Oscilloscope"):
+        self.msgq = msgq
+        self.sample_rate = sample_rate
+        self.frame_decim = frame_decim
+        self.scopesink = scopesink
+        self.title = title;
+
+        self.time_scale_cursor = gru.seq_with_cursor(time_base_list, 
initial_value = t_scale)
+        self.v_scale_cursor = gru.seq_with_cursor(v_scale_list, initial_value 
= v_scale)
+
+        self.marker = 'line'
+        self.xy = False
+        if v_scale == None:        # 0 and None are both False, but 0 != None
+            self.autorange = True
+        else:
+            self.autorange = False # 0 is a valid v_scale            
+        self.running = True
+
+    def get_time_per_div (self):
+        return self.time_scale_cursor.current ()
+
+    def get_volts_per_div (self):
+        return self.v_scale_cursor.current ()
+
+    def set_sample_rate(self, sample_rate):
+        self.sample_rate = sample_rate
+        
+    def get_sample_rate (self):
+        return self.sample_rate
+
+    def get_decimation_rate (self):
+        return 1.0
+
+    def set_marker (self, s):
+        self.marker = s
+
+    def get_marker (self):
+        return self.marker
+
+
+class input_watcher (threading.Thread):
+    def __init__ (self, msgq, event_receiver, frame_decim, **kwds):
+        threading.Thread.__init__ (self, **kwds)
+        self.setDaemon (1)
+        self.msgq = msgq
+        self.event_receiver = event_receiver
+        self.frame_decim = frame_decim
+        self.iscan = 0
+        self.keep_running = True
+        self.start ()
+
+    def run (self):
+        # print "input_watcher: pid = ", os.getpid ()
+        while (self.keep_running):
+            msg = self.msgq.delete_head()   # blocking read of message queue
+            if self.iscan == 0:            # only display at frame_decim
+                self.iscan = self.frame_decim
+                                
+                nchan = int(msg.arg1())    # number of channels of data in msg
+                nsamples = int(msg.arg2()) # number of samples in each channel
+
+                s = msg.to_string()      # get the body of the msg as a string
+
+                bytes_per_chan = nsamples * gr.sizeof_float
+
+                records = []
+                for ch in range (nchan):
+
+                    start = ch * bytes_per_chan
+                    chan_data = s[start:start+bytes_per_chan]
+                    rec = Numeric.fromstring (chan_data, Numeric.Float32)
+                    records.append (rec)
+
+                # print "nrecords = %d, reclen = %d" % (len (records),nsamples)
+
+                de = DataEvent (records)
+                wx.PostEvent (self.event_receiver, de)
+                records = []
+                del de
+
+            # end if iscan == 0
+            self.iscan -= 1
+    
+
+class scope_window (wx.Panel):
+
+    def __init__ (self, info, parent, id = -1,
+                  pos = wx.DefaultPosition, size = wx.DefaultSize, name = ""):
+        wx.Panel.__init__ (self, parent, -1)
+        self.info = info
+
+        vbox = wx.BoxSizer (wx.VERTICAL)
+
+        self.graph = graph_window (info, self, -1)
+
+        vbox.Add (self.graph, 1, wx.EXPAND)
+        vbox.Add (self.make_control_box(), 0, wx.EXPAND)
+        vbox.Add (self.make_control2_box(), 0, wx.EXPAND)
+
+        self.sizer = vbox
+        self.SetSizer (self.sizer)
+        self.SetAutoLayout (True)
+        self.sizer.Fit (self)
+        self.set_autorange(self.info.autorange)
+        
+
+    # second row of control buttons etc. appears BELOW control_box
+    def make_control2_box (self):
+        ctrlbox = wx.BoxSizer (wx.HORIZONTAL)
+
+        self.inc_v_button = wx.Button (self, 1101, " < ", style=wx.BU_EXACTFIT)
+        self.inc_v_button.SetToolTipString ("Increase vertical range")
+        wx.EVT_BUTTON (self, 1101, self.incr_v_scale) # ID matches button ID 
above
+
+        self.dec_v_button  = wx.Button (self, 1100, " > ", 
style=wx.BU_EXACTFIT)
+        self.dec_v_button.SetToolTipString ("Decrease vertical range")
+        wx.EVT_BUTTON (self, 1100, self.decr_v_scale)
+
+        self.v_scale_label = wx.StaticText (self, 1002, "None") # vertical /div
+        self.update_v_scale_label ()
+
+        self.autorange_checkbox = wx.CheckBox (self, 1102, "Autorange")
+        self.autorange_checkbox.SetToolTipString ("Select autorange on/off")
+        wx.EVT_CHECKBOX(self, 1102, self.autorange_checkbox_event)
+
+        ctrlbox.Add ((5,0) ,0) # left margin space
+        ctrlbox.Add (self.inc_v_button, 0, wx.EXPAND)
+        ctrlbox.Add (self.dec_v_button, 0, wx.EXPAND)
+        ctrlbox.Add (self.v_scale_label, 0, wx.ALIGN_CENTER)
+        ctrlbox.Add ((20,0) ,0) # spacer
+        ctrlbox.Add (self.autorange_checkbox, 0, wx.ALIGN_CENTER)
+
+        return ctrlbox
+
+    def make_control_box (self):
+        ctrlbox = wx.BoxSizer (wx.HORIZONTAL)
+
+        tb_left = wx.Button (self, 1001, " < ", style=wx.BU_EXACTFIT)
+        tb_left.SetToolTipString ("Increase time base")
+        wx.EVT_BUTTON (self, 1001, self.incr_timebase)
+
+
+        tb_right  = wx.Button (self, 1000, " > ", style=wx.BU_EXACTFIT)
+        tb_right.SetToolTipString ("Decrease time base")
+        wx.EVT_BUTTON (self, 1000, self.decr_timebase)
+
+        self.time_base_label = wx.StaticText (self, 1002, "")
+        self.update_timebase_label ()
+
+        ctrlbox.Add ((5,0) ,0)
+        # ctrlbox.Add (wx.StaticText (self, -1, "Horiz Scale: "), 0, 
wx.ALIGN_CENTER)
+        ctrlbox.Add (tb_left, 0, wx.EXPAND)
+        ctrlbox.Add (tb_right, 0, wx.EXPAND)
+        ctrlbox.Add (self.time_base_label, 0, wx.ALIGN_CENTER)
+
+        ctrlbox.Add ((10,0) ,1)            # stretchy space
+
+        ctrlbox.Add (wx.StaticText (self, -1, "Trig: "), 0, wx.ALIGN_CENTER)
+        self.trig_chan_choice = wx.Choice (self, 1004,
+                                           choices = ['Ch1', 'Ch2', 'Ch3', 
'Ch4'])
+        self.trig_chan_choice.SetToolTipString ("Select channel for trigger")
+        wx.EVT_CHOICE (self, 1004, self.trig_chan_choice_event)
+        ctrlbox.Add (self.trig_chan_choice, 0, wx.ALIGN_CENTER)
+
+        self.trig_mode_choice = wx.Choice (self, 1005,
+                                           choices = ['Auto', 'Pos', 'Neg'])
+        self.trig_mode_choice.SetToolTipString ("Select trigger slope or Auto 
(untriggered roll)")
+        wx.EVT_CHOICE (self, 1005, self.trig_mode_choice_event)
+        ctrlbox.Add (self.trig_mode_choice, 0, wx.ALIGN_CENTER)
+
+        trig_level50 = wx.Button (self, 1006, "50%")
+        trig_level50.SetToolTipString ("Set trigger level to 50%")
+        wx.EVT_BUTTON (self, 1006, self.set_trig_level50)
+        ctrlbox.Add (trig_level50, 0, wx.EXPAND)
+
+        run_stop = wx.Button (self, 1007, "Run/Stop")
+        run_stop.SetToolTipString ("Toggle Run/Stop mode")
+        wx.EVT_BUTTON (self, 1007, self.run_stop)
+        ctrlbox.Add (run_stop, 0, wx.EXPAND)
+
+        ctrlbox.Add ((10, 0) ,1)            # stretchy space
+
+        ctrlbox.Add (wx.StaticText (self, -1, "Fmt: "), 0, wx.ALIGN_CENTER)
+        self.marker_choice = wx.Choice (self, 1002, choices = 
self._marker_choices)
+        self.marker_choice.SetToolTipString ("Select plotting with lines, 
pluses or dots")
+        wx.EVT_CHOICE (self, 1002, self.marker_choice_event)
+        ctrlbox.Add (self.marker_choice, 0, wx.ALIGN_CENTER)
+
+        self.xy_choice = wx.Choice (self, 1003, choices = ['X:t', 'X:Y'])
+        self.xy_choice.SetToolTipString ("Select X vs time or X vs Y display")
+        wx.EVT_CHOICE (self, 1003, self.xy_choice_event)
+        ctrlbox.Add (self.xy_choice, 0, wx.ALIGN_CENTER)
+
+        return ctrlbox
+    
+    _marker_choices = ['line', 'plus', 'dot']
+
+    def update_timebase_label (self):
+        time_per_div = self.info.get_time_per_div ()
+        s = ' ' + eng_notation.num_to_str (time_per_div) + 's/div'
+        self.time_base_label.SetLabel (s)
+        
+    def decr_timebase (self, evt):
+        self.info.time_scale_cursor.prev ()
+        self.update_timebase_label ()
+
+    def incr_timebase (self, evt):
+        self.info.time_scale_cursor.next ()
+        self.update_timebase_label ()
+
+    def update_v_scale_label (self):
+        volts_per_div = self.info.get_volts_per_div ()
+        s = ' ' + eng_notation.num_to_str (volts_per_div) + '/div' # Not V/div
+        self.v_scale_label.SetLabel (s)
+        
+    def decr_v_scale (self, evt):
+        self.info.v_scale_cursor.prev ()
+        self.update_v_scale_label ()
+
+    def incr_v_scale (self, evt):
+        self.info.v_scale_cursor.next ()
+        self.update_v_scale_label ()
+        
+    def marker_choice_event (self, evt):
+        s = evt.GetString ()
+        self.set_marker (s)
+
+    def set_autorange(self, on):
+        if on:
+            self.v_scale_label.SetLabel(" (auto)")
+            self.info.autorange = True
+            self.autorange_checkbox.SetValue(True)
+            self.inc_v_button.Enable(False)
+            self.dec_v_button.Enable(False)
+        else:
+            if self.graph.y_range:
+                (l,u) = self.graph.y_range # found by autorange
+                self.info.v_scale_cursor.set_index_by_value((u-l)/8.0)
+            self.update_v_scale_label()
+            self.info.autorange = False
+            self.autorange_checkbox.SetValue(False)
+            self.inc_v_button.Enable(True)
+            self.dec_v_button.Enable(True)
+            
+    def autorange_checkbox_event(self, evt):
+        if evt.Checked():
+            self.set_autorange(True)
+        else:
+            self.set_autorange(False)
+            
+    def set_marker (self, s):
+        self.info.set_marker (s)        # set info for drawing routines
+        i = self.marker_choice.FindString (s)
+        assert i >= 0, "Hmmm, set_marker problem"
+        self.marker_choice.SetSelection (i)
+
+    def set_format_line (self):
+        self.set_marker ('line')
+
+    def set_format_dot (self):
+        self.set_marker ('dot')
+
+    def set_format_plus (self):
+        self.set_marker ('plus')
+        
+    def xy_choice_event (self, evt):
+        s = evt.GetString ()
+        self.info.xy = s == 'X:Y'
+
+    def trig_chan_choice_event (self, evt):
+        s = evt.GetString ()
+        ch = int (s[-1]) - 1
+        self.info.scopesink.set_trigger_channel (ch)
+
+    def trig_mode_choice_event (self, evt):
+        sink = self.info.scopesink
+        s = evt.GetString ()
+        if s == 'Pos':
+            sink.set_trigger_mode (gr.gr_TRIG_POS_SLOPE)
+        elif s == 'Neg':
+            sink.set_trigger_mode (gr.gr_TRIG_NEG_SLOPE)
+        elif s == 'Auto':
+            sink.set_trigger_mode (gr.gr_TRIG_AUTO)
+        else:
+            assert 0, "Bad trig_mode_choice string"
+    
+    def set_trig_level50 (self, evt):
+        self.info.scopesink.set_trigger_level_auto ()
+
+    def run_stop (self, evt):
+        self.info.running = not self.info.running
+        
+
+class graph_window (plot.PlotCanvas):
+
+    channel_colors = ['BLUE', 'RED',
+                      'CYAN', 'MAGENTA', 'GREEN', 'YELLOW']
+    
+    def __init__ (self, info, parent, id = -1,
+                  pos = wx.DefaultPosition, size = (640, 240),
+                  style = wx.DEFAULT_FRAME_STYLE, name = ""):
+        plot.PlotCanvas.__init__ (self, parent, id, pos, size, style, name)
+
+        self.SetXUseScopeTicks (True)
+        self.SetEnableGrid (True)
+        self.SetEnableZoom (True)
+        self.SetEnableLegend(True)
+        # self.SetBackgroundColour ('black')
+        
+        self.info = info;
+        self.y_range = None
+        self.x_range = None
+        self.avg_y_min = None
+        self.avg_y_max = None
+        self.avg_x_min = None
+        self.avg_x_max = None
+
+        EVT_DATA_EVENT (self, self.format_data)
+
+        self.input_watcher = input_watcher (info.msgq, self, info.frame_decim)
+
+    def channel_color (self, ch):
+        return self.channel_colors[ch % len(self.channel_colors)]
+       
+    def format_data (self, evt):
+        if not self.info.running:
+            return
+        
+        if self.info.xy:
+            self.format_xy_data (evt)
+            return
+
+        info = self.info
+        records = evt.data
+        nchannels = len (records)
+        npoints = len (records[0])
+
+        objects = []
+
+        Ts = 1.0 / (info.get_sample_rate () / info.get_decimation_rate ())
+        x_vals = Ts * Numeric.arrayrange (-npoints/2, npoints/2)
+
+        # preliminary clipping based on time axis here, instead of in graphics 
code
+        time_per_window = self.info.get_time_per_div () * 10
+        n = int (time_per_window / Ts + 0.5)
+        n = n & ~0x1                    # make even
+        n = max (2, min (n, npoints))
+
+        self.SetXUseScopeTicks (True)   # use 10 divisions, no labels
+
+        for ch in range(nchannels):
+            r = records[ch]
+
+            # plot middle n points of record
+
+            lb = npoints/2 - n/2
+            ub = npoints/2 + n/2
+            # points = zip (x_vals[lb:ub], r[lb:ub])
+            points = Numeric.zeros ((ub-lb, 2), Numeric.Float64)
+            points[:,0] = x_vals[lb:ub]
+            points[:,1] = r[lb:ub]
+
+            m = info.get_marker ()
+            if m == 'line':
+                objects.append (plot.PolyLine (points,
+                                               colour=self.channel_color (ch),
+                                               legend=('Ch%d' % (ch+1,))))
+            else:
+                objects.append (plot.PolyMarker (points,
+                                                 marker=m,
+                                                 colour=self.channel_color 
(ch),
+                                                 legend=('Ch%d' % (ch+1,))))
+
+        graphics = plot.PlotGraphics (objects,
+                                      title=self.info.title,
+                                      xLabel = '', yLabel = '')
+
+        time_per_div = info.get_time_per_div ()
+        x_range = (-5.0 * time_per_div, 5.0 * time_per_div) # ranges are 
tuples!
+        volts_per_div = info.get_volts_per_div ()
+        if not self.info.autorange:
+            self.y_range = (-4.0 * volts_per_div, 4.0 * volts_per_div)
+        self.Draw (graphics, xAxis=x_range, yAxis=self.y_range)
+        self.update_y_range () # autorange to self.y_range
+
+
+    def format_xy_data (self, evt):
+        info = self.info
+        records = evt.data
+        nchannels = len (records)
+        npoints = len (records[0])
+
+        if nchannels < 2:
+            return
+
+        objects = []
+        # points = zip (records[0], records[1])
+        points = Numeric.zeros ((len(records[0]), 2), Numeric.Float32)
+        points[:,0] = records[0]
+        points[:,1] = records[1]
+        
+        self.SetXUseScopeTicks (False)
+
+        m = info.get_marker ()
+        if m == 'line':
+            objects.append (plot.PolyLine (points,
+                                           colour=self.channel_color (0)))
+        else:
+            objects.append (plot.PolyMarker (points,
+                                             marker=m,
+                                             colour=self.channel_color (0)))
+
+        graphics = plot.PlotGraphics (objects,
+                                      title=self.info.title,
+                                      xLabel = 'I', yLabel = 'Q')
+
+        self.Draw (graphics, xAxis=self.x_range, yAxis=self.y_range)
+        self.update_y_range ()
+        self.update_x_range ()
+
+
+    def update_y_range (self):
+        alpha = 1.0/25
+        graphics = self.last_draw[0]
+        p1, p2 = graphics.boundingBox ()     # min, max points of graphics
+
+        if self.avg_y_min: # prevent vertical scale from jumping abruptly --?
+            self.avg_y_min = p1[1] * alpha + self.avg_y_min * (1 - alpha)
+            self.avg_y_max = p2[1] * alpha + self.avg_y_max * (1 - alpha)
+        else: # initial guess
+            self.avg_y_min = p1[1] # -500.0 workaround, sometimes p1 is ~ 10^35
+            self.avg_y_max = p2[1] # 500.0
+
+        self.y_range = self._axisInterval ('auto', self.avg_y_min, 
self.avg_y_max)
+        # print "p1 %s  p2 %s  y_min %s  y_max %s  y_range %s" \
+        #        % (p1, p2, self.avg_y_min, self.avg_y_max, self.y_range)
+
+
+    def update_x_range (self):
+        alpha = 1.0/25
+        graphics = self.last_draw[0]
+        p1, p2 = graphics.boundingBox ()     # min, max points of graphics
+
+        if self.avg_x_min:
+            self.avg_x_min = p1[0] * alpha + self.avg_x_min * (1 - alpha)
+            self.avg_x_max = p2[0] * alpha + self.avg_x_max * (1 - alpha)
+        else:
+            self.avg_x_min = p1[0]
+            self.avg_x_max = p2[0]
+
+        self.x_range = self._axisInterval ('auto', self.avg_x_min, 
self.avg_x_max)
+
+
+# ----------------------------------------------------------------
+# Stand-alone test application
+# ----------------------------------------------------------------
+
+class test_top_block (stdgui2.std_top_block):
+    def __init__(self, frame, panel, vbox, argv):
+        stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv)
+
+        if len(argv) > 1:
+            frame_decim = int(argv[1]) 
+        else:
+            frame_decim = 1
+
+        if len(argv) > 2:
+            v_scale = float(argv[2])  # start up at this v_scale value
+        else:
+            v_scale = None  # start up in autorange mode, default
+
+        if len(argv) > 3:
+            t_scale = float(argv[3])  # start up at this t_scale value
+        else:
+            t_scale = None  # old behavior
+
+        print "frame decim %s  v_scale %s  t_scale %s" % 
(frame_decim,v_scale,t_scale)
+            
+        input_rate = 1e6
+
+        # Generate a complex sinusoid
+        self.define_component("src0", gr.sig_source_c (input_rate, 
gr.GR_SIN_WAVE, 25.1e3, 1e3))
+
+        # We add this throttle block so that this demo doesn't suck down
+        # all the CPU available.  You normally wouldn't use it...
+        self.define_component("throttle", gr.throttle(gr.sizeof_gr_complex, 
input_rate))
+
+        scope = scope_sink_c (panel,"Secret Data",sample_rate=input_rate,
+                              frame_decim=frame_decim,
+                              v_scale=v_scale, t_scale=t_scale)
+        self.define_component("scope", scope)
+        vbox.Add (scope.win, 1, wx.EXPAND)
+
+        # Ultimately this will be
+        # self.connect("src0 throttle scope")
+        self.connect("src0", 0, "throttle", 0)
+        self.connect("throttle", 0, "scope", 0)
+
+
+def main ():
+    app = stdgui2.stdapp (test_top_block, "O'Scope Test App")
+    app.MainLoop ()
+
+if __name__ == '__main__':
+    main ()
+
+# ----------------------------------------------------------------

Copied: gnuradio/trunk/gr-wxgui/src/python/stdgui2.py (from rev 4680, 
gnuradio/branches/developers/jcorgan/est/gr-wxgui/src/python/stdgui2.py)
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/stdgui2.py                               
(rev 0)
+++ gnuradio/trunk/gr-wxgui/src/python/stdgui2.py       2007-03-02 02:49:48 UTC 
(rev 4681)
@@ -0,0 +1,95 @@
+#
+# Copyright 2004 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+'''A simple wx gui for GNU Radio applications'''
+
+import wx
+import sys
+from gnuradio import gr
+
+
+class stdapp (wx.App):
+    def __init__ (self, top_block_maker, title="GNU Radio", nstatus=2):
+        self.top_block_maker = top_block_maker
+        self.title = title
+        self._nstatus = nstatus
+        # All our initialization must come before calling wx.App.__init__.
+        # OnInit is called from somewhere in the guts of __init__.
+        wx.App.__init__ (self, redirect=False)
+
+    def OnInit (self):
+        frame = stdframe (self.top_block_maker, self.title, self._nstatus)
+        frame.Show (True)
+        self.SetTopWindow (frame)
+        return True
+
+
+class stdframe (wx.Frame):
+    def __init__ (self, top_block_maker, title="GNU Radio", nstatus=2):
+        # print "stdframe.__init__"
+        wx.Frame.__init__(self, None, -1, title)
+
+        self.CreateStatusBar (nstatus)
+        mainmenu = wx.MenuBar ()
+
+        menu = wx.Menu ()
+        item = menu.Append (200, 'E&xit', 'Exit')
+        self.Bind (wx.EVT_MENU, self.OnCloseWindow, item)
+        mainmenu.Append (menu, "&File")
+        self.SetMenuBar (mainmenu)
+
+        self.Bind (wx.EVT_CLOSE, self.OnCloseWindow)
+        self.panel = stdpanel (self, self, top_block_maker)
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        vbox.Add(self.panel, 1, wx.EXPAND)
+        self.SetSizer(vbox)
+        self.SetAutoLayout(True)
+        vbox.Fit(self)
+
+    def OnCloseWindow (self, event):
+        self.runtime().stop()
+        self.Destroy ()
+
+    def runtime (self):
+        return self.panel.runtime
+    
+class stdpanel (wx.Panel):
+    def __init__ (self, parent, frame, top_block_maker):
+        # print "stdpanel.__init__"
+        wx.Panel.__init__ (self, parent, -1)
+        self.frame = frame
+
+        vbox = wx.BoxSizer (wx.VERTICAL)
+        self.top_block = top_block_maker (frame, self, vbox, sys.argv)
+        self.SetSizer (vbox)
+        self.SetAutoLayout (True)
+        vbox.Fit (self)
+
+        self.runtime = gr.runtime(self.top_block)
+        self.runtime.start ()
+
+class std_top_block (gr.hier_block2):
+    def __init__ (self, parent, panel, vbox, argv):
+        # Call the hier_block2 constructor
+        # Top blocks have no inputs and outputs
+        gr.hier_block2.__init__(self, "std_top_block",
+                                gr.io_signature(0,0,0),
+                                gr.io_signature(0,0,0))

Copied: gnuradio/trunk/gr-wxgui/src/python/waterfallsink2.py (from rev 4680, 
gnuradio/branches/developers/jcorgan/est/gr-wxgui/src/python/waterfallsink2.py)
===================================================================
--- gnuradio/trunk/gr-wxgui/src/python/waterfallsink2.py                        
        (rev 0)
+++ gnuradio/trunk/gr-wxgui/src/python/waterfallsink2.py        2007-03-02 
02:49:48 UTC (rev 4681)
@@ -0,0 +1,489 @@
+#!/usr/bin/env python
+#
+# Copyright 2003,2004,2005,2007 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+from gnuradio import gr, gru, window
+from gnuradio.wxgui import stdgui2
+import wx
+import gnuradio.wxgui.plot as plot
+import Numeric
+import os
+import threading
+import math    
+
+default_fftsink_size = (640,240)
+default_fft_rate = gr.prefs().get_long('wxgui', 'fft_rate', 15)
+
+class waterfall_sink_base(object):
+    def __init__(self, input_is_real=False, baseband_freq=0,
+                 sample_rate=1, fft_size=512,
+                 fft_rate=default_fft_rate,
+                 average=False, avg_alpha=None, title=''):
+
+        # initialize common attributes
+        self.baseband_freq = baseband_freq
+        self.sample_rate = sample_rate
+        self.fft_size = fft_size
+        self.fft_rate = fft_rate
+        self.average = average
+        if avg_alpha is None:
+            self.avg_alpha = 2.0 / fft_rate
+        else:
+            self.avg_alpha = avg_alpha
+        self.title = title
+        self.input_is_real = input_is_real
+        self.msgq = gr.msg_queue(2)         # queue up to 2 messages
+
+    def set_average(self, average):
+        self.average = average
+        if average:
+            self.avg.set_taps(self.avg_alpha)
+        else:
+            self.avg.set_taps(1.0)
+
+    def set_avg_alpha(self, avg_alpha):
+        self.avg_alpha = avg_alpha
+
+    def set_baseband_freq(self, baseband_freq):
+        self.baseband_freq = baseband_freq
+
+    def set_sample_rate(self, sample_rate):
+        self.sample_rate = sample_rate
+        self._set_n()
+
+    def _set_n(self):
+        self.one_in_n.set_n(max(1, 
int(self.sample_rate/self.fft_size/self.fft_rate)))
+        
+class waterfall_sink_f(gr.hier_block2, waterfall_sink_base):
+    def __init__(self, parent, baseband_freq=0,
+                 y_per_div=10, ref_level=50, sample_rate=1, fft_size=512,
+                 fft_rate=default_fft_rate, average=False, avg_alpha=None,
+                 title='', size=default_fftsink_size):
+
+        gr.hier_block2.__init__(self, "waterfall_sink_f",
+                                gr.io_signature(1, 1, gr.sizeof_float),
+                                gr.io_signature(0,0,0))
+
+        waterfall_sink_base.__init__(self, input_is_real=True, 
baseband_freq=baseband_freq,
+                               sample_rate=sample_rate, fft_size=fft_size,
+                               fft_rate=fft_rate,
+                               average=average, avg_alpha=avg_alpha, 
title=title)
+                               
+        self.define_component("s2p", gr.serial_to_parallel(gr.sizeof_float, 
self.fft_size))
+        self.one_in_n = gr.keep_one_in_n(gr.sizeof_float * self.fft_size,
+                                         max(1, 
int(self.sample_rate/self.fft_size/self.fft_rate)))
+        self.define_component("one_in_n", self.one_in_n)
+        
+        mywindow = window.blackmanharris(self.fft_size)
+        self.define_component("fft", gr.fft_vfc(self.fft_size, True, mywindow))
+        self.define_component("c2mag", gr.complex_to_mag(self.fft_size))
+        self.avg = gr.single_pole_iir_filter_ff(1.0, self.fft_size)
+        self.define_component("avg", self.avg)
+        self.define_component("log", gr.nlog10_ff(20, self.fft_size, 
-20*math.log10(self.fft_size)))
+        self.define_component("sink", gr.message_sink(gr.sizeof_float * 
self.fft_size, self.msgq, True))
+
+        # Ultimately this will be
+        # self.connect("self s2p one_in_n fft c2mag avg log sink")
+        self.connect("self", 0, "s2p", 0)
+        self.connect("s2p", 0, "one_in_n", 0)
+        self.connect("one_in_n", 0, "fft", 0)
+        self.connect("fft", 0, "c2mag", 0)
+        self.connect("c2mag", 0, "avg", 0)
+        self.connect("avg", 0, "log", 0)
+        self.connect("log", 0, "sink", 0)
+
+        self.win = waterfall_window(self, parent, size=size)
+        self.set_average(self.average)
+
+
+class waterfall_sink_c(gr.hier_block2, waterfall_sink_base):
+    def __init__(self, parent, baseband_freq=0,
+                 y_per_div=10, ref_level=50, sample_rate=1, fft_size=512,
+                 fft_rate=default_fft_rate, average=False, avg_alpha=None, 
+                 title='', size=default_fftsink_size):
+
+        gr.hier_block2.__init__(self, "waterfall_sink_f",
+                                gr.io_signature(1, 1, gr.sizeof_gr_complex),
+                                gr.io_signature(0,0,0))
+
+        waterfall_sink_base.__init__(self, input_is_real=False, 
baseband_freq=baseband_freq,
+                                     sample_rate=sample_rate, 
fft_size=fft_size,
+                                     fft_rate=fft_rate,
+                                     average=average, avg_alpha=avg_alpha, 
title=title)
+
+        self.define_component("s2p", 
gr.serial_to_parallel(gr.sizeof_gr_complex, self.fft_size))
+        self.one_in_n = gr.keep_one_in_n(gr.sizeof_gr_complex * self.fft_size,
+                                         max(1, 
int(self.sample_rate/self.fft_size/self.fft_rate)))
+        self.define_component("one_in_n", self.one_in_n)
+        
+        mywindow = window.blackmanharris(self.fft_size)
+        self.define_component("fft", gr.fft_vcc(self.fft_size, True, mywindow))
+        self.define_component("c2mag", gr.complex_to_mag(self.fft_size))
+        self.avg = gr.single_pole_iir_filter_ff(1.0, self.fft_size)
+        self.define_component("avg", self.avg)
+        self.define_component("log", gr.nlog10_ff(20, self.fft_size, 
-20*math.log10(self.fft_size)))
+        self.define_component("sink", gr.message_sink(gr.sizeof_float * 
self.fft_size, self.msgq, True))
+
+        # Ultimately this will be
+        # self.connect("self s2p one_in_n fft c2mag avg log sink")
+        self.connect("self", 0, "s2p", 0)
+        self.connect("s2p", 0, "one_in_n", 0)
+        self.connect("one_in_n", 0, "fft", 0)
+        self.connect("fft", 0, "c2mag", 0)
+        self.connect("c2mag", 0, "avg", 0)
+        self.connect("avg", 0, "log", 0)
+        self.connect("log", 0, "sink", 0)
+
+        self.win = waterfall_window(self, parent, size=size)
+        self.set_average(self.average)
+
+
+# ------------------------------------------------------------------------
+
+myDATA_EVENT = wx.NewEventType()
+EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0)
+
+
+class DataEvent(wx.PyEvent):
+    def __init__(self, data):
+        wx.PyEvent.__init__(self)
+        self.SetEventType (myDATA_EVENT)
+        self.data = data
+
+    def Clone (self): 
+        self.__class__ (self.GetId())
+
+
+class input_watcher (threading.Thread):
+    def __init__ (self, msgq, fft_size, event_receiver, **kwds):
+        threading.Thread.__init__ (self, **kwds)
+        self.setDaemon (1)
+        self.msgq = msgq
+        self.fft_size = fft_size
+        self.event_receiver = event_receiver
+        self.keep_running = True
+        self.start ()
+
+    def run (self):
+        while (self.keep_running):
+            msg = self.msgq.delete_head()  # blocking read of message queue
+            itemsize = int(msg.arg1())
+            nitems = int(msg.arg2())
+
+            s = msg.to_string()            # get the body of the msg as a 
string
+
+            # There may be more than one FFT frame in the message.
+            # If so, we take only the last one
+            if nitems > 1:
+                start = itemsize * (nitems - 1)
+                s = s[start:start+itemsize]
+
+            complex_data = Numeric.fromstring (s, Numeric.Float32)
+            de = DataEvent (complex_data)
+            wx.PostEvent (self.event_receiver, de)
+            del de
+    
+
+class waterfall_window (wx.Panel):
+    def __init__ (self, fftsink, parent, id = -1,
+                  pos = wx.DefaultPosition, size = wx.DefaultSize,
+                  style = wx.DEFAULT_FRAME_STYLE, name = ""):
+        wx.Panel.__init__(self, parent, id, pos, size, style, name)
+
+        self.fftsink = fftsink
+        self.bm = wx.EmptyBitmap(self.fftsink.fft_size, 300, -1)
+
+        self.scale_factor = 5.0           # FIXME should autoscale, or set this
+        
+        dc1 = wx.MemoryDC()
+        dc1.SelectObject(self.bm)
+        dc1.Clear()
+
+        self.pens = self.make_pens()
+
+        wx.EVT_PAINT( self, self.OnPaint )
+        wx.EVT_CLOSE (self, self.on_close_window)
+        EVT_DATA_EVENT (self, self.set_data)
+        
+        self.build_popup_menu()
+        
+        wx.EVT_CLOSE (self, self.on_close_window)
+        self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
+
+        self.input_watcher = input_watcher(fftsink.msgq, fftsink.fft_size, 
self)
+
+
+    def on_close_window (self, event):
+        print "waterfall_window: on_close_window"
+        self.keep_running = False
+
+    def const_list(self,const,len):
+        return [const] * len
+
+    def make_colormap(self):
+        r = []
+        r.extend(self.const_list(0,96))
+        r.extend(range(0,255,4))
+        r.extend(self.const_list(255,64))
+        r.extend(range(255,128,-4))
+        
+        g = []
+        g.extend(self.const_list(0,32))
+        g.extend(range(0,255,4))
+        g.extend(self.const_list(255,64))
+        g.extend(range(255,0,-4))
+        g.extend(self.const_list(0,32))
+        
+        b = range(128,255,4)
+        b.extend(self.const_list(255,64))
+        b.extend(range(255,0,-4))
+        b.extend(self.const_list(0,96))
+        return (r,g,b)
+
+    def make_pens(self):
+        (r,g,b) = self.make_colormap()
+        pens = []
+        for i in range(0,256):
+            colour = wx.Colour(r[i], g[i], b[i])
+            pens.append( wx.Pen(colour, 2, wx.SOLID))
+        return pens
+        
+    def OnPaint(self, event):
+        dc = wx.PaintDC(self)
+        self.DoDrawing(dc)
+
+    def DoDrawing(self, dc=None):
+        if dc is None:
+            dc = wx.ClientDC(self)
+        dc.DrawBitmap(self.bm, 0, 0, False )
+    
+
+    def const_list(self,const,len):
+        a = [const]
+        for i in range(1,len):
+            a.append(const)
+        return a
+
+    def make_colormap(self):
+        r = []
+        r.extend(self.const_list(0,96))
+        r.extend(range(0,255,4))
+        r.extend(self.const_list(255,64))
+        r.extend(range(255,128,-4))
+        
+        g = []
+        g.extend(self.const_list(0,32))
+        g.extend(range(0,255,4))
+        g.extend(self.const_list(255,64))
+        g.extend(range(255,0,-4))
+        g.extend(self.const_list(0,32))
+        
+        b = range(128,255,4)
+        b.extend(self.const_list(255,64))
+        b.extend(range(255,0,-4))
+        b.extend(self.const_list(0,96))
+        return (r,g,b)
+
+    def set_data (self, evt):
+        dB = evt.data
+        L = len (dB)
+
+        dc1 = wx.MemoryDC()
+        dc1.SelectObject(self.bm)
+        dc1.Blit(0,1,self.fftsink.fft_size,300,dc1,0,0,wx.COPY,False,-1,-1)
+
+        x = max(abs(self.fftsink.sample_rate), abs(self.fftsink.baseband_freq))
+        if x >= 1e9:
+            sf = 1e-9
+            units = "GHz"
+        elif x >= 1e6:
+            sf = 1e-6
+            units = "MHz"
+        else:
+            sf = 1e-3
+            units = "kHz"
+
+
+        if self.fftsink.input_is_real:     # only plot 1/2 the points
+            d_max = L/2
+            p_width = 2
+        else:
+            d_max = L/2
+            p_width = 1
+
+        scale_factor = self.scale_factor
+        if self.fftsink.input_is_real:     # real fft
+           for x_pos in range(0, d_max):
+               value = int(dB[x_pos] * scale_factor)
+               value = min(255, max(0, value))
+               dc1.SetPen(self.pens[value])
+               dc1.DrawRectangle(x_pos*p_width, 0, p_width, 1) 
+        else:                               # complex fft
+           for x_pos in range(0, d_max):    # positive freqs
+               value = int(dB[x_pos] * scale_factor)
+               value = min(255, max(0, value))
+               dc1.SetPen(self.pens[value])
+               dc1.DrawRectangle(x_pos*p_width + d_max, 0, p_width, 1) 
+           for x_pos in range(0 , d_max):   # negative freqs
+               value = int(dB[x_pos+d_max] * scale_factor)
+               value = min(255, max(0, value))
+               dc1.SetPen(self.pens[value])
+               dc1.DrawRectangle(x_pos*p_width, 0, p_width, 1) 
+
+        self.DoDrawing (None)
+
+    def on_average(self, evt):
+        # print "on_average"
+        self.fftsink.set_average(evt.IsChecked())
+
+    def on_right_click(self, event):
+        menu = self.popup_menu
+        for id, pred in self.checkmarks.items():
+            item = menu.FindItemById(id)
+            item.Check(pred())
+        self.PopupMenu(menu, event.GetPosition())
+
+
+    def build_popup_menu(self):
+        self.id_incr_ref_level = wx.NewId()
+        self.id_decr_ref_level = wx.NewId()
+        self.id_incr_y_per_div = wx.NewId()
+        self.id_decr_y_per_div = wx.NewId()
+        self.id_y_per_div_1 = wx.NewId()
+        self.id_y_per_div_2 = wx.NewId()
+        self.id_y_per_div_5 = wx.NewId()
+        self.id_y_per_div_10 = wx.NewId()
+        self.id_y_per_div_20 = wx.NewId()
+        self.id_average = wx.NewId()
+
+        self.Bind(wx.EVT_MENU, self.on_average, id=self.id_average)
+        #self.Bind(wx.EVT_MENU, self.on_incr_ref_level, 
id=self.id_incr_ref_level)
+        #self.Bind(wx.EVT_MENU, self.on_decr_ref_level, 
id=self.id_decr_ref_level)
+        #self.Bind(wx.EVT_MENU, self.on_incr_y_per_div, 
id=self.id_incr_y_per_div)
+        #self.Bind(wx.EVT_MENU, self.on_decr_y_per_div, 
id=self.id_decr_y_per_div)
+        #self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_1)
+        #self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_2)
+        #self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_5)
+        #self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_10)
+        #self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_20)
+
+
+        # make a menu
+        menu = wx.Menu()
+        self.popup_menu = menu
+        menu.AppendCheckItem(self.id_average, "Average")
+        # menu.Append(self.id_incr_ref_level, "Incr Ref Level")
+        # menu.Append(self.id_decr_ref_level, "Decr Ref Level")
+        # menu.Append(self.id_incr_y_per_div, "Incr dB/div")
+        # menu.Append(self.id_decr_y_per_div, "Decr dB/div")
+        # menu.AppendSeparator()
+        # we'd use RadioItems for these, but they're not supported on Mac
+        #menu.AppendCheckItem(self.id_y_per_div_1, "1 dB/div")
+        #menu.AppendCheckItem(self.id_y_per_div_2, "2 dB/div")
+        #menu.AppendCheckItem(self.id_y_per_div_5, "5 dB/div")
+        #menu.AppendCheckItem(self.id_y_per_div_10, "10 dB/div")
+        #menu.AppendCheckItem(self.id_y_per_div_20, "20 dB/div")
+
+        self.checkmarks = {
+            self.id_average : lambda : self.fftsink.average
+            #self.id_y_per_div_1 : lambda : self.fftsink.y_per_div == 1,
+            #self.id_y_per_div_2 : lambda : self.fftsink.y_per_div == 2,
+            #self.id_y_per_div_5 : lambda : self.fftsink.y_per_div == 5,
+            #self.id_y_per_div_10 : lambda : self.fftsink.y_per_div == 10,
+            #self.id_y_per_div_20 : lambda : self.fftsink.y_per_div == 20,
+            }
+
+
+def next_up(v, seq):
+    """
+    Return the first item in seq that is > v.
+    """
+    for s in seq:
+        if s > v:
+            return s
+    return v
+
+def next_down(v, seq):
+    """
+    Return the last item in seq that is < v.
+    """
+    rseq = list(seq[:])
+    rseq.reverse()
+
+    for s in rseq:
+        if s < v:
+            return s
+    return v
+
+
+# ----------------------------------------------------------------
+# Standalone test app
+# ----------------------------------------------------------------
+
+class test_top_block (stdgui2.std_top_block):
+    def __init__(self, frame, panel, vbox, argv):
+        stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv)
+
+        fft_size = 512
+
+        # build our flow graph
+        input_rate = 20.000e3
+
+        # Generate a complex sinusoid
+        self.define_component("src1", gr.sig_source_c (input_rate, 
gr.GR_SIN_WAVE, 5.75e3, 1000))
+        #src1 = gr.sig_source_c (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1000)
+
+        # We add these throttle blocks so that this demo doesn't
+        # suck down all the CPU available.  Normally you wouldn't use these.
+        self.define_component("thr1", gr.throttle(gr.sizeof_gr_complex, 
input_rate))
+
+        sink1 = waterfall_sink_c (self, panel, title="Complex Data", 
fft_size=fft_size,
+                                  sample_rate=input_rate, baseband_freq=100e3)
+        self.define_component("sink1", sink1)
+
+        vbox.Add (sink1.win, 1, wx.EXPAND)
+
+        # Ultimately this will be
+        # self.connect("src1 thr1 sink1")
+        self.connect("src1", 0, "thr1", 0)
+        self.connect("thr1", 0, "sink1", 0)
+
+        # generate a real sinusoid
+        self.define_component("src2", gr.sig_source_f (input_rate, 
gr.GR_SIN_WAVE, 5.75e3, 1000))
+        #src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1000)
+        self.define_component("thr2", gr.throttle(gr.sizeof_float, input_rate))
+        sink2 = waterfall_sink_f (self, panel, title="Real Data", 
fft_size=fft_size,
+                                  sample_rate=input_rate, baseband_freq=100e3)
+        self.define_component("sink2", sink2)
+        vbox.Add (sink2.win, 1, wx.EXPAND)
+
+        # Ultimately this will be
+        # self.connect("src2 thr2 sink2")
+        self.connect("src2", 0, "thr2", 0)
+        self.connect("thr2", 0, "sink2", 0)
+
+def main ():
+    app = stdgui2.stdapp (test_top_block,
+                         "Waterfall Sink Test App")
+    app.MainLoop ()
+
+if __name__ == '__main__':
+    main ()





reply via email to

[Prev in Thread] Current Thread [Next in Thread]