3 from gnuradio import gr, gru, eng_notation, optfir, window
4 from gnuradio import audio
5 from gnuradio import usrp
6 from gnuradio import blks
7 from gnuradio.eng_option import eng_option
8 from optparse import OptionParser
15 class tune(gr.feval_dd):
17 This class allows C++ code to callback into python.
19 def __init__(self, fg):
20 gr.feval_dd.__init__(self)
23 def eval(self, ignore):
25 This method is called from gr.bin_statistics_f when it wants to change
26 the center frequency. This method tunes the front end to the new center
27 frequency, and returns the new frequency as its result.
30 # We use this try block so that if something goes wrong from here
31 # down, at least we'll have a prayer of knowing what went wrong.
32 # Without this, you get a very mysterious:
34 # terminate called after throwing an instance of 'Swig::DirectorMethodException'
37 # message on stderr. Not exactly helpful ;)
39 new_freq = self.fg.set_next_freq()
43 print "tune: Exception: ", e
46 class parse_msg(object):
47 def __init__(self, msg):
48 self.center_freq = msg.arg1()
49 self.vlen = int(msg.arg2())
50 assert(msg.length() == self.vlen * gr.sizeof_float)
52 # FIXME consider using Numarray or NumPy vector
55 self.data = struct.unpack('%df' % (self.vlen,), t)
58 class my_graph(gr.flow_graph):
61 gr.flow_graph.__init__(self)
63 usage = "usage: %prog [options] min_freq max_freq"
64 parser = OptionParser(option_class=eng_option, usage=usage)
65 parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=(0,0),
66 help="select USRP Rx side A or B (default=A)")
67 parser.add_option("-g", "--gain", type="eng_float", default=None,
68 help="set gain in dB (default is midpoint)")
69 parser.add_option("", "--tune-delay", type="eng_float", default=1e-3, metavar="SECS",
70 help="time to delay (in seconds) after changing frequency [default=%default]")
71 parser.add_option("", "--dwell-delay", type="eng_float", default=10e-3, metavar="SECS",
72 help="time to dwell (in seconds) at a given frequncy [default=%default]")
73 parser.add_option("-F", "--fft-size", type="int", default=256,
74 help="specify number of FFT bins [default=%default]")
75 parser.add_option("-d", "--decim", type="intx", default=16,
76 help="set decimation to DECIM [default=%default]")
77 parser.add_option("", "--real-time", action="store_true", default=False,
78 help="Attempt to enable real-time scheduling")
79 parser.add_option("-B", "--fusb-block-size", type="int", default=0,
80 help="specify fast usb block size [default=%default]")
81 parser.add_option("-N", "--fusb-nblocks", type="int", default=0,
82 help="specify number of fast usb blocks [default=%default]")
84 (options, args) = parser.parse_args()
89 self.min_freq = eng_notation.str_to_num(args[0])
90 self.max_freq = eng_notation.str_to_num(args[1])
92 if self.min_freq > self.max_freq:
93 self.min_freq, self.max_freq = self.max_freq, self.min_freq # swap them
95 self.fft_size = options.fft_size
98 if not options.real_time:
101 # Attempt to enable realtime scheduling
102 r = gr.enable_realtime_scheduling()
107 print "Note: failed to enable realtime scheduling"
109 # If the user hasn't set the fusb_* parameters on the command line,
110 # pick some values that will reduce latency.
113 if options.fusb_block_size == 0 and options.fusb_nblocks == 0:
114 if realtime: # be more aggressive
115 options.fusb_block_size = gr.prefs().get_long('fusb', 'rt_block_size', 1024)
116 options.fusb_nblocks = gr.prefs().get_long('fusb', 'rt_nblocks', 16)
118 options.fusb_block_size = gr.prefs().get_long('fusb', 'block_size', 4096)
119 options.fusb_nblocks = gr.prefs().get_long('fusb', 'nblocks', 16)
121 #print "fusb_block_size =", options.fusb_block_size
122 #print "fusb_nblocks =", options.fusb_nblocks
126 self.u = usrp.source_c(fusb_block_size=options.fusb_block_size,
127 fusb_nblocks=options.fusb_nblocks)
130 adc_rate = self.u.adc_rate() # 64 MS/s
131 usrp_decim = options.decim
132 self.u.set_decim_rate(usrp_decim)
133 usrp_rate = adc_rate / usrp_decim
135 self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec))
136 self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec)
137 print "Using RX d'board %s" % (self.subdev.side_and_name(),)
140 s2v = gr.stream_to_vector(gr.sizeof_gr_complex, self.fft_size)
142 mywindow = window.blackmanharris(self.fft_size)
143 fft = gr.fft_vcc(self.fft_size, True, mywindow)
148 c2mag = gr.complex_to_mag_squared(self.fft_size)
150 # FIXME the log10 primitive is dog slow
151 log = gr.nlog10_ff(10, self.fft_size,
152 -20*math.log10(self.fft_size)-10*math.log10(power/self.fft_size))
154 # Set the freq_step to 75% of the actual data throughput.
155 # This allows us to discard the bins on both ends of the spectrum.
157 self.freq_step = 0.75 * usrp_rate
158 self.min_center_freq = self.min_freq + self.freq_step/2
159 nsteps = math.ceil((self.max_freq - self.min_freq) / self.freq_step)
160 self.max_center_freq = self.min_center_freq + (nsteps * self.freq_step)
162 self.next_freq = self.min_center_freq
164 tune_delay = max(0, int(round(options.tune_delay * usrp_rate / self.fft_size))) # in fft_frames
165 dwell_delay = max(1, int(round(options.dwell_delay * usrp_rate / self.fft_size))) # in fft_frames
167 self.msgq = gr.msg_queue(16)
168 self._tune_callback = tune(self) # hang on to this to keep it from being GC'd
169 stats = gr.bin_statistics_f(self.fft_size, self.msgq,
170 self._tune_callback, tune_delay, dwell_delay)
172 # FIXME leave out the log10 until we speed it up
173 #self.connect(self.u, s2v, fft, c2mag, log, stats)
174 self.connect(self.u, s2v, fft, c2mag, stats)
176 if options.gain is None:
177 # if no gain was specified, use the mid-point in dB
178 g = self.subdev.gain_range()
179 options.gain = float(g[0]+g[1])/2
181 self.set_gain(options.gain)
182 print "gain =", options.gain
185 def set_next_freq(self):
186 target_freq = self.next_freq
187 self.next_freq = self.next_freq + self.freq_step
188 if self.next_freq >= self.max_center_freq:
189 self.next_freq = self.min_center_freq
191 if not self.set_freq(target_freq):
192 print "Failed to set frequency to", target_freq
197 def set_freq(self, target_freq):
199 Set the center frequency we're interested in.
201 @param target_freq: frequency in Hz
204 Tuning is a two step process. First we ask the front-end to
205 tune as close to the desired frequency as it can. Then we use
206 the result of that operation and our target_frequency to
207 determine the value for the digital down converter.
209 return self.u.tune(0, self.subdev, target_freq)
212 def set_gain(self, gain):
213 self.subdev.set_gain(gain)
219 # Get the next message sent from the C++ code (blocking call).
220 # It contains the center frequency and the mag squared of the fft
221 m = parse_msg(fg.msgq.delete_head())
223 # Print center freq so we know that something is happening...
226 # FIXME do something useful with the data...
228 # m.data are the mag_squared of the fft output (they are in the
229 # standard order. I.e., bin 0 == DC.)
230 # You'll probably want to do the equivalent of "fftshift" on them
231 # m.raw_data is a string that contains the binary floats.
232 # You could write this as binary to a file.
235 if __name__ == '__main__':
238 fg.start() # start executing flow graph in another thread...
241 except KeyboardInterrupt: