37 | | self.connect(self.txpath); |
38 | | self.connect(self.rxpath); |
39 | | |
40 | | |
41 | | ##################### Spectrum sense specific classes########################################################### |
42 | | class tune(gr.feval_dd): |
43 | | """ |
44 | | This class allows C++ code to callback into python. |
45 | | """ |
46 | | def __init__(self, tb): |
47 | | gr.feval_dd.__init__(self) |
48 | | self.tb = tb |
49 | | |
50 | | def eval(self, ignore): |
51 | | """ |
52 | | This method is called from gr.bin_statistics_f when it wants to change |
53 | | the center frequency. This method tunes the front end to the new center |
54 | | frequency, and returns the new frequency as its result. |
55 | | """ |
56 | | try: |
57 | | # We use this try block so that if something goes wrong from here |
58 | | # down, at least we'll have a prayer of knowing what went wrong. |
59 | | # Without this, you get a very mysterious: |
60 | | # |
61 | | # terminate called after throwing an instance of 'Swig::DirectorMethodException' |
62 | | # Aborted |
63 | | # |
64 | | # message on stderr. Not exactly helpful ;) |
65 | | |
66 | | new_freq = self.tb.set_next_freq() |
67 | | return new_freq |
68 | | |
69 | | except Exception, e: |
70 | | print "tune: Exception: ", e |
71 | | |
72 | | |
73 | | class parse_msg(object): |
74 | | def __init__(self, msg): |
75 | | self.center_freq = msg.arg1() |
76 | | self.vlen = int(msg.arg2()) |
77 | | assert(msg.length() == self.vlen * gr.sizeof_float) |
78 | | |
79 | | # FIXME consider using Numarray or NumPy vector |
80 | | t = msg.to_string() |
81 | | self.raw_data = t |
82 | | self.data = struct.unpack('%df' % (self.vlen,), t) |
83 | | |
84 | | |
85 | | class spec_sense_top_block(gr.top_block): |
86 | | |
87 | | def __init__(self,min_f,max_f): |
88 | | gr.top_block.__init__(self) |
89 | | |
90 | | usage = "usage: %prog [options] min_freq max_freq" |
91 | | parser = OptionParser(option_class=eng_option, usage=usage) |
92 | | parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=(0,0), |
93 | | help="select USRP Rx side A or B (default=A)") |
94 | | parser.add_option("-g", "--gain", type="eng_float", default=None, |
95 | | help="set gain in dB (default is midpoint)") |
96 | | parser.add_option("", "--tune-delay", type="eng_float", default=1e-3, metavar="SECS", |
97 | | help="time to delay (in seconds) after changing frequency [default=%default]") |
98 | | parser.add_option("", "--dwell-delay", type="eng_float", default=10e-3, metavar="SECS", |
99 | | help="time to dwell (in seconds) at a given frequncy [default=%default]") |
100 | | parser.add_option("-F", "--fft-size", type="int", default=256, |
101 | | help="specify number of FFT bins [default=%default]") |
102 | | parser.add_option("-d", "--decim", type="intx", default=16, |
103 | | help="set decimation to DECIM [default=%default]") |
104 | | parser.add_option("", "--real-time", action="store_true", default=False, |
105 | | help="Attempt to enable real-time scheduling") |
106 | | parser.add_option("-B", "--fusb-block-size", type="int", default=0, |
107 | | help="specify fast usb block size [default=%default]") |
108 | | parser.add_option("-N", "--fusb-nblocks", type="int", default=0, |
109 | | help="specify number of fast usb blocks [default=%default]") |
110 | | |
111 | | (options, args) = parser.parse_args() |
112 | | #if len(args) != 2: |
113 | | # parser.print_help() |
114 | | # sys.exit(1) |
115 | | |
116 | | #self.min_freq = eng_notation.str_to_num(args[0]) |
117 | | self.min_freq = float(min_f) # setting min and max frequency inside the init rather than taking it from the command line |
118 | | #self.max_freq = eng_notation.str_to_num(args[1]) |
119 | | self.max_freq = float(max_f) |
120 | | |
121 | | if self.min_freq > self.max_freq: |
122 | | self.min_freq, self.max_freq = self.max_freq, self.min_freq # swap them |
123 | | |
124 | | self.fft_size = options.fft_size |
125 | | |
126 | | |
127 | | if not options.real_time: |
128 | | realtime = False |
129 | | else: |
130 | | # Attempt to enable realtime scheduling |
131 | | r = gr.enable_realtime_scheduling() |
132 | | if r == gr.RT_OK: |
133 | | realtime = True |
134 | | else: |
135 | | realtime = False |
136 | | print "Note: failed to enable realtime scheduling" |
137 | | |
138 | | # If the user hasn't set the fusb_* parameters on the command line, |
139 | | # pick some values that will reduce latency. |
140 | | |
141 | | if 1: |
142 | | if options.fusb_block_size == 0 and options.fusb_nblocks == 0: |
143 | | if realtime: # be more aggressive |
144 | | options.fusb_block_size = gr.prefs().get_long('fusb', 'rt_block_size', 1024) |
145 | | options.fusb_nblocks = gr.prefs().get_long('fusb', 'rt_nblocks', 16) |
146 | | else: |
147 | | options.fusb_block_size = gr.prefs().get_long('fusb', 'block_size', 4096) |
148 | | options.fusb_nblocks = gr.prefs().get_long('fusb', 'nblocks', 16) |
149 | | |
150 | | #print "fusb_block_size =", options.fusb_block_size |
151 | | #print "fusb_nblocks =", options.fusb_nblocks |
152 | | |
153 | | # build graph |
154 | | |
155 | | self.u = usrp.source_c(fusb_block_size=options.fusb_block_size, |
156 | | fusb_nblocks=options.fusb_nblocks) |
157 | | |
158 | | |
159 | | adc_rate = self.u.adc_rate() # 64 MS/s |
160 | | usrp_decim = options.decim |
161 | | self.u.set_decim_rate(usrp_decim) |
162 | | usrp_rate = adc_rate / usrp_decim |
163 | | |
164 | | self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) |
165 | | self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) |
166 | | print "Using RX d'board %s" % (self.subdev.side_and_name(),) |
167 | | |
168 | | |
169 | | s2v = gr.stream_to_vector(gr.sizeof_gr_complex, self.fft_size) |
170 | | |
171 | | mywindow = window.blackmanharris(self.fft_size) |
172 | | fft = gr.fft_vcc(self.fft_size, True, mywindow) |
173 | | power = 0 |
174 | | for tap in mywindow: |
175 | | power += tap*tap |
176 | | |
177 | | c2mag = gr.complex_to_mag_squared(self.fft_size) |
178 | | print "print c2mag ",c2mag,"\n" |
179 | | # FIXME the log10 primitive is dog slow |
180 | | log = gr.nlog10_ff(10, self.fft_size, |
181 | | -20*math.log10(self.fft_size)-10*math.log10(power/self.fft_size)) |
182 | | |
183 | | # Set the freq_step to 75% of the actual data throughput. |
184 | | # This allows us to discard the bins on both ends of the spectrum. |
185 | | |
186 | | self.freq_step = 0.75 * usrp_rate |
187 | | self.min_center_freq = self.min_freq + self.freq_step/2 |
188 | | nsteps = math.ceil((self.max_freq - self.min_freq) / self.freq_step) |
189 | | self.max_center_freq = self.min_center_freq + (nsteps * self.freq_step) |
190 | | |
191 | | self.next_freq = self.min_center_freq |
192 | | |
193 | | tune_delay = max(0, int(round(options.tune_delay * usrp_rate / self.fft_size))) # in fft_frames |
194 | | dwell_delay = max(1, int(round(options.dwell_delay * usrp_rate / self.fft_size))) # in fft_frames |
195 | | |
196 | | self.msgq = gr.msg_queue(16) |
197 | | self._tune_callback = tune(self) # hang on to this to keep it from being GC'd |
198 | | stats = gr.bin_statistics_f(self.fft_size, self.msgq, |
199 | | self._tune_callback, tune_delay, dwell_delay) |
200 | | |
201 | | # FIXME leave out the log10 until we speed it up |
202 | | #self.connect(self.u, s2v, fft, c2mag, log, stats) |
203 | | self.connect(self.u, s2v, fft, c2mag, stats) |
204 | | |
205 | | if options.gain is None: |
206 | | # if no gain was specified, use the mid-point in dB |
207 | | g = self.subdev.gain_range() |
208 | | options.gain = float(g[0]+g[1])/2 |
209 | | |
210 | | self.set_gain(options.gain) |
211 | | print "gain =", options.gain |
212 | | |
213 | | |
214 | | def set_next_freq(self): |
215 | | target_freq = self.next_freq |
216 | | self.next_freq = self.next_freq + self.freq_step |
217 | | if self.next_freq >= self.max_center_freq: |
218 | | self.next_freq = self.min_center_freq |
219 | | |
220 | | if not self.set_freq(target_freq): |
221 | | print "Failed to set frequency to", target_freq |
222 | | |
223 | | return target_freq |
224 | | |
225 | | |
226 | | def set_freq(self, target_freq): |
227 | | """ |
228 | | Set the center frequency we're interested in. |
229 | | |
230 | | @param target_freq: frequency in Hz |
231 | | @rypte: bool |
232 | | |
233 | | Tuning is a two step process. First we ask the front-end to |
234 | | tune as close to the desired frequency as it can. Then we use |
235 | | the result of that operation and our target_frequency to |
236 | | determine the value for the digital down converter. |
237 | | """ |
238 | | return self.u.tune(0, self.subdev, target_freq) |
239 | | |
240 | | |
241 | | def set_gain(self, gain): |
242 | | self.subdev.set_gain(gain) |
243 | | |
244 | | def set_min_max_freq(self,minimum,maximum):# setting min and max frequency |
245 | | self.min_freq = float(minimum) |
246 | | self.max_freq = float(maximum) |
247 | | |
248 | | def get_avg_power(self,trials): |
249 | | 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) |
250 | | counter = 0 |
251 | | _trials = int(trials) |
252 | | while counter < _trials : |
253 | | |
254 | | # Get the next message sent from the C++ code (blocking call). |
255 | | # It contains the center frequency and the mag squared of the fft |
256 | | m = parse_msg(self.msgq.delete_head()) |
257 | | print "printing mag sq of fft ",sum(m.data),"\n" |
258 | | #if sum(m.data) > 1e12: |
259 | | power_sum = power_sum + sum(m.data) |
260 | | # Print center freq so we know that something is happening... |
261 | | #print m.center_freq |
262 | | counter +=1 |
263 | | # FIXME do something useful with the data... |
264 | | |
265 | | # m.data are the mag_squared of the fft output (they are in the |
266 | | # standard order. I.e., bin 0 == DC.) |
267 | | # You'll probably want to do the equivalent of "fftshift" on them |
268 | | # m.raw_data is a string that contains the binary floats. |
269 | | # You could write this as binary to a file. |
270 | | |
271 | | avg_power = power_sum/_trials |
272 | | print "printing average power ",avg_power,"\n" |
273 | | return avg_power |
274 | | |
275 | | ######################## end of spectrum sense specific classes ############################################################# |
| 38 | |
| 39 | ########connect for the spectrum sense######### |
| 40 | default_min_freq = 462.4825e6 #80 Khz to the left of channel 1 (462.5625e6) in frs band |
| 41 | default_max_freq = 462.6425e6 #80 Khz to the right of channel 1 (462.5625e6) in frs band |
| 42 | self.sp_sense_path = spec_sense_path.spec_sense_top_block(default_min_freq,default_min_freq) |
| 43 | self.connect(self.sp_sense_path.u, self.sp_sense_path.s2v, self.sp_sense_path.fft, self.sp_sense_path.c2mag, self.sp_sense_path.stats) |
| 44 | #self.connect(self.txpath); |
| 45 | #self.connect(self.rxpath); |