root/vtcross/branches/sriram/receive_path.py @ 354

Revision 354, 16.5 KB (checked in by sriram, 15 years ago)

Printing individual power values...checking stats

Line 
1#!/usr/bin/env python
2#
3# Copyright 2005,2006,2007,2009 Free Software Foundation, Inc.
4#
5# This file is part of GNU Radio
6#
7# GNU Radio is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 3, or (at your option)
10# any later version.
11#
12# GNU Radio is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with GNU Radio; see the file COPYING.  If not, write to
19# the Free Software Foundation, Inc., 51 Franklin Street,
20# Boston, MA 02110-1301, USA.
21#
22
23from gnuradio import gr, gru, blks2
24############## imports specific to spectrum sense #######################
25from gnuradio import optfir, window
26from gnuradio import audio
27from gnuradio.eng_option import eng_option
28from optparse import OptionParser
29from usrpm import usrp_dbid
30import math
31import struct
32#########################################################################
33from gnuradio import usrp
34from gnuradio import eng_notation
35import copy
36import sys
37
38# from current dir
39from pick_bitrate import pick_rx_bitrate
40import usrp_options
41
42# /////////////////////////////////////////////////////////////////////////////
43#                              receive path
44# /////////////////////////////////////////////////////////////////////////////
45
46class receive_path(gr.hier_block2):
47    def __init__(self, demod_class, rx_callback, options):
48
49        gr.hier_block2.__init__(self, "receive_path",
50                                gr.io_signature(0, 0, 0), # Input signature
51                                gr.io_signature(0, 0, 0)) # Output signature
52
53        options = copy.copy(options)    # make a copy so we can destructively modify
54
55        self._verbose            = options.verbose
56        self._rx_freq            = options.rx_freq         # receiver's center frequency
57        self._rx_gain            = options.rx_gain         # receiver's gain
58        self._bitrate            = options.bitrate         # desired bit rate
59        self._decim              = options.decim           # Decimating rate for the USRP (prelim)
60        self._samples_per_symbol = options.samples_per_symbol  # desired samples/symbol
61
62        self._rx_callback   = rx_callback      # this callback is fired when there's a packet available
63        self._demod_class   = demod_class      # the demodulator_class we're using
64
65        if self._rx_freq is None:
66            sys.stderr.write("-f FREQ or --freq FREQ or --rx-freq FREQ must be specified\n")
67            raise SystemExit
68
69        # Set up USRP source; also adjusts decim, samples_per_symbol, and bitrate
70        self._setup_usrp_source(options)
71
72        if options.show_rx_gain_range:
73            print "Rx Gain Range: minimum = %g, maximum = %g, step size = %g"%tuple(self.u.gain_range())
74
75        self.set_gain(options.rx_gain)
76
77        # Set RF frequency
78        ok = self.set_freq(self._rx_freq)
79        if not ok:
80            print "Failed to set Rx frequency to %s" % (eng_notation.num_to_str(self._rx_freq))
81            raise ValueError, eng_notation.num_to_str(self._rx_freq)
82
83        # copy the final answers back into options for use by demodulator
84        options.samples_per_symbol = self._samples_per_symbol
85        options.bitrate = self._bitrate
86        options.decim = self._decim
87
88        # Get demod_kwargs
89        demod_kwargs = self._demod_class.extract_kwargs_from_options(options)
90
91        # Design filter to get actual channel we want
92        sw_decim = 1
93        chan_coeffs = gr.firdes.low_pass (1.0,                  # gain
94                                          sw_decim * self._samples_per_symbol, # sampling rate
95                                          1.0,                  # midpoint of trans. band
96                                          0.5,                  # width of trans. band
97                                          gr.firdes.WIN_HANN)   # filter type
98
99        # Decimating channel filter
100        # complex in and out, float taps
101        self.chan_filt = gr.fft_filter_ccc(sw_decim, chan_coeffs)
102        #self.chan_filt = gr.fir_filter_ccf(sw_decim, chan_coeffs)
103
104        # receiver
105        self.packet_receiver = \
106            blks2.demod_pkts(self._demod_class(**demod_kwargs),
107                             access_code=None,
108                             callback=self._rx_callback,
109                             threshold=-1)
110   
111        # Carrier Sensing Blocks
112        alpha = 0.001
113        thresh = 30   # in dB, will have to adjust
114
115        if options.log_rx_power == True:
116            self.probe = gr.probe_avg_mag_sqrd_cf(thresh,alpha)
117            self.power_sink = gr.file_sink(gr.sizeof_float, "rxpower.dat")
118            self.connect(self.chan_filt, self.probe, self.power_sink)
119        else:
120            self.probe = gr.probe_avg_mag_sqrd_c(thresh,alpha)
121            self.connect(self.chan_filt, self.probe)
122
123        # Display some information about the setup
124        if self._verbose:
125            self._print_verbage()
126
127        #################################spectrum sense specific code#############################
128
129
130        self.min_freq = 462.4825e6 # setting min and max frequency inside the init rather than taking it from the command line
131        self.max_freq = 462.6425e6
132
133        if self.min_freq > self.max_freq:
134            self.min_freq, self.max_freq = self.max_freq, self.min_freq   # swap them
135
136        self.fft_size = 512
137
138
139        #if not options.real_time:
140        #    realtime = False
141
142        #else:
143        #    # Attempt to enable realtime scheduling
144        #    r = gr.enable_realtime_scheduling()
145        #    if r == gr.RT_OK:
146        #        realtime = True
147        #    else:
148        #        realtime = False
149        #        print "Note: failed to enable realtime scheduling"
150
151        # If the user hasn't set the fusb_* parameters on the command line,
152        # pick some values that will reduce latency.
153
154        #if 1:
155        #    if options.fusb_block_size == 0 and options.fusb_nblocks == 0:
156        #        if realtime:                        # be more aggressive
157        #            options.fusb_block_size = gr.prefs().get_long('fusb', 'rt_block_size', 1024)
158        #            options.fusb_nblocks    = gr.prefs().get_long('fusb', 'rt_nblocks', 16)
159        #        else:
160        #            options.fusb_block_size = gr.prefs().get_long('fusb', 'block_size', 4096)
161        #            options.fusb_nblocks    = gr.prefs().get_long('fusb', 'nblocks', 16)
162   
163        #print "fusb_block_size =", options.fusb_block_size
164        #print "fusb_nblocks    =", options.fusb_nblocks
165
166        # build graph
167       
168        #self.u = usrp_options.create_usrp_source(options)
169
170
171        #adc_rate = self.u.adc_rate()                # 64 MS/s
172        #usrp_decim = options.decim
173        #self.u.set_decim_rate(usrp_decim)
174        usrp_rate = self.adc_rate / self._decim
175
176        #self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec))
177        #self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec)
178        #print "Using RX d'board %s" % (self.subdev.side_and_name(),)
179
180
181       
182        s2v = gr.stream_to_vector(gr.sizeof_gr_complex, self.fft_size)
183
184        mywindow = window.blackmanharris(self.fft_size)
185        fft = gr.fft_vcc(self.fft_size, True, mywindow)
186        power = 0
187        for tap in mywindow:
188            power += tap*tap
189
190       
191        c2mag = gr.complex_to_mag_squared(self.fft_size)
192        print "print c2mag ",c2mag,"\n"
193        # FIXME the log10 primitive is dog slow
194        log = gr.nlog10_ff(10, self.fft_size,
195                           -20*math.log10(self.fft_size)-10*math.log10(power/self.fft_size))
196               
197        # Set the freq_step to 75% of the actual data throughput.
198        # This allows us to discard the bins on both ends of the spectrum.
199
200        self.freq_step = 0.75 * usrp_rate
201        self.min_center_freq = self.min_freq + self.freq_step/2
202        nsteps = math.ceil((self.max_freq - self.min_freq) / self.freq_step)
203        self.max_center_freq = self.min_center_freq + (nsteps * self.freq_step)
204
205        self.next_freq = self.min_center_freq
206        tune_delay = 1e-3
207        dwell_delay = 1e-2
208        tune_delay  = max(0, int(round(tune_delay * usrp_rate / self.fft_size)))  # in fft_frames
209        dwell_delay = max(1, int(round(dwell_delay * usrp_rate / self.fft_size))) # in fft_frames
210
211        self.msgq = gr.msg_queue(16)
212        self._tune_callback = tune(self)        # hang on to this to keep it from being GC'd
213               
214        stats = gr.bin_statistics_f(self.fft_size, self.msgq,
215                                    self._tune_callback, tune_delay, dwell_delay)
216
217
218
219        # FIXME leave out the log10 until we speed it up
220        #self.connect(self.u, s2v, fft, c2mag, log, stats)
221        #self.connect(self.u, s2v, fft, c2mag, stats)
222       
223        #if options.gain is None:
224        #    # if no gain was specified, use the mid-point in dB
225        #    g = self.subdev.gain_range()
226        #    options.gain = float(g[0]+g[1])/2
227
228        #self.set_gain(options.gain)
229        #print "gain =", options.gain
230       
231        ##########################################################################################
232           
233        self.connect(self.u, self.chan_filt, self.packet_receiver)
234        self.connect(self.u, s2v, fft, c2mag, stats)
235
236    def _setup_usrp_source(self, options):
237
238        self.u = usrp_options.create_usrp_source(options)
239        self.adc_rate = self.u.adc_rate()
240
241        # derive values of bitrate, samples_per_symbol, and decim from desired info
242        (self._bitrate, self._samples_per_symbol, self._decim) = \
243            pick_rx_bitrate(self._bitrate, self._demod_class.bits_per_symbol(), \
244                            self._samples_per_symbol, self._decim, self.adc_rate)
245
246        self.u.set_decim(self._decim)
247
248    def set_freq(self, target_freq):
249        """
250        Set the center frequency we're interested in.
251
252        @param target_freq: frequency in Hz
253        @rypte: bool
254
255        Tuning is a two step process.  First we ask the front-end to
256        tune as close to the desired frequency as it can.  Then we use
257        the result of that operation and our target_frequency to
258        determine the value for the digital up converter.
259        """
260        return self.u.set_center_freq(target_freq)
261
262    def set_gain(self, gain):
263        """
264        Sets the analog gain in the USRP
265        """
266        return self.u.set_gain(gain)
267       
268    def bitrate(self):
269        return self._bitrate
270
271    def samples_per_symbol(self):
272        return self._samples_per_symbol
273
274    def decim(self):
275        return self._decim
276
277    def carrier_sensed(self):
278        """
279        Return True if we think carrier is present.
280        """
281        #return self.probe.level() > X
282        return self.probe.unmuted()
283
284    def carrier_threshold(self):
285        """
286        Return current setting in dB.
287        """
288        return self.probe.threshold()
289
290    def set_carrier_threshold(self, threshold_in_db):
291        """
292        Set carrier threshold.
293
294        @param threshold_in_db: set detection threshold
295        @type threshold_in_db:  float (dB)
296        """
297        self.probe.set_threshold(threshold_in_db)
298   
299    @staticmethod
300    def add_options(normal, expert):
301        """
302        Adds receiver-specific options to the Options Parser
303        """
304        add_freq_option(normal)
305        if not normal.has_option("--bitrate"):
306            normal.add_option("-r", "--bitrate", type="eng_float", default=None,
307                              help="specify bitrate.  samples-per-symbol and interp/decim will be derived.")
308        usrp_options.add_rx_options(normal, expert)
309        normal.add_option("-v", "--verbose", action="store_true", default=False)
310        expert.add_option("-S", "--samples-per-symbol", type="int", default=None,
311                          help="set samples/symbol [default=%default]")
312        expert.add_option("", "--rx-freq", type="eng_float", default=None,
313                          help="set Rx frequency to FREQ [default=%default]", metavar="FREQ")
314        expert.add_option("", "--log", action="store_true", default=False,
315                          help="Log all parts of flow graph to files (CAUTION: lots of data)")
316        expert.add_option("", "--log-rx-power", action="store_true", default=False,
317                          help="Log receive signal power to file (CAUTION: lots of data)")
318
319    def set_next_freq(self):
320        target_freq = self.next_freq
321        self.next_freq = self.next_freq + self.freq_step
322        if self.next_freq >= self.max_center_freq:
323            self.next_freq = self.min_center_freq
324
325        if not self.set_freq(target_freq):
326            print "Failed to set frequency to", target_freq
327
328        return target_freq
329
330    def _print_verbage(self):
331        """
332        Prints information about the receive path
333        """
334        print "\nReceive Path:"
335        print "USRP %s"    % (self.u,)
336        print "Rx gain:         %g"    % (self.u.gain(),)
337        print "modulation:      %s"    % (self._demod_class.__name__)
338        print "bitrate:         %sb/s" % (eng_notation.num_to_str(self._bitrate))
339        print "samples/symbol:  %3d"   % (self._samples_per_symbol)
340        print "decim:           %3d"   % (self._decim)
341        print "Rx Frequency:    %s"    % (eng_notation.num_to_str(self._rx_freq))
342
343    def get_avg_power(self,trials):
344        power_sum = 0 #sum of powers(each power value is determined by adding the 'fft square' points..these fft square points are essentially points from the PSD curve...and adding them gives us the power contained in the spectrum)
345        counter = 0
346       
347        while counter < 10 :
348
349                # Get the next message sent from the C++ code (blocking call).
350                # It contains the center frequency and the mag squared of the fft
351                m = parse_msg(self.msgq.delete_head())
352                #print "printing mag sq of fft ",sum(m.data),"\n"
353                #if sum(m.data) > 1e12:
354                power_sum = power_sum + sum(m.data)
355                print sum(m.data),"\n"
356                # Print center freq so we know that something is happening...
357                #print m.center_freq
358                counter +=1
359        # FIXME do something useful with the data...
360       
361        # m.data are the mag_squared of the fft output (they are in the
362        # standard order.  I.e., bin 0 == DC.)
363        # You'll probably want to do the equivalent of "fftshift" on them
364        # m.raw_data is a string that contains the binary floats.
365        # You could write this as binary to a file.
366
367        avg_power = power_sum/counter
368        print "printing average power ",avg_power,"\n"
369
370def add_freq_option(parser):
371    """
372    Hackery that has the -f / --freq option set both tx_freq and rx_freq
373    """
374    def freq_callback(option, opt_str, value, parser):
375        parser.values.rx_freq = value
376        parser.values.tx_freq = value
377
378    if not parser.has_option('--freq'):
379        parser.add_option('-f', '--freq', type="eng_float",
380                          action="callback", callback=freq_callback,
381                          help="set Tx and/or Rx frequency to FREQ [default=%default]",
382                          metavar="FREQ")
383
384
385##################### Spectrum sense specific classes##################################
386
387class tune(gr.feval_dd):
388    """
389    This class allows C++ code to callback into python.
390    """
391    def __init__(self, tb):
392        gr.feval_dd.__init__(self)
393        self.tb = tb
394
395    def eval(self, ignore):
396        """
397        This method is called from gr.bin_statistics_f when it wants to change
398        the center frequency.  This method tunes the front end to the new center
399        frequency, and returns the new frequency as its result.
400        """
401        try:
402            # We use this try block so that if something goes wrong from here
403            # down, at least we'll have a prayer of knowing what went wrong.
404            # Without this, you get a very mysterious:
405            #
406            #   terminate called after throwing an instance of 'Swig::DirectorMethodException'
407            #   Aborted
408            #
409            # message on stderr.  Not exactly helpful ;)
410
411            new_freq = self.tb.set_next_freq()
412            return new_freq
413
414        except Exception, e:
415            print "tune: Exception: ", e
416
417
418class parse_msg(object):
419    def __init__(self, msg):
420        self.center_freq = msg.arg1()
421        self.vlen = int(msg.arg2())
422        assert(msg.length() == self.vlen * gr.sizeof_float)
423
424        # FIXME consider using Numarray or NumPy vector
425        t = msg.to_string()
426        self.raw_data = t
427        self.data = struct.unpack('%df' % (self.vlen,), t)
428
429
430########################################################################################
Note: See TracBrowser for help on using the browser.