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 self.max_center_freq = self.max_freq - self.freq_step/2
161 self.next_freq = self.min_center_freq
163 tune_delay = max(0, int(round(options.tune_delay * usrp_rate / self.fft_size))) # in fft_frames
164 dwell_delay = max(1, int(round(options.dwell_delay * usrp_rate / self.fft_size))) # in fft_frames
166 self.msgq = gr.msg_queue(16)
167 self._tune_callback = tune(self) # hang on to this to keep it from being GC'd
168 stats = gr.bin_statistics_f(self.fft_size, self.msgq,
169 self._tune_callback, tune_delay, dwell_delay)
171 # FIXME leave out the log10 until we speed it up
172 #self.connect(self.u, s2v, fft, c2mag, log, stats)
173 self.connect(self.u, s2v, fft, c2mag, stats)
175 if options.gain is None:
176 # if no gain was specified, use the mid-point in dB
177 g = self.subdev.gain_range()
178 options.gain = float(g[0]+g[1])/2
180 self.set_gain(options.gain)
181 print "gain =", options.gain
184 def set_next_freq(self):
185 target_freq = self.next_freq
186 self.next_freq = self.next_freq + self.freq_step
187 if self.next_freq > self.max_center_freq:
188 self.next_freq = self.min_center_freq
190 if not self.set_freq(target_freq):
191 print "Failed to set frequency to", target_freq
196 def set_freq(self, target_freq):
198 Set the center frequency we're interested in.
200 @param target_freq: frequency in Hz
203 Tuning is a two step process. First we ask the front-end to
204 tune as close to the desired frequency as it can. Then we use
205 the result of that operation and our target_frequency to
206 determine the value for the digital down converter.
208 return self.u.tune(0, self.subdev, target_freq)
211 def set_gain(self, gain):
212 self.subdev.set_gain(gain)
218 # Get the next message sent from the C++ code (blocking call).
219 # It contains the center frequency and the mag squared of the fft
220 m = parse_msg(fg.msgq.delete_head())
222 # Print center freq so we know that something is happening...
225 # FIXME do something useful with the data...
227 # m.data are the mag_squared of the fft output (they are in the
228 # standard order. I.e., bin 0 == DC.)
229 # You'll probably want to do the equivalent of "fftshift" on them
230 # m.raw_data is a string that contains the binary floats.
231 # You could write this as binary to a file.
234 if __name__ == '__main__':
237 fg.start() # start executing flow graph in another thread...
240 except KeyboardInterrupt: