diff options
Diffstat (limited to 'apps/osmocom_fft')
-rwxr-xr-x | apps/osmocom_fft | 452 |
1 files changed, 337 insertions, 115 deletions
diff --git a/apps/osmocom_fft b/apps/osmocom_fft index 5449354..85406b1 100755 --- a/apps/osmocom_fft +++ b/apps/osmocom_fft @@ -20,9 +20,20 @@ # Boston, MA 02110-1301, USA. # +SAMP_RANGE_KEY = 'samp_range' +SAMP_RATE_KEY = 'samp_rate' +GAIN_KEY = lambda x: 'gain:'+x +BWIDTH_KEY = 'bwidth' +CENTER_FREQ_KEY = 'center_freq' +FREQ_CORR_KEY = 'freq_corr' +FREQ_RANGE_KEY = 'freq_range' +GAIN_RANGE_KEY = lambda x: 'gain_range:'+x +BWIDTH_RANGE_KEY = 'bwidth_range' + import osmosdr from gnuradio import gr, gru from gnuradio import eng_notation +from gnuradio.gr.pubsub import pubsub from gnuradio.eng_option import eng_option from optparse import OptionParser @@ -38,24 +49,27 @@ except ImportError: sys.stderr.write("Error importing GNU Radio's wxgui. Please make sure gr-wxgui is installed.\n") sys.exit(1) -class app_top_block(stdgui2.std_top_block): +class app_top_block(stdgui2.std_top_block, pubsub): def __init__(self, frame, panel, vbox, argv): stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv) + pubsub.__init__(self) self.frame = frame self.panel = panel parser = OptionParser(option_class=eng_option) parser.add_option("-a", "--args", type="string", default="", - help="device args, [default=%default]") + help="Device args, [default=%default]") parser.add_option("-A", "--antenna", type="string", default=None, - help="select Rx Antenna where appropriate") - parser.add_option("-s", "--samp-rate", type="eng_float", default=1e6, - help="set sample rate (bandwidth) [default=%default]") - parser.add_option("-f", "--freq", type="eng_float", default=None, - help="set frequency to FREQ", metavar="FREQ") + help="Select RX antenna where appropriate") + parser.add_option("-s", "--samp-rate", type="eng_float", default=None, + help="Set sample rate (bandwidth), minimum by default") + parser.add_option("-f", "--center-freq", type="eng_float", default=None, + help="Set frequency to FREQ", metavar="FREQ") + parser.add_option("-c", "--freq-corr", type="eng_float", default=0, + help="Set frequency correction (ppm)") parser.add_option("-g", "--gain", type="eng_float", default=None, - help="set gain in dB (default is midpoint)") + help="Set gain in dB (default is midpoint)") parser.add_option("-W", "--waterfall", action="store_true", default=False, help="Enable waterfall display") parser.add_option("-S", "--oscilloscope", action="store_true", default=False, @@ -70,30 +84,83 @@ class app_top_block(stdgui2.std_top_block): help="Set number of FFT bins [default=%default]") parser.add_option("", "--fft-rate", type="int", default=30, help="Set FFT update rate, [default=%default]") + parser.add_option("-v", "--verbose", action="store_true", default=False, + help="Use verbose console output [default=%default]") (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) self.options = options - self.show_debug_info = True + + self._verbose = options.verbose self.src = osmosdr.source_c(options.args) # Set the antenna if(options.antenna): - self.src.set_antenna(options.antenna, 0) + self.src.set_antenna(options.antenna) + + if options.samp_rate is None: + options.samp_rate = self.src.get_sample_rates().start() + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + r = self.src.get_gain_range() + options.gain = float(r.start()+r.stop())/2 + + if options.center_freq is None: + # if no freq was specified, use the mid-point in Hz + r = self.src.get_freq_range() + options.center_freq = float(r.start()+r.stop())/2 + + input_rate = self.src.set_sample_rate(options.samp_rate) + + self.src.set_gain(options.gain) + + #print self.src.get_sample_rates().to_pp_string() + + self.publish(SAMP_RANGE_KEY, self.src.get_sample_rates) + self.publish(FREQ_RANGE_KEY, self.src.get_freq_range) + + for name in self.get_gain_names(): + self.publish(GAIN_RANGE_KEY(name), (lambda self=self,name=name: self.src.get_gain_range(name))) + + self.publish(BWIDTH_RANGE_KEY, self.src.get_bandwidth_range) - self.src.set_sample_rate(options.samp_rate) - input_rate = self.src.get_sample_rate() + for name in self.get_gain_names(): + self.publish(GAIN_KEY(name), (lambda self=self,name=name: self.src.get_gain(name))) + + self.publish(BWIDTH_KEY, self.src.get_bandwidth) + + #initialize values from options + self[SAMP_RANGE_KEY] = self.src.get_sample_rates() + self[SAMP_RATE_KEY] = options.samp_rate + self[CENTER_FREQ_KEY] = options.center_freq + self[FREQ_CORR_KEY] = options.freq_corr + + #subscribe set methods + self.subscribe(SAMP_RATE_KEY, self.set_sample_rate) + + for name in self.get_gain_names(): + self.subscribe(GAIN_KEY(name), (lambda gain,self=self,name=name: self.set_named_gain(gain, name))) + + self.subscribe(BWIDTH_KEY, self.set_bandwidth) + self.subscribe(CENTER_FREQ_KEY, self.set_freq) + self.subscribe(FREQ_CORR_KEY, self.set_freq_corr) + + #force update on pubsub keys + for key in (SAMP_RATE_KEY, BWIDTH_KEY, CENTER_FREQ_KEY, FREQ_CORR_KEY): + print key, "=", self[key] + #self[key] = self[key] if options.waterfall: self.scope = \ - waterfallsink2.waterfall_sink_c (panel, fft_size=1024, + waterfallsink2.waterfallsrc_c (panel, fft_size=options.fft_size, sample_rate=input_rate) self.frame.SetMinSize((800, 420)) elif options.oscilloscope: - self.scope = scopesink2.scope_sink_c(panel, sample_rate=input_rate) + self.scope = scopesink2.scopesrc_c(panel, sample_rate=input_rate) self.frame.SetMinSize((800, 600)) else: self.scope = fftsink2.fft_sink_c (panel, @@ -107,7 +174,7 @@ class app_top_block(stdgui2.std_top_block): fft_rate=options.fft_rate) def fftsink_callback(x, y): self.set_freq(x) - + self.scope.set_callback(fftsink_callback) self.frame.SetMinSize((800, 420)) @@ -116,25 +183,8 @@ class app_top_block(stdgui2.std_top_block): self._build_gui(vbox) self._setup_events() - # set initial values - - if options.gain is None: - # if no gain was specified, use the mid-point in dB - g = self.src.get_gain_range() - options.gain = float(g.start()+g.stop())/2 - - if options.freq is None: - # if no freq was specified, use the mid-point - r = self.src.get_freq_range() - options.freq = float(r.start()+r.stop())/2 - - self.set_gain(options.gain) - - if self.show_debug_info: - self.myform['samprate'].set_value(self.src.get_sample_rate()) - - if not(self.set_freq(options.freq)): + if not(self.set_freq(options.center_freq)): self._set_status_msg("Failed to set initial frequency") def _set_status_msg(self, msg): @@ -142,99 +192,271 @@ class app_top_block(stdgui2.std_top_block): def _build_gui(self, vbox): - def _form_set_freq(kv): - return self.set_freq(kv['freq']) - - vbox.Add(self.scope.win, 10, wx.EXPAND) + vbox.Add(self.scope.win, 0, wx.EXPAND) + vbox.AddSpacer(3) # add control area at the bottom self.myform = myform = form.form() - hbox = wx.BoxSizer(wx.HORIZONTAL) - hbox.Add((4,0), 0, 0) - myform['freq'] = form.float_field( - parent=self.panel, sizer=hbox, label="Center freq", weight=1, - callback=myform.check_input_and_call(_form_set_freq, - self._set_status_msg)) - - hbox.Add((4,0), 0, 0) - g = self.src.get_gain_range() - - # some configurations don't have gain control - if g.stop() <= g.start(): - glow = 0.0 - ghigh = 1.0 - - else: - glow = g.start() - ghigh = g.stop() - - myform['gain'] = form.slider_field(parent=self.panel, - sizer=hbox, label="Gain", - weight=3, - min=int(glow), max=int(ghigh), - callback=self.set_gain) - hbox.Add((4,0), 0, 0) - vbox.Add(hbox, 0, wx.EXPAND) - - self._build_subpanel(vbox) - - def _build_subpanel(self, vbox_arg): - # build a secondary information panel (sometimes hidden) - # FIXME figure out how to have this be a subpanel that is always - # created, but has its visibility controlled by foo.Show(True/False) - - def _form_set_sample_rate(kv): - return self.set_sample_rate(kv['samprate']) - - if not(self.show_debug_info): - return - - panel = self.panel - vbox = vbox_arg - myform = self.myform - - hbox = wx.BoxSizer(wx.HORIZONTAL) - - hbox.Add((4,0), 0) - myform['samprate'] = form.float_field( - parent=panel, sizer=hbox, label="Sample Rate", - callback=myform.check_input_and_call(_form_set_sample_rate, - self._set_status_msg)) + ################################################## + # Frequency controls + ################################################## + fc_vbox = forms.static_box_sizer(parent=self.panel, + label="Center Frequency", + orient=wx.VERTICAL, + bold=True) + fc_vbox.AddSpacer(3) + # First row of frequency controls (center frequency) + freq_hbox = wx.BoxSizer(wx.HORIZONTAL) + fc_vbox.Add(freq_hbox, 0, wx.EXPAND) + fc_vbox.AddSpacer(5) + # Second row of frequency controls (freq. correction) + corr_hbox = wx.BoxSizer(wx.HORIZONTAL) + fc_vbox.Add(corr_hbox, 0, wx.EXPAND) + fc_vbox.AddSpacer(3) + + # Add frequency controls to top window sizer + vbox.Add(fc_vbox, 0, wx.EXPAND) vbox.AddSpacer(5) - - vbox.Add(hbox, 0, wx.EXPAND) + vbox.AddStretchSpacer() + + freq_hbox.AddSpacer(3) + forms.text_box( + parent=self.panel, sizer=freq_hbox, + label='Center Frequency (Hz)', + proportion=1, + converter=forms.float_converter(), + ps=self, + key=CENTER_FREQ_KEY, + ) + freq_hbox.AddSpacer(5) + + forms.slider( + parent=self.panel, sizer=freq_hbox, + proportion=3, + ps=self, + key=CENTER_FREQ_KEY, + minimum=self[FREQ_RANGE_KEY].start(), + maximum=self[FREQ_RANGE_KEY].stop(), + num_steps=101, + ) + freq_hbox.AddSpacer(3) + + corr_hbox.AddSpacer(3) + forms.text_box( + parent=self.panel, sizer=corr_hbox, + label='Freq. Correction (ppm)', + proportion=1, + converter=forms.int_converter(), + ps=self, + key=FREQ_CORR_KEY, + ) + corr_hbox.AddSpacer(5) + + forms.slider( + parent=self.panel, sizer=corr_hbox, + proportion=3, + ps=self, + key=FREQ_CORR_KEY, + minimum=-100, + maximum=+100, + num_steps=201, + step_size=1, + ) + corr_hbox.AddSpacer(3) + + ################################################## + # Gain controls + ################################################## + gc_vbox = forms.static_box_sizer(parent=self.panel, + label="Gain Settings", + orient=wx.VERTICAL, + bold=True) + gc_vbox.AddSpacer(3) + + # Add gain controls to top window sizer + vbox.Add(gc_vbox, 0, wx.EXPAND) vbox.AddSpacer(5) + vbox.AddStretchSpacer() + + for gain_name in self.get_gain_names(): + range = self[GAIN_RANGE_KEY(gain_name)] + gain = self[GAIN_KEY(gain_name)] + + #print gain_name, gain, range.to_pp_string() + if range.start() < range.stop(): + gain_hbox = wx.BoxSizer(wx.HORIZONTAL) + gc_vbox.Add(gain_hbox, 0, wx.EXPAND) + gc_vbox.AddSpacer(3) + + gain_hbox.AddSpacer(3) + forms.text_box( + parent=self.panel, sizer=gain_hbox, + proportion=1, + converter=forms.float_converter(), + ps=self, + key=GAIN_KEY(gain_name), + label=gain_name + " Gain (dB)", + ) + gain_hbox.AddSpacer(5) + forms.slider( + parent=self.panel, sizer=gain_hbox, + proportion=3, + ps=self, + key=GAIN_KEY(gain_name), + minimum=range.start(), + maximum=range.stop(), + step_size=range.step(), + ) + gain_hbox.AddSpacer(3) + + ################################################## + # Bandwidth controls + ################################################## + try: + + bw_range = self[BWIDTH_RANGE_KEY] + #print bw_range.to_pp_string() + #if bw_range.start() < bw_range.stop(): + if 0: + bwidth_vbox = forms.static_box_sizer(parent=self.panel, + label="Bandwidth", + orient=wx.VERTICAL, + bold=True) + bwidth_vbox.AddSpacer(3) + bwidth_hbox = wx.BoxSizer(wx.HORIZONTAL) + bwidth_vbox.Add(bwidth_hbox, 0, wx.EXPAND) + bwidth_vbox.AddSpacer(3) + + vbox.Add(bwidth_vbox, 0, wx.EXPAND) + vbox.AddSpacer(5) + vbox.AddStretchSpacer() + + bwidth_hbox.AddSpacer(3) + forms.text_box( + parent=self.panel, sizer=bwidth_hbox, + proportion=1, + converter=forms.float_converter(), + ps=self, + key=BWIDTH_KEY, + label="Bandwidth (Hz)", + ) + bwidth_hbox.AddSpacer(5) + forms.slider( + parent=self.panel, sizer=bwidth_hbox, + proportion=3, + ps=self, + key=BWIDTH_KEY, + minimum=bw_range.start(), + maximum=bw_range.stop(), + step_size=bw_range.step(), + ) + bwidth_hbox.AddSpacer(3) + + except RuntimeError: + pass + + + ################################################## + # Sample rate controls + ################################################## + sr_vbox = forms.static_box_sizer(parent=self.panel, + label="Sample Rate", + orient=wx.VERTICAL, + bold=True) + sr_vbox.AddSpacer(3) + # First row of sample rate controls + sr_hbox = wx.BoxSizer(wx.HORIZONTAL) + sr_vbox.Add(sr_hbox, 0, wx.EXPAND) + sr_vbox.AddSpacer(5) + + # Add frequency controls to top window sizer + vbox.Add(sr_vbox, 0, wx.EXPAND) + vbox.AddSpacer(5) + vbox.AddStretchSpacer() + + sr_hbox.AddSpacer(3) + forms.text_box( + parent=self.panel, sizer=sr_hbox, + label='Sample Rate (Hz)', + proportion=1, + converter=forms.float_converter(), + ps=self, + key=SAMP_RATE_KEY, + ) + sr_hbox.AddSpacer(5) + + #forms.slider( + # parent=self.panel, sizer=sr_hbox, + # proportion=3, + # ps=self, + # key=SAMP_RATE_KEY, + # minimum=self[SAMP_RANGE_KEY].start(), + # maximum=self[SAMP_RANGE_KEY].stop(), + # step_size=self[SAMP_RANGE_KEY].step(), + #) + #sr_hbox.AddSpacer(3) - def set_freq(self, target_freq): - """ - Set the center frequency we're interested in. - - @param target_freq: frequency in Hz - @rypte: bool - """ - actual_freq = self.src.set_center_freq(target_freq, 0) - self.myform['freq'].set_value(actual_freq) + def set_sample_rate(self, samp_rate): + samp_rate = self.src.set_sample_rate(samp_rate) + self.scope.set_sample_rate(samp_rate) + if self._verbose: + print "Set sample rate to:", samp_rate + return samp_rate + + def get_gain_names(self): + return self.src.get_gain_names() + + def set_named_gain(self, gain, name): + if gain is None: + g = self[GAIN_RANGE_KEY(name)] + gain = float(g.start()+g.stop())/2 + if self._verbose: + print "Using auto-calculated mid-point gain" + self[GAIN_KEY(name)] = gain + return - if not self.options.oscilloscope: - self.scope.set_baseband_freq(actual_freq) + gain = self.src.set_gain(gain, name) + if self._verbose: + print "Set " + name + " gain to:", gain + + def set_bandwidth(self, bw): + bw = self.src.set_bandwidth(bw) + if self._verbose: + print "Set bandwidth to:", bw + + def set_freq(self, freq): + if freq is None: + f = self[FREQ_RANGE_KEY] + freq = float(f.start()+f.stop())/2.0 + if self._verbose: + print "Using auto-calculated mid-point frequency" + self[CENTER_FREQ_KEY] = freq + return - return True + freq = self.src.set_center_freq(freq) - def set_gain(self, gain): - if self.myform.has_key('gain'): - self.myform['gain'].set_value(gain) # update displayed value - self.src.set_gain(gain, 0) + if not self.options.oscilloscope: + self.scope.set_baseband_freq(freq) + + if freq is not None: + if self._verbose: + print "Set center frequency to", freq + elif self._verbose: + print "Failed to set freq." + return freq + + def set_freq_corr(self, ppm): + if ppm is None: + ppm = 0.0 + if self._verbose: + print "Using frequency corrrection of", ppm + self[FREQ_CORR_KEY] = ppm + return - def set_sample_rate(self, samp_rate): - ok = self.src.set_sample_rate(samp_rate) - input_rate = self.src.get_sample_rate() - self.scope.set_sample_rate(input_rate) - if self.show_debug_info: # update displayed values - self.myform['samprate'].set_value(self.src.get_sample_rate()) - - # set_sample_rate never fails; always falls back to closest requested. - return True + ppm = self.src.set_freq_corr(ppm) + if self._verbose: + print "Set frequency correction to:", ppm def _setup_events(self): if not self.options.waterfall and not self.options.oscilloscope: |