switch source package format to 3.0 quilt
[debian/gnuradio] / gr-utils / src / python / usrp_siggen.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2008,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
23 DESC_KEY = 'desc'
24 SAMP_RATE_KEY = 'samp_rate'
25 LINK_RATE_KEY = 'link_rate'
26 DAC_RATE_KEY = 'dac_rate'
27 INTERP_KEY = 'interp'
28 GAIN_KEY = 'gain'
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'
39 TYPE_KEY = 'type'
40
41 def setter(ps, key, val): ps[key] = val
42
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
48 import sys
49 import math
50
51 n2s = eng_notation.num_to_str
52
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",
57               "2tone"          : "Two Tone",
58               "sweep"          : "Sweep" }
59
60 #
61 # GUI-unaware GNU Radio flowgraph.  This may be used either with command
62 # line applications or GUI applications.
63 #
64 class top_block(gr.top_block, pubsub):
65     def __init__(self, options, args):
66         gr.top_block.__init__(self)
67         pubsub.__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
79         self[BB_FREQ_KEY] = 0
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):
92             self[key] = self[key]
93         self[TYPE_KEY] = options.type #set type last
94
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)
103
104     def _set_tx_amplitude(self, ampl):
105         """
106         Sets the transmit amplitude sent to the USRP
107         @param ampl the amplitude or None for automatic
108         """
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]))
112
113     def set_interp(self, interp):
114         if not self._u.set_interp(interp):
115             raise RuntimeError("Failed to set interpolation rate %i" % (interp,))
116
117         if self._verbose:
118             print "USRP interpolation rate:", interp
119             print "USRP IF bandwidth: %sHz" % (n2s(self[SAMP_RATE_KEY]),)
120
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])
129         else:
130             return True # Waveform not yet set
131         
132         if self._verbose: print "Set interpolation rate to:", interp
133         return True
134
135     def set_gain(self, gain):
136         if gain is None:
137             g = self[GAIN_RANGE_KEY]
138             gain = float(g[0]+g[1])/2
139             if self._verbose:
140                 print "Using auto-calculated mid-point TX gain"
141             self[GAIN_KEY] = gain
142             return
143         self._u.set_gain(gain)
144         if self._verbose:
145             print "Set TX gain to:", gain
146
147     def set_freq(self, target_freq):
148
149         if target_freq is None:
150             f = self[FREQ_RANGE_KEY]
151             target_freq = float(f[0]+f[1])/2.0
152             if self._verbose:
153                 print "Using auto-calculated mid-point frequency"
154             self[TX_FREQ_KEY] = target_freq
155             return
156
157         tr = self._u.set_center_freq(target_freq)
158         fs = "%sHz" % (n2s(target_freq),)
159         if tr is not None:
160             self._freq = target_freq
161             self[DDC_FREQ_KEY] = tr.dxc_freq
162             self[BB_FREQ_KEY] = tr.baseband_freq
163             if self._verbose:
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." 
169         return tr
170
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]
179         return True
180
181     def set_waveform2_freq(self, freq):
182         if freq is None:
183             self[WAVEFORM2_FREQ_KEY] = -self[WAVEFORM_FREQ_KEY]
184             return
185         if self[TYPE_KEY] == "2tone":
186             self._src2.set_frequency(freq)
187         elif self[TYPE_KEY] == "sweep":
188             self._src1.set_frequency(freq)
189         return True
190
191     def set_waveform(self, type):
192         self.lock()
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],
204                                          gr.GR_SIN_WAVE,
205                                          self[WAVEFORM_FREQ_KEY],
206                                          self[AMPLITUDE_KEY]/2.0,
207                                          0)
208             if(self[WAVEFORM2_FREQ_KEY] is None):
209                 self[WAVEFORM2_FREQ_KEY] = -self[WAVEFORM_FREQ_KEY]
210
211             self._src2 = gr.sig_source_c(self[SAMP_RATE_KEY],
212                                          gr.GR_SIN_WAVE,
213                                          self[WAVEFORM2_FREQ_KEY],
214                                          self[AMPLITUDE_KEY]/2.0,
215                                          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
226
227             self._src1 = gr.sig_source_f(self[SAMP_RATE_KEY],
228                                          gr.GR_TRI_WAVE,
229                                          self[WAVEFORM2_FREQ_KEY],
230                                          1.0,
231                                          -0.5)
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)
235         else:
236             raise RuntimeError("Unknown waveform type")
237
238         self.connect(self._src, self._u)
239         self.unlock()
240
241         if self._verbose:
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]
253
254
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
258             return False
259
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)
267         else:
268             return True # Waveform not yet set
269         
270         if self._verbose: print "Set amplitude to:", amplitude
271         return True
272
273 def get_options():
274     usage="%prog: [options]"
275
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]")
302
303     (options, args) = parser.parse_args()
304
305     return (options, args)
306
307 # If this script is executed, the following runs. If it is imported, the below does not run.
308 def main():
309     if gr.enable_realtime_scheduling() != gr.RT_OK:
310         print "Note: failed to enable realtime scheduling, continuing"
311     
312     # Grab command line options and create top block
313     try:
314         (options, args) = get_options()
315         tb = top_block(options, args)
316
317     except RuntimeError, e:
318         print e
319         sys.exit(1)
320
321     tb.start()
322     raw_input('Press Enter to quit: ')
323     tb.stop()
324     tb.wait()
325
326 # Make sure to create the top block (tb) within a function:
327 # That code in main will allow tb to go out of scope on return,
328 # which will call the decontructor on usrp and stop transmit.
329 # Whats odd is that grc works fine with tb in the __main__,
330 # perhaps its because the try/except clauses around tb.
331 if __name__ == "__main__": main()