3 # Copyright 2008,2009 Free Software Foundation, Inc.
5 # This file is part of GNU Radio
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)
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.
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.
24 SAMP_RATE_KEY = 'samp_rate'
25 LINK_RATE_KEY = 'link_rate'
26 DAC_RATE_KEY = 'dac_rate'
29 TX_FREQ_KEY = 'tx_freq'
30 DDC_FREQ_KEY = 'ddc_freq'
31 BB_FREQ_KEY = 'bb_freq'
32 AMPLITUDE_KEY = 'amplitude'
33 AMPL_RANGE_KEY = 'ampl_range'
34 WAVEFORM_FREQ_KEY = 'waveform_freq'
35 WAVEFORM_OFFSET_KEY = 'waveform_offset'
36 WAVEFORM2_FREQ_KEY = 'waveform2_freq'
37 FREQ_RANGE_KEY = 'freq_range'
38 GAIN_RANGE_KEY = 'gain_range'
41 def setter(ps, key, val): ps[key] = val
43 from gnuradio import gr, eng_notation
44 from gnuradio.gr.pubsub import pubsub
45 from gnuradio.eng_option import eng_option
46 from gnuradio import usrp_options
47 from optparse import OptionParser
51 n2s = eng_notation.num_to_str
53 waveforms = { gr.GR_SIN_WAVE : "Complex Sinusoid",
54 gr.GR_CONST_WAVE : "Constant",
55 gr.GR_GAUSSIAN : "Gaussian Noise",
56 gr.GR_UNIFORM : "Uniform Noise",
61 # GUI-unaware GNU Radio flowgraph. This may be used either with command
62 # line applications or GUI applications.
64 class top_block(gr.top_block, pubsub):
65 def __init__(self, options, args):
66 gr.top_block.__init__(self)
68 self._verbose = options.verbose
69 #initialize values from options
70 self._setup_usrpx(options)
71 self.subscribe(INTERP_KEY, lambda i: setter(self, SAMP_RATE_KEY, self[DAC_RATE_KEY]/i))
72 self.subscribe(SAMP_RATE_KEY, lambda e: setter(self, LINK_RATE_KEY, e*32))
73 self[INTERP_KEY] = options.interp or 16
74 self[TX_FREQ_KEY] = options.tx_freq
75 self[AMPLITUDE_KEY] = options.amplitude
76 self[WAVEFORM_FREQ_KEY] = options.waveform_freq
77 self[WAVEFORM_OFFSET_KEY] = options.offset
78 self[WAVEFORM2_FREQ_KEY] = options.waveform2_freq
80 self[DDC_FREQ_KEY] = 0
81 #subscribe set methods
82 self.subscribe(INTERP_KEY, self.set_interp)
83 self.subscribe(GAIN_KEY, self.set_gain)
84 self.subscribe(TX_FREQ_KEY, self.set_freq)
85 self.subscribe(AMPLITUDE_KEY, self.set_amplitude)
86 self.subscribe(WAVEFORM_FREQ_KEY, self.set_waveform_freq)
87 self.subscribe(WAVEFORM2_FREQ_KEY, self.set_waveform2_freq)
88 self.subscribe(TYPE_KEY, self.set_waveform)
89 #force update on pubsub keys
90 for key in (INTERP_KEY, GAIN_KEY, TX_FREQ_KEY,
91 AMPLITUDE_KEY, WAVEFORM_FREQ_KEY, WAVEFORM_OFFSET_KEY, WAVEFORM2_FREQ_KEY):
93 self[TYPE_KEY] = options.type #set type last
95 def _setup_usrpx(self, options):
96 self._u = usrp_options.create_usrp_sink(options)
97 self.publish(DESC_KEY, lambda: str(self._u))
98 self.publish(DAC_RATE_KEY, self._u.dac_rate)
99 self.publish(FREQ_RANGE_KEY, self._u.freq_range)
100 self.publish(GAIN_RANGE_KEY, self._u.gain_range)
101 self.publish(GAIN_KEY, self._u.gain)
102 if self._verbose: print str(self._u)
104 def _set_tx_amplitude(self, ampl):
106 Sets the transmit amplitude sent to the USRP
107 @param ampl the amplitude or None for automatic
109 ampl_range = self[AMPL_RANGE_KEY]
110 if ampl is None: ampl = (ampl_range[1] - ampl_range[0])*0.15 + ampl_range[0]
111 self[AMPLITUDE_KEY] = max(ampl_range[0], min(ampl, ampl_range[1]))
113 def set_interp(self, interp):
114 if not self._u.set_interp(interp):
115 raise RuntimeError("Failed to set interpolation rate %i" % (interp,))
118 print "USRP interpolation rate:", interp
119 print "USRP IF bandwidth: %sHz" % (n2s(self[SAMP_RATE_KEY]),)
121 if self[TYPE_KEY] in (gr.GR_SIN_WAVE, gr.GR_CONST_WAVE):
122 self._src.set_sampling_freq(self[SAMP_RATE_KEY])
123 elif self[TYPE_KEY] == "2tone":
124 self._src1.set_sampling_freq(self[SAMP_RATE_KEY])
125 self._src2.set_sampling_freq(self[SAMP_RATE_KEY])
126 elif self[TYPE_KEY] == "sweep":
127 self._src1.set_sampling_freq(self[SAMP_RATE_KEY])
128 self._src2.set_sampling_freq(self[WAVEFORM_FREQ_KEY]*2*math.pi/self[SAMP_RATE_KEY])
130 return True # Waveform not yet set
132 if self._verbose: print "Set interpolation rate to:", interp
135 def set_gain(self, gain):
137 g = self[GAIN_RANGE_KEY]
138 gain = float(g[0]+g[1])/2
140 print "Using auto-calculated mid-point TX gain"
141 self[GAIN_KEY] = gain
143 self._u.set_gain(gain)
145 print "Set TX gain to:", gain
147 def set_freq(self, target_freq):
149 if target_freq is None:
150 f = self[FREQ_RANGE_KEY]
151 target_freq = float(f[0]+f[1])/2.0
153 print "Using auto-calculated mid-point frequency"
154 self[TX_FREQ_KEY] = target_freq
157 tr = self._u.set_center_freq(target_freq)
158 fs = "%sHz" % (n2s(target_freq),)
160 self._freq = target_freq
161 self[DDC_FREQ_KEY] = tr.dxc_freq
162 self[BB_FREQ_KEY] = tr.baseband_freq
164 print "Set center frequency to", fs
165 print "Tx baseband frequency: %sHz" % (n2s(tr.baseband_freq),)
166 print "Tx DDC frequency: %sHz" % (n2s(tr.dxc_freq),)
167 print "Tx residual frequency: %sHz" % (n2s(tr.residual_freq),)
168 elif self._verbose: print "Failed to set freq."
171 def set_waveform_freq(self, freq):
172 if self[TYPE_KEY] == gr.GR_SIN_WAVE:
173 self._src.set_frequency(freq)
174 elif self[TYPE_KEY] == "2tone":
175 self._src1.set_frequency(freq)
176 elif self[TYPE_KEY] == 'sweep':
177 #there is no set sensitivity, redo fg
178 self[TYPE_KEY] = self[TYPE_KEY]
181 def set_waveform2_freq(self, freq):
183 self[WAVEFORM2_FREQ_KEY] = -self[WAVEFORM_FREQ_KEY]
185 if self[TYPE_KEY] == "2tone":
186 self._src2.set_frequency(freq)
187 elif self[TYPE_KEY] == "sweep":
188 self._src1.set_frequency(freq)
191 def set_waveform(self, type):
193 self.disconnect_all()
194 if type == gr.GR_SIN_WAVE or type == gr.GR_CONST_WAVE:
195 self._src = gr.sig_source_c(self[SAMP_RATE_KEY], # Sample rate
196 type, # Waveform type
197 self[WAVEFORM_FREQ_KEY], # Waveform frequency
198 self[AMPLITUDE_KEY], # Waveform amplitude
199 self[WAVEFORM_OFFSET_KEY]) # Waveform offset
200 elif type == gr.GR_GAUSSIAN or type == gr.GR_UNIFORM:
201 self._src = gr.noise_source_c(type, self[AMPLITUDE_KEY])
202 elif type == "2tone":
203 self._src1 = gr.sig_source_c(self[SAMP_RATE_KEY],
205 self[WAVEFORM_FREQ_KEY],
206 self[AMPLITUDE_KEY]/2.0,
208 if(self[WAVEFORM2_FREQ_KEY] is None):
209 self[WAVEFORM2_FREQ_KEY] = -self[WAVEFORM_FREQ_KEY]
211 self._src2 = gr.sig_source_c(self[SAMP_RATE_KEY],
213 self[WAVEFORM2_FREQ_KEY],
214 self[AMPLITUDE_KEY]/2.0,
216 self._src = gr.add_cc()
217 self.connect(self._src1,(self._src,0))
218 self.connect(self._src2,(self._src,1))
219 elif type == "sweep":
220 # rf freq is center frequency
221 # waveform_freq is total swept width
222 # waveform2_freq is sweep rate
223 # will sweep from (rf_freq-waveform_freq/2) to (rf_freq+waveform_freq/2)
224 if self[WAVEFORM2_FREQ_KEY] is None:
225 self[WAVEFORM2_FREQ_KEY] = 0.1
227 self._src1 = gr.sig_source_f(self[SAMP_RATE_KEY],
229 self[WAVEFORM2_FREQ_KEY],
232 self._src2 = gr.frequency_modulator_fc(self[WAVEFORM_FREQ_KEY]*2*math.pi/self[SAMP_RATE_KEY])
233 self._src = gr.multiply_const_cc(self[AMPLITUDE_KEY])
234 self.connect(self._src1,self._src2,self._src)
236 raise RuntimeError("Unknown waveform type")
238 self.connect(self._src, self._u)
242 print "Set baseband modulation to:", waveforms[type]
243 if type == gr.GR_SIN_WAVE:
244 print "Modulation frequency: %sHz" % (n2s(self[WAVEFORM_FREQ_KEY]),)
245 print "Initial phase:", self[WAVEFORM_OFFSET_KEY]
246 elif type == "2tone":
247 print "Tone 1: %sHz" % (n2s(self[WAVEFORM_FREQ_KEY]),)
248 print "Tone 2: %sHz" % (n2s(self[WAVEFORM2_FREQ_KEY]),)
249 elif type == "sweep":
250 print "Sweeping across %sHz to %sHz" % (n2s(-self[WAVEFORM_FREQ_KEY]/2.0),n2s(self[WAVEFORM_FREQ_KEY]/2.0))
251 print "Sweep rate: %sHz" % (n2s(self[WAVEFORM2_FREQ_KEY]),)
252 print "TX amplitude:", self[AMPLITUDE_KEY]
255 def set_amplitude(self, amplitude):
256 if amplitude < 0.0 or amplitude > 1.0:
257 if self._verbose: print "Amplitude out of range:", amplitude
260 if self[TYPE_KEY] in (gr.GR_SIN_WAVE, gr.GR_CONST_WAVE, gr.GR_GAUSSIAN, gr.GR_UNIFORM):
261 self._src.set_amplitude(amplitude)
262 elif self[TYPE_KEY] == "2tone":
263 self._src1.set_amplitude(amplitude/2.0)
264 self._src2.set_amplitude(amplitude/2.0)
265 elif self[TYPE_KEY] == "sweep":
266 self._src.set_k(amplitude)
268 return True # Waveform not yet set
270 if self._verbose: print "Set amplitude to:", amplitude
274 usage="%prog: [options]"
276 parser = OptionParser(option_class=eng_option, usage=usage)
277 usrp_options.add_tx_options(parser)
278 parser.add_option("-f", "--tx-freq", type="eng_float", default=None,
279 help="Set carrier frequency to FREQ [default=mid-point]", metavar="FREQ")
280 parser.add_option("-x", "--waveform-freq", type="eng_float", default=0,
281 help="Set baseband waveform frequency to FREQ [default=%default]")
282 parser.add_option("-y", "--waveform2-freq", type="eng_float", default=None,
283 help="Set 2nd waveform frequency to FREQ [default=%default]")
284 parser.add_option("--sine", dest="type", action="store_const", const=gr.GR_SIN_WAVE,
285 help="Generate a carrier modulated by a complex sine wave", default=gr.GR_SIN_WAVE)
286 parser.add_option("--const", dest="type", action="store_const", const=gr.GR_CONST_WAVE,
287 help="Generate a constant carrier")
288 parser.add_option("--offset", type="eng_float", default=0,
289 help="Set waveform phase offset to OFFSET [default=%default]")
290 parser.add_option("--gaussian", dest="type", action="store_const", const=gr.GR_GAUSSIAN,
291 help="Generate Gaussian random output")
292 parser.add_option("--uniform", dest="type", action="store_const", const=gr.GR_UNIFORM,
293 help="Generate Uniform random output")
294 parser.add_option("--2tone", dest="type", action="store_const", const="2tone",
295 help="Generate Two Tone signal for IMD testing")
296 parser.add_option("--sweep", dest="type", action="store_const", const="sweep",
297 help="Generate a swept sine wave")
298 parser.add_option("-A", "--amplitude", type="eng_float", default=0.15,
299 help="Set output amplitude to AMPL (0.0-1.0) [default=%default]", metavar="AMPL")
300 parser.add_option("-v", "--verbose", action="store_true", default=False,
301 help="Use verbose console output [default=%default]")
303 (options, args) = parser.parse_args()
305 return (options, args)
307 # If this script is executed, the following runs. If it is imported, the below does not run.
308 if __name__ == "__main__":
309 if gr.enable_realtime_scheduling() != gr.RT_OK:
310 print "Note: failed to enable realtime scheduling, continuing"
312 # Grab command line options and create top block
314 (options, args) = get_options()
315 tb = top_block(options, args)
317 except RuntimeError, e:
322 raw_input('Press Enter to quit: ')