From 15d3699d72df6a473ee0b4dc23fc1920a055e3fa Mon Sep 17 00:00:00 2001 From: Dimitri Stolnikov Date: Sat, 28 Dec 2013 00:56:11 +0100 Subject: [PATCH] apps/osmocom_fft: gui to set DC offset / IQ imbalance correction mode --- apps/osmocom_fft | 272 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 249 insertions(+), 23 deletions(-) diff --git a/apps/osmocom_fft b/apps/osmocom_fft index 7a7168d..3847d1c 100755 --- a/apps/osmocom_fft +++ b/apps/osmocom_fft @@ -66,10 +66,14 @@ class app_top_block(stdgui2.std_top_block, pubsub): 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, + parser.add_option("-c", "--freq-corr", type="eng_float", default=None, help="Set frequency correction (ppm)") parser.add_option("-g", "--gain", type="eng_float", default=None, help="Set gain in dB (default is midpoint)") + parser.add_option("", "--dc-offset-mode", type="int", default=None, + help="Set the RX frontend DC offset correction mode") + parser.add_option("", "--iq-balance-mode", type="int", default=None, + help="Set the RX frontend IQ imbalance correction mode") parser.add_option("-W", "--waterfall", action="store_true", default=False, help="Enable waterfall display") parser.add_option("-F", "--fosphor", action="store_true", default=False, @@ -78,7 +82,7 @@ class app_top_block(stdgui2.std_top_block, pubsub): help="Enable oscilloscope display") parser.add_option("", "--avg-alpha", type="eng_float", default=1e-1, help="Set fftsink averaging factor, default=[%default]") - parser.add_option ("", "--averaging", action="store_true", default=False, + parser.add_option("", "--averaging", action="store_true", default=False, help="Enable fftsink averaging, default=[%default]") parser.add_option("", "--ref-scale", type="eng_float", default=1.0, help="Set dBFS=0dB input value, default=[%default]") @@ -158,6 +162,15 @@ class app_top_block(stdgui2.std_top_block, pubsub): self[CENTER_FREQ_KEY] = options.center_freq self[FREQ_CORR_KEY] = options.freq_corr + self.dc_offset_mode = options.dc_offset_mode + self.iq_balance_mode = options.iq_balance_mode + + # initialize reasonable defaults for DC / IQ correction + self['dc_offset_real'] = 0 + self['dc_offset_imag'] = 0 + self['iq_balance_mag'] = 0 + self['iq_balance_pha'] = 0 + #subscribe set methods self.subscribe(SAMP_RATE_KEY, self.set_sample_rate) @@ -168,6 +181,11 @@ class app_top_block(stdgui2.std_top_block, pubsub): self.subscribe(CENTER_FREQ_KEY, self.set_freq) self.subscribe(FREQ_CORR_KEY, self.set_freq_corr) + self.subscribe('dc_offset_real', self.set_dc_offset) + self.subscribe('dc_offset_imag', self.set_dc_offset) + self.subscribe('iq_balance_mag', self.set_iq_balance) + self.subscribe('iq_balance_pha', self.set_iq_balance) + #force update on pubsub keys #for key in (SAMP_RATE_KEY, BWIDTH_KEY, CENTER_FREQ_KEY, FREQ_CORR_KEY): #print key, "=", self[key] @@ -209,6 +227,12 @@ class app_top_block(stdgui2.std_top_block, pubsub): self._build_gui(vbox) + if self.dc_offset_mode != None: + self.set_dc_offset_mode(self.dc_offset_mode) + + if self.iq_balance_mode != None: + self.set_iq_balance_mode(self.iq_balance_mode) + # set initial values if not(self.set_freq(options.center_freq)): self._set_status_msg("Failed to set initial frequency") @@ -276,28 +300,30 @@ class app_top_block(stdgui2.std_top_block, pubsub): except AssertionError: pass - corr_hbox.AddSpacer(3) - forms.text_box( - parent=self.panel, sizer=corr_hbox, - label='Freq. Correction (ppm)', - proportion=1, - converter=forms.float_converter(), - ps=self, - key=FREQ_CORR_KEY, - ) - corr_hbox.AddSpacer(5) + if self[FREQ_CORR_KEY] != None: # show frequency correction scrollbar - forms.slider( - parent=self.panel, sizer=corr_hbox, - proportion=3, - ps=self, - key=FREQ_CORR_KEY, - minimum=-100, - maximum=+100, - num_steps=2010, - step_size=0.1, - ) - corr_hbox.AddSpacer(3) + corr_hbox.AddSpacer(3) + forms.text_box( + parent=self.panel, sizer=corr_hbox, + label='Freq. Correction (ppm)', + proportion=1, + converter=forms.float_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=2010, + step_size=0.1, + ) + corr_hbox.AddSpacer(3) ################################################## # Gain controls @@ -427,6 +453,206 @@ class app_top_block(stdgui2.std_top_block, pubsub): #) #sr_hbox.AddSpacer(3) + ################################################## + # DC Offset controls + ################################################## + + if self.dc_offset_mode != None: + + dc_offset_vbox = forms.static_box_sizer(parent=self.panel, + label="DC Offset Correction", + orient=wx.VERTICAL, + bold=True) + dc_offset_vbox.AddSpacer(3) + # First row of sample rate controls + dc_offset_hbox = wx.BoxSizer(wx.HORIZONTAL) + dc_offset_vbox.Add(dc_offset_hbox, 0, wx.EXPAND) + dc_offset_vbox.AddSpacer(3) + + # Add frequency controls to top window sizer + vbox.Add(dc_offset_vbox, 0, wx.EXPAND) + vbox.AddSpacer(3) + + self.dc_offset_mode_chooser = forms.radio_buttons( + parent=self.panel, + value=self.dc_offset_mode, + callback=self.set_dc_offset_mode, + label='', + choices=[0, 1, 2], + labels=["Off", "Manual", "Auto"], + style=wx.RA_HORIZONTAL, + ) + dc_offset_hbox.Add(self.dc_offset_mode_chooser) + dc_offset_hbox.AddSpacer(3) + + dc_offset_hbox.AddSpacer(3) + self.dc_offset_real_text = forms.text_box( + parent=self.panel, sizer=dc_offset_hbox, + label='Real', + proportion=1, + converter=forms.float_converter(), + ps=self, + key='dc_offset_real', + ) + dc_offset_hbox.AddSpacer(3) + + self.dc_offset_real_slider = forms.slider( + parent=self.panel, sizer=dc_offset_hbox, + proportion=3, + minimum=-1, + maximum=+1, + step_size=0.001, + ps=self, + key='dc_offset_real', + ) + dc_offset_hbox.AddSpacer(3) + + dc_offset_hbox.AddSpacer(3) + self.dc_offset_imag_text = forms.text_box( + parent=self.panel, sizer=dc_offset_hbox, + label='Imag', + proportion=1, + converter=forms.float_converter(), + ps=self, + key='dc_offset_imag', + ) + dc_offset_hbox.AddSpacer(3) + + self.dc_offset_imag_slider = forms.slider( + parent=self.panel, sizer=dc_offset_hbox, + proportion=3, + minimum=-1, + maximum=+1, + step_size=0.001, + ps=self, + key='dc_offset_imag', + ) + dc_offset_hbox.AddSpacer(3) + + ################################################## + # IQ Imbalance controls + ################################################## + + if self.iq_balance_mode != None: + + iq_balance_vbox = forms.static_box_sizer(parent=self.panel, + label="IQ Imbalance Correction", + orient=wx.VERTICAL, + bold=True) + iq_balance_vbox.AddSpacer(3) + # First row of sample rate controls + iq_balance_hbox = wx.BoxSizer(wx.HORIZONTAL) + iq_balance_vbox.Add(iq_balance_hbox, 0, wx.EXPAND) + iq_balance_vbox.AddSpacer(3) + + # Add frequency controls to top window sizer + vbox.Add(iq_balance_vbox, 0, wx.EXPAND) + vbox.AddSpacer(3) + + self.iq_balance_mode_chooser = forms.radio_buttons( + parent=self.panel, + value=self.iq_balance_mode, + callback=self.set_iq_balance_mode, + label='', + choices=[0, 1, 2], + labels=["Off", "Manual", "Auto"], + style=wx.RA_HORIZONTAL, + ) + iq_balance_hbox.Add(self.iq_balance_mode_chooser) + iq_balance_hbox.AddSpacer(3) + + iq_balance_hbox.AddSpacer(3) + self.iq_balance_mag_text = forms.text_box( + parent=self.panel, sizer=iq_balance_hbox, + label='Mag', + proportion=1, + converter=forms.float_converter(), + ps=self, + key='iq_balance_mag', + ) + iq_balance_hbox.AddSpacer(3) + + self.iq_balance_mag_slider = forms.slider( + parent=self.panel, sizer=iq_balance_hbox, + proportion=3, + minimum=-1, + maximum=+1, + step_size=0.001, + ps=self, + key='iq_balance_mag', + ) + iq_balance_hbox.AddSpacer(3) + + iq_balance_hbox.AddSpacer(3) + self.iq_balance_pha_text = forms.text_box( + parent=self.panel, sizer=iq_balance_hbox, + label='Phase', + proportion=1, + converter=forms.float_converter(), + ps=self, + key='iq_balance_pha', + ) + iq_balance_hbox.AddSpacer(3) + + self.iq_balance_pha_slider = forms.slider( + parent=self.panel, sizer=iq_balance_hbox, + proportion=3, + minimum=-1, + maximum=+1, + step_size=0.001, + ps=self, + key='iq_balance_pha', + ) + iq_balance_hbox.AddSpacer(3) + + def set_dc_offset_mode(self, dc_offset_mode): + if dc_offset_mode == 1: + self.dc_offset_real_text.Enable() + self.dc_offset_real_slider.Enable() + self.dc_offset_imag_text.Enable() + self.dc_offset_imag_slider.Enable() + + self.set_dc_offset(0) + else: + self.dc_offset_real_text.Disable() + self.dc_offset_real_slider.Disable() + self.dc_offset_imag_text.Disable() + self.dc_offset_imag_slider.Disable() + + self.dc_offset_mode = dc_offset_mode + self.src.set_dc_offset_mode(dc_offset_mode) + self.dc_offset_mode_chooser.set_value(self.dc_offset_mode) + + def set_dc_offset(self, value): + correction = complex( self['dc_offset_real'], self['dc_offset_imag'] ) + if self._verbose: + print "Set DC offset to", correction + self.src.set_dc_offset( correction ) + + def set_iq_balance_mode(self, iq_balance_mode): + if iq_balance_mode == 1: + self.iq_balance_mag_text.Enable() + self.iq_balance_mag_slider.Enable() + self.iq_balance_pha_text.Enable() + self.iq_balance_pha_slider.Enable() + + self.set_iq_balance(0) + else: + self.iq_balance_mag_text.Disable() + self.iq_balance_mag_slider.Disable() + self.iq_balance_pha_text.Disable() + self.iq_balance_pha_slider.Disable() + + self.iq_balance_mode = iq_balance_mode + self.src.set_iq_balance_mode(iq_balance_mode) + self.iq_balance_mode_chooser.set_value(self.iq_balance_mode) + + def set_iq_balance(self, value): + correction = complex( self['iq_balance_mag'], self['iq_balance_pha'] ) + if self._verbose: + print "Set IQ balance to", correction + self.src.set_iq_balance( correction ) + def set_sample_rate(self, samp_rate): samp_rate = self.src.set_sample_rate(samp_rate) if hasattr(self.scope, 'set_sample_rate'):