gr-wxgui: Renamed "emulate analog" feature to "use persistence"
[debian/gnuradio] / gr-wxgui / src / python / fft_window.py
index 5f48e8324e3538d3ac1c7c31196e42d8d1f16282..a460fe995de617a2e412a031d0989d84c82bf914 100644 (file)
@@ -1,5 +1,5 @@
 #
 #
-# Copyright 2008 Free Software Foundation, Inc.
+# Copyright 2008, 2009 Free Software Foundation, Inc.
 #
 # This file is part of GNU Radio
 #
 #
 # This file is part of GNU Radio
 #
@@ -29,103 +29,184 @@ import numpy
 import math
 import pubsub
 from constants import *
 import math
 import pubsub
 from constants import *
+from gnuradio import gr #for gr.prefs
+import forms
 
 ##################################################
 # Constants
 ##################################################
 SLIDER_STEPS = 100
 AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP = -3, 0
 
 ##################################################
 # Constants
 ##################################################
 SLIDER_STEPS = 100
 AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP = -3, 0
+PERSIST_ALPHA_MIN_EXP, PERSIST_ALPHA_MAX_EXP = -2, 0
 DEFAULT_WIN_SIZE = (600, 300)
 DEFAULT_WIN_SIZE = (600, 300)
-DEFAULT_FRAME_RATE = 30
-DIV_LEVELS = (1, 2, 5, 10, 20)
-FFT_PLOT_COLOR_SPEC = (0, 0, 1)
-PEAK_VALS_COLOR_SPEC = (0, 1, 0)
-NO_PEAK_VALS = list()
+DEFAULT_FRAME_RATE = gr.prefs().get_long('wxgui', 'fft_rate', 30)
+DB_DIV_MIN, DB_DIV_MAX = 1, 20
+FFT_PLOT_COLOR_SPEC = (0.3, 0.3, 1.0)
+PEAK_VALS_COLOR_SPEC = (0.0, 0.8, 0.0)
+EMPTY_TRACE = list()
+TRACES = ('A', 'B')
+TRACES_COLOR_SPEC = {
+       'A': (1.0, 0.0, 0.0),
+       'B': (0.8, 0.0, 0.8),
+}
 
 ##################################################
 # FFT window control panel
 ##################################################
 class control_panel(wx.Panel):
 
 ##################################################
 # FFT window control panel
 ##################################################
 class control_panel(wx.Panel):
-       """!
+       """
        A control panel with wx widgits to control the plotter and fft block chain.
        """
 
        def __init__(self, parent):
        A control panel with wx widgits to control the plotter and fft block chain.
        """
 
        def __init__(self, parent):
-               """!
+               """
                Create a new control panel.
                @param parent the wx parent window
                """
                self.parent = parent
                Create a new control panel.
                @param parent the wx parent window
                """
                self.parent = parent
-               wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
+               wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
+               parent[SHOW_CONTROL_PANEL_KEY] = True
+               parent.subscribe(SHOW_CONTROL_PANEL_KEY, self.Show)
                control_box = wx.BoxSizer(wx.VERTICAL)
                control_box = wx.BoxSizer(wx.VERTICAL)
-               #checkboxes for average and peak hold
                control_box.AddStretchSpacer()
                control_box.AddStretchSpacer()
-               control_box.Add(common.LabelText(self, 'Options'), 0, wx.ALIGN_CENTER)
-               self.average_check_box = common.CheckBoxController(self, 'Average', parent.ext_controller, parent.average_key)
-               control_box.Add(self.average_check_box, 0, wx.EXPAND)
-               self.peak_hold_check_box = common.CheckBoxController(self, 'Peak Hold', parent, PEAK_HOLD_KEY)
-               control_box.Add(self.peak_hold_check_box, 0, wx.EXPAND)
-               control_box.AddSpacer(2)
-               self.avg_alpha_slider = common.LogSliderController(
-                       self, 'Avg Alpha',
-                       AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS,
-                       parent.ext_controller, parent.avg_alpha_key,
-                       formatter=lambda x: ': %.4f'%x,
+               #checkboxes for average and peak hold
+               options_box = forms.static_box_sizer(
+                       parent=self, sizer=control_box, label='Trace Options',
+                       bold=True, orient=wx.VERTICAL,
+               )
+               forms.check_box(
+                       sizer=options_box, parent=self, label='Peak Hold',
+                       ps=parent, key=PEAK_HOLD_KEY,
+               )
+               forms.check_box(
+                       sizer=options_box, parent=self, label='Average',
+                       ps=parent, key=AVERAGE_KEY,
+               )
+               #static text and slider for averaging
+               avg_alpha_text = forms.static_text(
+                       sizer=options_box, parent=self, label='Avg Alpha',
+                       converter=forms.float_converter(lambda x: '%.4f'%x),
+                       ps=parent, key=AVG_ALPHA_KEY, width=50,
+               )
+               avg_alpha_slider = forms.log_slider(
+                       sizer=options_box, parent=self,
+                       min_exp=AVG_ALPHA_MIN_EXP,
+                       max_exp=AVG_ALPHA_MAX_EXP,
+                       num_steps=SLIDER_STEPS,
+                       ps=parent, key=AVG_ALPHA_KEY,
+               )
+               for widget in (avg_alpha_text, avg_alpha_slider):
+                       parent.subscribe(AVERAGE_KEY, widget.Enable)
+                       widget.Enable(parent[AVERAGE_KEY])
+                       parent.subscribe(AVERAGE_KEY, widget.ShowItems)
+                        #allways show initially, so room is reserved for them
+                       widget.ShowItems(True) # (parent[AVERAGE_KEY])
+
+                parent.subscribe(AVERAGE_KEY, self._update_layout)
+
+               forms.check_box(
+                       sizer=options_box, parent=self, label='Persistence',
+                       ps=parent, key=USE_PERSISTENCE_KEY,
+               )
+               #static text and slider for persist alpha
+               persist_alpha_text = forms.static_text(
+                       sizer=options_box, parent=self, label='Persist Alpha',
+                       converter=forms.float_converter(lambda x: '%.4f'%x),
+                       ps=parent, key=PERSIST_ALPHA_KEY, width=50,
                )
                )
-               parent.ext_controller.subscribe(parent.average_key, self.avg_alpha_slider.Enable)
-               control_box.Add(self.avg_alpha_slider, 0, wx.EXPAND)
+               persist_alpha_slider = forms.log_slider(
+                       sizer=options_box, parent=self,
+                       min_exp=PERSIST_ALPHA_MIN_EXP,
+                       max_exp=PERSIST_ALPHA_MAX_EXP,
+                       num_steps=SLIDER_STEPS,
+                       ps=parent, key=PERSIST_ALPHA_KEY,
+               )
+               for widget in (persist_alpha_text, persist_alpha_slider):
+                       parent.subscribe(USE_PERSISTENCE_KEY, widget.Enable)
+                       widget.Enable(parent[USE_PERSISTENCE_KEY])
+                       parent.subscribe(USE_PERSISTENCE_KEY, widget.ShowItems)
+                        #allways show initially, so room is reserved for them
+                       widget.ShowItems(True) # (parent[USE_PERSISTENCE_KEY])
+               
+                parent.subscribe(USE_PERSISTENCE_KEY, self._update_layout)
+
+               #trace menu
+               for trace in TRACES:
+                       trace_box = wx.BoxSizer(wx.HORIZONTAL)
+                       options_box.Add(trace_box, 0, wx.EXPAND)
+                       forms.check_box(
+                               sizer=trace_box, parent=self,
+                               ps=parent, key=TRACE_SHOW_KEY+trace,
+                               label='Trace %s'%trace,
+                       )
+                       trace_box.AddSpacer(10)
+                       forms.single_button(
+                               sizer=trace_box, parent=self,
+                               ps=parent, key=TRACE_STORE_KEY+trace,
+                               label='Store', style=wx.BU_EXACTFIT,
+                       )
+                       trace_box.AddSpacer(10)
                #radio buttons for div size
                control_box.AddStretchSpacer()
                #radio buttons for div size
                control_box.AddStretchSpacer()
-               control_box.Add(common.LabelText(self, 'Set dB/div'), 0, wx.ALIGN_CENTER)
-               radio_box = wx.BoxSizer(wx.VERTICAL)
-               self.radio_buttons = list()
-               for y_per_div in DIV_LEVELS:
-                       radio_button = wx.RadioButton(self, -1, "%d dB/div"%y_per_div)
-                       radio_button.Bind(wx.EVT_RADIOBUTTON, self._on_y_per_div)
-                       self.radio_buttons.append(radio_button)
-                       radio_box.Add(radio_button, 0, wx.ALIGN_LEFT)
-               parent.subscribe(Y_PER_DIV_KEY, self._on_set_y_per_div)
-               control_box.Add(radio_box, 0, wx.EXPAND)
+               y_ctrl_box = forms.static_box_sizer(
+                       parent=self, sizer=control_box, label='Axis Options',
+                       bold=True, orient=wx.VERTICAL,
+               )
+               forms.incr_decr_buttons(
+                       parent=self, sizer=y_ctrl_box, label='dB/Div',
+                       on_incr=self._on_incr_db_div, on_decr=self._on_decr_db_div,
+               )
                #ref lvl buttons
                #ref lvl buttons
-               control_box.AddStretchSpacer()
-               control_box.Add(common.LabelText(self, 'Set Ref Level'), 0, wx.ALIGN_CENTER)
-               control_box.AddSpacer(2)
-               self._ref_lvl_buttons = common.IncrDecrButtons(self, self._on_incr_ref_level, self._on_decr_ref_level)
-               control_box.Add(self._ref_lvl_buttons, 0, wx.ALIGN_CENTER)
+               forms.incr_decr_buttons(
+                       parent=self, sizer=y_ctrl_box, label='Ref Level',
+                       on_incr=self._on_incr_ref_level, on_decr=self._on_decr_ref_level,
+               )
+               y_ctrl_box.AddSpacer(2)
                #autoscale
                #autoscale
-               control_box.AddStretchSpacer()
-               self.autoscale_button = wx.Button(self, label='Autoscale', style=wx.BU_EXACTFIT)
-               self.autoscale_button.Bind(wx.EVT_BUTTON, self.parent.autoscale)
-               control_box.Add(self.autoscale_button, 0, wx.EXPAND)
+               forms.single_button(
+                       sizer=y_ctrl_box, parent=self, label='Autoscale',
+                       callback=self.parent.autoscale,
+               )
                #run/stop
                #run/stop
-               self.run_button = common.ToggleButtonController(self, parent, RUNNING_KEY, 'Stop', 'Run')
-               control_box.Add(self.run_button, 0, wx.EXPAND)
+               control_box.AddStretchSpacer()
+               forms.toggle_button(
+                       sizer=control_box, parent=self,
+                       true_label='Stop', false_label='Run',
+                       ps=parent, key=RUNNING_KEY,
+               )
                #set sizer
                self.SetSizerAndFit(control_box)
 
                #set sizer
                self.SetSizerAndFit(control_box)
 
+               #mouse wheel event
+               def on_mouse_wheel(event):
+                       if event.GetWheelRotation() < 0: self._on_incr_ref_level(event)
+                       else: self._on_decr_ref_level(event)
+               parent.plotter.Bind(wx.EVT_MOUSEWHEEL, on_mouse_wheel)
+
        ##################################################
        # Event handlers
        ##################################################
        ##################################################
        # Event handlers
        ##################################################
-       def _on_set_y_per_div(self, y_per_div):
-               try:
-                       index = list(DIV_LEVELS).index(y_per_div)
-                       self.radio_buttons[index].SetValue(True)
-               except: pass
-       def _on_y_per_div(self, event):
-               selected_radio_button = filter(lambda rb: rb.GetValue(), self.radio_buttons)[0]
-               index = self.radio_buttons.index(selected_radio_button)
-               self.parent[Y_PER_DIV_KEY] = DIV_LEVELS[index]
        def _on_incr_ref_level(self, event):
        def _on_incr_ref_level(self, event):
-               self.parent.set_ref_level(
-                       self.parent[REF_LEVEL_KEY] + self.parent[Y_PER_DIV_KEY])
+               self.parent[REF_LEVEL_KEY] = self.parent[REF_LEVEL_KEY] + self.parent[Y_PER_DIV_KEY]
        def _on_decr_ref_level(self, event):
        def _on_decr_ref_level(self, event):
-               self.parent.set_ref_level(
-                       self.parent[REF_LEVEL_KEY] - self.parent[Y_PER_DIV_KEY])
+               self.parent[REF_LEVEL_KEY] = self.parent[REF_LEVEL_KEY] - self.parent[Y_PER_DIV_KEY]
+       def _on_incr_db_div(self, event):
+               self.parent[Y_PER_DIV_KEY] = min(DB_DIV_MAX, common.get_clean_incr(self.parent[Y_PER_DIV_KEY]))
+       def _on_decr_db_div(self, event):
+               self.parent[Y_PER_DIV_KEY] = max(DB_DIV_MIN, common.get_clean_decr(self.parent[Y_PER_DIV_KEY]))
+       ##################################################
+       # subscriber handlers
+       ##################################################
+        def _update_layout(self,key):
+          # Just ignore the key value we get
+          # we only need to now that the visability or size of something has changed
+          self.parent.Layout()
+          #self.parent.Fit()          
 
 ##################################################
 # FFT window with plotter and control panel
 ##################################################
 
 ##################################################
 # FFT window with plotter and control panel
 ##################################################
-class fft_window(wx.Panel, pubsub.pubsub, common.prop_setter):
+class fft_window(wx.Panel, pubsub.pubsub):
        def __init__(
                self,
                parent,
        def __init__(
                self,
                parent,
@@ -143,75 +224,99 @@ class fft_window(wx.Panel, pubsub.pubsub, common.prop_setter):
                avg_alpha_key,
                peak_hold,
                msg_key,
                avg_alpha_key,
                peak_hold,
                msg_key,
+                use_persistence,
+                persist_alpha,
        ):
        ):
+
                pubsub.pubsub.__init__(self)
                pubsub.pubsub.__init__(self)
-               #ensure y_per_div
-               if y_per_div not in DIV_LEVELS: y_per_div = DIV_LEVELS[0]
                #setup
                #setup
-               self.ext_controller = controller
+               self.samples = EMPTY_TRACE
                self.real = real
                self.fft_size = fft_size
                self.real = real
                self.fft_size = fft_size
-               self.sample_rate_key = sample_rate_key
-               self.average_key = average_key
-               self.avg_alpha_key = avg_alpha_key
                self._reset_peak_vals()
                self._reset_peak_vals()
+               self._traces = dict()
+               #proxy the keys
+               self.proxy(MSG_KEY, controller, msg_key)
+               self.proxy(AVERAGE_KEY, controller, average_key)
+               self.proxy(AVG_ALPHA_KEY, controller, avg_alpha_key)
+               self.proxy(SAMPLE_RATE_KEY, controller, sample_rate_key)
+               #initialize values
+               self[PEAK_HOLD_KEY] = peak_hold
+               self[Y_PER_DIV_KEY] = y_per_div
+               self[Y_DIVS_KEY] = y_divs
+               self[X_DIVS_KEY] = 8 #approximate
+               self[REF_LEVEL_KEY] = ref_level
+               self[BASEBAND_FREQ_KEY] = baseband_freq
+               self[RUNNING_KEY] = True
+               self[USE_PERSISTENCE_KEY] = use_persistence
+               self[PERSIST_ALPHA_KEY] = persist_alpha
+               for trace in TRACES:
+                       #a function that returns a function
+                       #so the function wont use local trace
+                       def new_store_trace(my_trace):
+                               def store_trace(*args):
+                                       self._traces[my_trace] = self.samples
+                                       self.update_grid()
+                               return store_trace
+                       def new_toggle_trace(my_trace):
+                               def toggle_trace(toggle):
+                                       #do an automatic store if toggled on and empty trace
+                                       if toggle and not len(self._traces[my_trace]):
+                                               self._traces[my_trace] = self.samples
+                                       self.update_grid()
+                               return toggle_trace
+                       self._traces[trace] = EMPTY_TRACE
+                       self[TRACE_STORE_KEY+trace] = False
+                       self[TRACE_SHOW_KEY+trace] = False
+                       self.subscribe(TRACE_STORE_KEY+trace, new_store_trace(trace))
+                       self.subscribe(TRACE_SHOW_KEY+trace, new_toggle_trace(trace))
                #init panel and plot
                #init panel and plot
-               wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER)
+               wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER)
                self.plotter = plotter.channel_plotter(self)
                self.plotter.SetSize(wx.Size(*size))
                self.plotter.set_title(title)
                self.plotter = plotter.channel_plotter(self)
                self.plotter.SetSize(wx.Size(*size))
                self.plotter.set_title(title)
+               self.plotter.enable_legend(True)
                self.plotter.enable_point_label(True)
                self.plotter.enable_point_label(True)
+               self.plotter.enable_grid_lines(True)
+                self.plotter.set_use_persistence(use_persistence)
+                self.plotter.set_persist_alpha(persist_alpha)
                #setup the box with plot and controls
                self.control_panel = control_panel(self)
                main_box = wx.BoxSizer(wx.HORIZONTAL)
                main_box.Add(self.plotter, 1, wx.EXPAND)
                main_box.Add(self.control_panel, 0, wx.EXPAND)
                self.SetSizerAndFit(main_box)
                #setup the box with plot and controls
                self.control_panel = control_panel(self)
                main_box = wx.BoxSizer(wx.HORIZONTAL)
                main_box.Add(self.plotter, 1, wx.EXPAND)
                main_box.Add(self.control_panel, 0, wx.EXPAND)
                self.SetSizerAndFit(main_box)
-               #initial setup
-               self.ext_controller[self.average_key] = self.ext_controller[self.average_key]
-               self.ext_controller[self.avg_alpha_key] = self.ext_controller[self.avg_alpha_key]
-               self._register_set_prop(self, PEAK_HOLD_KEY, peak_hold)
-               self._register_set_prop(self, Y_PER_DIV_KEY, y_per_div)
-               self._register_set_prop(self, Y_DIVS_KEY, y_divs)
-               self._register_set_prop(self, X_DIVS_KEY, 8) #approximate
-               self._register_set_prop(self, REF_LEVEL_KEY, ref_level)
-               self._register_set_prop(self, BASEBAND_FREQ_KEY, baseband_freq)
-               self._register_set_prop(self, RUNNING_KEY, True)
                #register events
                #register events
-               self.subscribe(PEAK_HOLD_KEY, self.plotter.enable_legend)
-               self.ext_controller.subscribe(AVERAGE_KEY, lambda x: self._reset_peak_vals())
-               self.ext_controller.subscribe(msg_key, self.handle_msg)
-               self.ext_controller.subscribe(self.sample_rate_key, self.update_grid)
+               self.subscribe(AVERAGE_KEY, self._reset_peak_vals)
+               self.subscribe(MSG_KEY, self.handle_msg)
+               self.subscribe(SAMPLE_RATE_KEY, self.update_grid)
                for key in (
                        BASEBAND_FREQ_KEY,
                        Y_PER_DIV_KEY, X_DIVS_KEY,
                        Y_DIVS_KEY, REF_LEVEL_KEY,
                ): self.subscribe(key, self.update_grid)
                for key in (
                        BASEBAND_FREQ_KEY,
                        Y_PER_DIV_KEY, X_DIVS_KEY,
                        Y_DIVS_KEY, REF_LEVEL_KEY,
                ): self.subscribe(key, self.update_grid)
+               self.subscribe(USE_PERSISTENCE_KEY, self.plotter.set_use_persistence)
+               self.subscribe(PERSIST_ALPHA_KEY, self.plotter.set_persist_alpha)
                #initial update
                #initial update
-               self.plotter.enable_legend(self[PEAK_HOLD_KEY])
                self.update_grid()
 
                self.update_grid()
 
+
        def autoscale(self, *args):
        def autoscale(self, *args):
-               """!
+               """
                Autoscale the fft plot to the last frame.
                Set the dynamic range and reference level.
                """
                Autoscale the fft plot to the last frame.
                Set the dynamic range and reference level.
                """
-               #get the peak level (max of the samples)
-               peak_level = numpy.max(self.samples)
-               #get the noise floor (averge the smallest samples)
-               noise_floor = numpy.average(numpy.sort(self.samples)[:len(self.samples)/4])
-               #padding
-               noise_floor -= abs(noise_floor)*.5
-               peak_level += abs(peak_level)*.1
-               #set the reference level to a multiple of y divs
-               self.set_ref_level(self[Y_DIVS_KEY]*math.ceil(peak_level/self[Y_DIVS_KEY]))
+               if not len(self.samples): return
+               min_level, max_level = common.get_min_max_fft(self.samples)
                #set the range to a clean number of the dynamic range
                #set the range to a clean number of the dynamic range
-               self.set_y_per_div(common.get_clean_num((peak_level - noise_floor)/self[Y_DIVS_KEY]))
+               self[Y_PER_DIV_KEY] = common.get_clean_num(1+(max_level - min_level)/self[Y_DIVS_KEY])
+               #set the reference level to a multiple of y per div
+               self[REF_LEVEL_KEY] = self[Y_PER_DIV_KEY]*round(.5+max_level/self[Y_PER_DIV_KEY])
 
 
-       def _reset_peak_vals(self): self.peak_vals = NO_PEAK_VALS
+       def _reset_peak_vals(self, *args): self.peak_vals = EMPTY_TRACE
 
        def handle_msg(self, msg):
 
        def handle_msg(self, msg):
-               """!
+               """
                Handle the message from the fft sink message queue.
                If complex, reorder the fft samples so the negative bins come first.
                If real, keep take only the positive bins.
                Handle the message from the fft sink message queue.
                If complex, reorder the fft samples so the negative bins come first.
                If real, keep take only the positive bins.
@@ -224,39 +329,50 @@ class fft_window(wx.Panel, pubsub.pubsub, common.prop_setter):
                samples = numpy.fromstring(msg, numpy.float32)[:self.fft_size] #only take first frame
                num_samps = len(samples)
                #reorder fft
                samples = numpy.fromstring(msg, numpy.float32)[:self.fft_size] #only take first frame
                num_samps = len(samples)
                #reorder fft
-               if self.real: samples = samples[:num_samps/2]
-               else: samples = numpy.concatenate((samples[num_samps/2:], samples[:num_samps/2]))
+               if self.real: samples = samples[:(num_samps+1)/2]
+               else: samples = numpy.concatenate((samples[num_samps/2+1:], samples[:(num_samps+1)/2]))
                self.samples = samples
                #peak hold calculation
                if self[PEAK_HOLD_KEY]:
                        if len(self.peak_vals) != len(samples): self.peak_vals = samples
                        self.peak_vals = numpy.maximum(samples, self.peak_vals)
                self.samples = samples
                #peak hold calculation
                if self[PEAK_HOLD_KEY]:
                        if len(self.peak_vals) != len(samples): self.peak_vals = samples
                        self.peak_vals = numpy.maximum(samples, self.peak_vals)
-               else: self._reset_peak_vals()
+                       #plot the peak hold
+                       self.plotter.set_waveform(
+                               channel='Peak',
+                               samples=self.peak_vals,
+                               color_spec=PEAK_VALS_COLOR_SPEC,
+                       )
+               else:
+                       self._reset_peak_vals()
+                       self.plotter.clear_waveform(channel='Peak')
                #plot the fft
                self.plotter.set_waveform(
                        channel='FFT',
                        samples=samples,
                        color_spec=FFT_PLOT_COLOR_SPEC,
                )
                #plot the fft
                self.plotter.set_waveform(
                        channel='FFT',
                        samples=samples,
                        color_spec=FFT_PLOT_COLOR_SPEC,
                )
-               #plot the peak hold
-               self.plotter.set_waveform(
-                       channel='Peak',
-                       samples=self.peak_vals,
-                       color_spec=PEAK_VALS_COLOR_SPEC,
-               )
                #update the plotter
                self.plotter.update()
 
        def update_grid(self, *args):
                #update the plotter
                self.plotter.update()
 
        def update_grid(self, *args):
-               """!
+               """
                Update the plotter grid.
                This update method is dependent on the variables below.
                Determine the x and y axis grid parameters.
                The x axis depends on sample rate, baseband freq, and x divs.
                The y axis depends on y per div, y divs, and ref level.
                """
                Update the plotter grid.
                This update method is dependent on the variables below.
                Determine the x and y axis grid parameters.
                The x axis depends on sample rate, baseband freq, and x divs.
                The y axis depends on y per div, y divs, and ref level.
                """
+               for trace in TRACES:
+                       channel = '%s'%trace.upper()
+                       if self[TRACE_SHOW_KEY+trace]:
+                               self.plotter.set_waveform(
+                                       channel=channel,
+                                       samples=self._traces[trace],
+                                       color_spec=TRACES_COLOR_SPEC[trace],
+                               )
+                       else: self.plotter.clear_waveform(channel=channel)
                #grid parameters
                #grid parameters
-               sample_rate = self.ext_controller[self.sample_rate_key]
+               sample_rate = self[SAMPLE_RATE_KEY]
                baseband_freq = self[BASEBAND_FREQ_KEY]
                y_per_div = self[Y_PER_DIV_KEY]
                y_divs = self[Y_DIVS_KEY]
                baseband_freq = self[BASEBAND_FREQ_KEY]
                y_per_div = self[Y_PER_DIV_KEY]
                y_divs = self[Y_DIVS_KEY]
@@ -266,24 +382,21 @@ class fft_window(wx.Panel, pubsub.pubsub, common.prop_setter):
                if self.real: x_width = sample_rate/2.0
                else: x_width = sample_rate/1.0
                x_per_div = common.get_clean_num(x_width/x_divs)
                if self.real: x_width = sample_rate/2.0
                else: x_width = sample_rate/1.0
                x_per_div = common.get_clean_num(x_width/x_divs)
-               coeff, exp, prefix = common.get_si_components(abs(baseband_freq) + abs(sample_rate/2.0))
                #update the x grid
                if self.real:
                        self.plotter.set_x_grid(
                                baseband_freq,
                                baseband_freq + sample_rate/2.0,
                #update the x grid
                if self.real:
                        self.plotter.set_x_grid(
                                baseband_freq,
                                baseband_freq + sample_rate/2.0,
-                               x_per_div,
-                               10**(-exp),
+                               x_per_div, True,
                        )
                else:
                        self.plotter.set_x_grid(
                                baseband_freq - sample_rate/2.0,
                                baseband_freq + sample_rate/2.0,
                        )
                else:
                        self.plotter.set_x_grid(
                                baseband_freq - sample_rate/2.0,
                                baseband_freq + sample_rate/2.0,
-                               x_per_div,
-                               10**(-exp),
+                               x_per_div, True,
                        )
                #update x units
                        )
                #update x units
-               self.plotter.set_x_label('Frequency', prefix+'Hz')
+               self.plotter.set_x_label('Frequency', 'Hz')
                #update y grid
                self.plotter.set_y_grid(ref_level-y_per_div*y_divs, ref_level, y_per_div)
                #update y units
                #update y grid
                self.plotter.set_y_grid(ref_level-y_per_div*y_divs, ref_level, y_per_div)
                #update y units