discuss-gnuradio
[Top][All Lists]
Advanced

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

[Discuss-gnuradio] Object model for stdgui2


From: Paul Mathews
Subject: [Discuss-gnuradio] Object model for stdgui2
Date: Wed, 14 Jan 2009 11:12:29 -0800

I have my '900 MHz AM Receiver with AFC' running satisfactorily, but I'm not
happy with some of the approaches I ended up using to accomplish the AFC. I
spliced together most of fftsink2.py and usrp_am_rcvr.py and modified both
as follows:

A. fftsink2.py
Added code to input_watcher class run method (thread):
1)find highest peak in FFT data arriving via msgq
2)calculate the frequency difference between nominal tuning freq and the
peak, i.e., tuning error or drift.
3)calculate new tune freq and retune the usrp 

B. usrp_am_rcvr.py:
Adjusted filter parameters to my requirements.
Added staticmethod access to its topblock class 'set_freq' method.
Added global variables to contain information about the single instance of
this class that is invoked by __main__.

What I don't like about the above is using staticmethod to get access to the
set_freq method of the top block. However, I have not figured out how to
address the single instance of the topblock that exists. Considering the
code snippets below, how can I address the instance of wm_rx_block (which I
referred to as 'top block' earlier) that gets passed to stdgui2?

A little more description of the app:
3 stdgui2 FFTs are in view: FFT of USRP data, FFT of channel filter output,
FFT of audio
Channel filter output is supposed to have a single peak centered on 0
frequency. Any difference can be used to adjust usrp tuning freq. In
practice Thread-2 handles channel filter data and knows the freq offset. As
usual, the usrp object is bound to a topblock, an instance of class
wam_rx_block. What I don't understand is how to address this instance. You
might be able to tell me by simply looking at the __main__ code below.

I'm sure that you longtime Python and gnuradio folks will consider this
trivial, but I have a new bald spot. Any hints will be appreciated.
Paul Mathews
---------------------------------------------------------------------------

Here's __main__

if __name__ == '__main__':
    app = stdgui2.stdapp (wam_rx_block, "USRP 915MHz AM ISM RX")
    app.MainLoop ()

And here are the most important bits of wam_rx_block:

class wam_rx_block (stdgui2.std_top_block):

    my_usrp = None
    my_usrp_subdev = None
    
    def __init__(self,frame,panel,vbox,argv):
        global target_freq, tune_freq, my_usrp, my_usrp_subdev

        < option parsing and other standard setup stuff snipped >

        tune_freq = self.freq
        target_freq = tune_freq
        my_IF_freq = self.IF_freq    # allow class method access

        ################# Build main flowgraph ##################

        #TODO: add an AGC after the channel filter and before the AM_demod
        
        self.u = usrp.source_c()                    # usrp is data source
        my_usrp = self.u                            # allow global access

        < more standard setup stuff snipped >

        self.u.set_mux(usrp.determine_rx_mux_value(self.u,
                                                   options.rx_subdev_spec))
        self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec)
        my_usrp_subdev = self.subdev

        < wx GUI setup stuff snipped >


    def set_freq(self, usrp_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 = usrp.tune(self.u, 0, self.subdev, usrp_freq + self.IF_freq)
        #TODO: check if db is inverting the spectrum or not to decide
        # if we should do + self.IF_freq  or - self.IF_freq

        return r

    def set_freq_static(usrp_freq):
        global target_freq, tune_freq, my_usrp, my_IF_freq, my_usrp_subdev

        r = usrp.tune(my_usrp, 0, my_usrp_subdev, usrp_freq + my_IF_freq)

    set_freq_static = staticmethod(set_freq_static)

----------------------------------------------------------------------------

And, here's the modified run method for input_watcher in fftsink2, with the
call to set_freq_static near the end:

    def run (self):
        global target_freq, tune_freq

        while (self.keep_running):
            msg = self.msgq.delete_head()  # blocking read of message queue
            # FFT results are prefixed by vector size and number of frames.
            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]
                
            # String actually contains f.p. numbers, so convert.
            complex_data = fromstring (s, float32)

            # find location and amplitude of highest peak
            self.maxval = max(complex_data)
            self.pk_bin = argmax(complex_data)
            self.avgval = mean(complex_data)

            self.cycles_per_bin = self.sample_rate / self.fft_size

            # Show size of packet, max value, index of max, and mean value.
            # decide if peak is significant
            # FIXME: make threshold a parameter and/or dynamic
            if ((self.maxval - self.avgval) >= 8): 
                # 1st vlen/2 bins get peak when center_freq is low,
                # with offset maximum = 1/2 of BW at bin vlen/2
                # 2nd vlen/2 bins get peak when center_freq is high,
                # with offset maximum = 1/2 of BW at bin vlen/2
                # Both halves repeat for freq diffs > FFT width / 2.
                # For usrp_rate = 64 MHz / 256 = 250 kHz, do bin (vlen/2)
                # represents f = -125 kHz, +125 kHz,...
                # If we restrict the range of center_freq to within BW/2
                # of carrier, there can be no ambiguity.
                # Given a peak located at bin N, solve for frequency:
                #      cycles_per_bin = usrp_rate/vlen,
                # This can be refined by calculating a mean pk_freq value
                # based on several bins,
                # starting with the values on either side of the peak:
                if self.pk_bin > 0:
                    left_bin = self.pk_bin - 1
                else:
                    left_bin = (self.fft_size - 1)
                if self.pk_bin < (self.fft_size - 2):
                    right_bin = self.pk_bin + 1
                else:
                    right_bin = 0
                left_val = complex_data[left_bin]
                right_val = complex_data[right_bin]
                # Calculate a weighted average bin location starting with
                # the fraction of a bin to offset the center. 
                wtd_sum = (right_val - left_val) / (left_val + right_val)
                self.pk_bin += wtd_sum
                # Adjust for bottom wraparound
                if (self.pk_bin < 0): self.pk_bin += (nitems - 1)
                # Adjust for top wraparound
                if (self.pk_bin > (self.fft_size - 1)):
                    self.pk_bin += (1 - self.fft_size)
                # Calculate equivalent freq of bin, accounting for spectral
                # folding around center.
                if self.pk_bin < self.fft_size / 2:
                    self.pk_freq = int(self.center_freq +
                                       (self.pk_bin * self.cycles_per_bin))
                else:
                    self.pk_freq = int(self.center_freq +
                                       ((self.pk_bin - (self.fft_size - 1))
*
                                        self.cycles_per_bin))
                    
                ################### RETUNING OCCURS HERE:
####################
                # NOTE: For the passband data, pk_freq represents freq
drift.
                if self.afc:
                    if abs(self.pk_freq) > 200:
                        target_freq = tune_freq + self.pk_freq/3     #
correct for drift
                        print "retune:", self.pk_freq, "to", target_freq
                        wam_rx_block.set_freq_static(target_freq)
 
################################################################

            # package the data as a wx event object
            de = DataEvent (complex_data)
            # package this packet as a data event within wx and
            # push this single fft plot data packet into fftsink msgq
            wx.PostEvent (self.event_receiver, de)
            del de # no further need for this copy of data

        





reply via email to

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