2 # Copyright 2008 Free Software Foundation, Inc.
4 # This file is part of GNU Radio
6 # GNU Radio is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3, or (at your option)
11 # GNU Radio is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with GNU Radio; see the file COPYING. If not, write to
18 # the Free Software Foundation, Inc., 51 Franklin Street,
19 # Boston, MA 02110-1301, USA.
22 ##################################################
24 ##################################################
31 from constants import *
32 from gnuradio import gr #for gr.prefs, trigger modes
35 ##################################################
37 ##################################################
38 DEFAULT_FRAME_RATE = gr.prefs().get_long('wxgui', 'scope_rate', 30)
39 DEFAULT_WIN_SIZE = (600, 300)
45 ('Freerun', gr.gr_TRIG_MODE_FREE),
46 ('Auto', gr.gr_TRIG_MODE_AUTO),
47 ('Normal', gr.gr_TRIG_MODE_NORM),
50 ('Pos +', gr.gr_TRIG_SLOPE_POS),
51 ('Neg -', gr.gr_TRIG_SLOPE_NEG),
53 CHANNEL_COLOR_SPECS = (
59 TRIGGER_COLOR_SPEC = (1.0, 0.4, 0.0)
60 AUTORANGE_UPDATE_RATE = 0.5 #sec
68 DEFAULT_MARKER_TYPE = None
70 ##################################################
71 # Scope window control panel
72 ##################################################
73 class control_panel(wx.Panel):
75 A control panel with wx widgits to control the plotter and scope block.
77 def __init__(self, parent):
79 Create a new control panel.
80 @param parent the wx parent window
84 wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
85 control_box = wx.BoxSizer(wx.VERTICAL)
86 ##################################################
88 ##################################################
89 control_box.AddStretchSpacer()
90 axes_options_box = forms.static_box_sizer(
91 parent=self, sizer=control_box, label='Axes Options',
92 bold=True, orient=wx.VERTICAL,
94 ##################################################
96 ##################################################
97 scope_mode_box = wx.BoxSizer(wx.VERTICAL)
98 axes_options_box.Add(scope_mode_box, 0, wx.EXPAND)
100 forms.incr_decr_buttons(
101 parent=self, sizer=scope_mode_box, label='Secs/Div',
102 on_incr=self._on_incr_t_divs, on_decr=self._on_decr_t_divs,
105 y_buttons_scope = forms.incr_decr_buttons(
106 parent=self, sizer=scope_mode_box, label='Counts/Div',
107 on_incr=self._on_incr_y_divs, on_decr=self._on_decr_y_divs,
110 y_off_buttons_scope = forms.incr_decr_buttons(
111 parent=self, sizer=scope_mode_box, label='Y Offset',
112 on_incr=self._on_incr_y_off, on_decr=self._on_decr_y_off,
115 scope_mode_box.AddSpacer(5)
117 parent=self, sizer=scope_mode_box,
118 ps=parent, key=T_FRAC_OFF_KEY, label='T Offset',
119 minimum=0, maximum=1, num_steps=1000,
121 scope_mode_box.AddSpacer(5)
122 ##################################################
124 ##################################################
125 xy_mode_box = wx.BoxSizer(wx.VERTICAL)
126 axes_options_box.Add(xy_mode_box, 0, wx.EXPAND)
128 x_buttons = forms.incr_decr_buttons(
129 parent=self, sizer=xy_mode_box, label='X/Div',
130 on_incr=self._on_incr_x_divs, on_decr=self._on_decr_x_divs,
133 y_buttons = forms.incr_decr_buttons(
134 parent=self, sizer=xy_mode_box, label='Y/Div',
135 on_incr=self._on_incr_y_divs, on_decr=self._on_decr_y_divs,
138 x_off_buttons = forms.incr_decr_buttons(
139 parent=self, sizer=xy_mode_box, label='X Off',
140 on_incr=self._on_incr_x_off, on_decr=self._on_decr_x_off,
143 y_off_buttons = forms.incr_decr_buttons(
144 parent=self, sizer=xy_mode_box, label='Y Off',
145 on_incr=self._on_incr_y_off, on_decr=self._on_decr_y_off,
147 for widget in (y_buttons_scope, y_off_buttons_scope, x_buttons, y_buttons, x_off_buttons, y_off_buttons):
148 parent.subscribe(AUTORANGE_KEY, widget.Disable)
149 widget.Disable(parent[AUTORANGE_KEY])
150 xy_mode_box.ShowItems(False)
153 parent=self, sizer=axes_options_box, label='Autorange',
154 ps=parent, key=AUTORANGE_KEY,
156 ##################################################
158 ##################################################
159 TRIGGER_PAGE_INDEX = parent.num_inputs
160 XY_PAGE_INDEX = parent.num_inputs+1
161 control_box.AddStretchSpacer()
162 chan_options_box = forms.static_box_sizer(
163 parent=self, sizer=control_box, label='Channel Options',
164 bold=True, orient=wx.VERTICAL,
166 options_notebook = wx.Notebook(self)
167 options_notebook_args = list()
168 CHANNELS = [('Ch %d'%(i+1), i) for i in range(parent.num_inputs)]
169 ##################################################
171 ##################################################
172 for i in range(parent.num_inputs):
173 channel_menu_panel = wx.Panel(options_notebook)
174 options_notebook_args.append((channel_menu_panel, i, 'Ch%d'%(i+1)))
175 channel_menu_box = wx.BoxSizer(wx.VERTICAL)
176 channel_menu_panel.SetSizer(channel_menu_box)
178 channel_menu_box.AddStretchSpacer()
180 parent=channel_menu_panel, sizer=channel_menu_box,
181 ps=parent, key=common.index_key(AC_COUPLE_KEY, i),
182 choices=map(lambda x: x[1], COUPLING_MODES),
183 labels=map(lambda x: x[0], COUPLING_MODES),
184 label='Coupling', width=WIDTH,
187 channel_menu_box.AddStretchSpacer()
189 parent=channel_menu_panel, sizer=channel_menu_box,
190 ps=parent, key=common.index_key(MARKER_KEY, i),
191 choices=map(lambda x: x[1], MARKER_TYPES),
192 labels=map(lambda x: x[0], MARKER_TYPES),
193 label='Marker', width=WIDTH,
195 channel_menu_box.AddStretchSpacer()
196 ##################################################
198 ##################################################
199 trigger_menu_panel = wx.Panel(options_notebook)
200 options_notebook_args.append((trigger_menu_panel, TRIGGER_PAGE_INDEX, 'Trig'))
201 trigger_menu_box = wx.BoxSizer(wx.VERTICAL)
202 trigger_menu_panel.SetSizer(trigger_menu_box)
205 parent=trigger_menu_panel, sizer=trigger_menu_box,
206 ps=parent, key=TRIGGER_MODE_KEY,
207 choices=map(lambda x: x[1], TRIGGER_MODES),
208 labels=map(lambda x: x[0], TRIGGER_MODES),
209 label='Mode', width=WIDTH,
212 trigger_slope_chooser = forms.drop_down(
213 parent=trigger_menu_panel, sizer=trigger_menu_box,
214 ps=parent, key=TRIGGER_SLOPE_KEY,
215 choices=map(lambda x: x[1], TRIGGER_SLOPES),
216 labels=map(lambda x: x[0], TRIGGER_SLOPES),
217 label='Slope', width=WIDTH,
220 trigger_channel_chooser = forms.drop_down(
221 parent=trigger_menu_panel, sizer=trigger_menu_box,
222 ps=parent, key=TRIGGER_CHANNEL_KEY,
223 choices=map(lambda x: x[1], CHANNELS),
224 labels=map(lambda x: x[0], CHANNELS),
225 label='Channel', width=WIDTH,
228 hbox = wx.BoxSizer(wx.HORIZONTAL)
229 trigger_menu_box.Add(hbox, 0, wx.EXPAND)
230 hbox.Add(wx.StaticText(trigger_menu_panel, label='Level:'), 1, wx.ALIGN_CENTER_VERTICAL)
231 trigger_level_button = forms.single_button(
232 parent=trigger_menu_panel, sizer=hbox, label='50%',
233 callback=parent.set_auto_trigger_level, style=wx.BU_EXACTFIT,
235 hbox.AddSpacer(WIDTH-60)
236 trigger_level_buttons = forms.incr_decr_buttons(
237 parent=trigger_menu_panel, sizer=hbox,
238 on_incr=self._on_incr_trigger_level, on_decr=self._on_decr_trigger_level,
240 def disable_all(trigger_mode):
241 for widget in (trigger_slope_chooser, trigger_channel_chooser, trigger_level_buttons, trigger_level_button):
242 widget.Disable(trigger_mode == gr.gr_TRIG_MODE_FREE)
243 parent.subscribe(TRIGGER_MODE_KEY, disable_all)
244 disable_all(parent[TRIGGER_MODE_KEY])
245 ##################################################
247 ##################################################
248 if parent.num_inputs > 1:
249 xy_menu_panel = wx.Panel(options_notebook)
250 options_notebook_args.append((xy_menu_panel, XY_PAGE_INDEX, 'XY'))
251 xy_menu_box = wx.BoxSizer(wx.VERTICAL)
252 xy_menu_panel.SetSizer(xy_menu_box)
253 #x and y channel choosers
254 xy_menu_box.AddStretchSpacer()
256 parent=xy_menu_panel, sizer=xy_menu_box,
257 ps=parent, key=X_CHANNEL_KEY,
258 choices=map(lambda x: x[1], CHANNELS),
259 labels=map(lambda x: x[0], CHANNELS),
260 label='Channel X', width=WIDTH,
262 xy_menu_box.AddStretchSpacer()
264 parent=xy_menu_panel, sizer=xy_menu_box,
265 ps=parent, key=Y_CHANNEL_KEY,
266 choices=map(lambda x: x[1], CHANNELS),
267 labels=map(lambda x: x[0], CHANNELS),
268 label='Channel Y', width=WIDTH,
271 xy_menu_box.AddStretchSpacer()
273 parent=xy_menu_panel, sizer=xy_menu_box,
274 ps=parent, key=XY_MARKER_KEY,
275 choices=map(lambda x: x[1], MARKER_TYPES),
276 labels=map(lambda x: x[0], MARKER_TYPES),
277 label='Marker', width=WIDTH,
279 xy_menu_box.AddStretchSpacer()
280 ##################################################
281 # Setup Options Notebook
282 ##################################################
284 parent=self, sizer=chan_options_box,
285 notebook=options_notebook,
286 ps=parent, key=CHANNEL_OPTIONS_KEY,
287 pages=map(lambda x: x[0], options_notebook_args),
288 choices=map(lambda x: x[1], options_notebook_args),
289 labels=map(lambda x: x[2], options_notebook_args),
291 #gui handling for channel options changing
292 def options_notebook_changed(chan_opt):
294 parent[TRIGGER_SHOW_KEY] = chan_opt == TRIGGER_PAGE_INDEX
295 parent[XY_MODE_KEY] = chan_opt == XY_PAGE_INDEX
296 except wx.PyDeadObjectError: pass
297 parent.subscribe(CHANNEL_OPTIONS_KEY, options_notebook_changed)
298 #gui handling for xy mode changing
299 def xy_mode_changed(mode):
300 #ensure xy tab is selected
301 if mode and parent[CHANNEL_OPTIONS_KEY] != XY_PAGE_INDEX:
302 parent[CHANNEL_OPTIONS_KEY] = XY_PAGE_INDEX
303 #ensure xy tab is not selected
304 elif not mode and parent[CHANNEL_OPTIONS_KEY] == XY_PAGE_INDEX:
305 parent[CHANNEL_OPTIONS_KEY] = 0
306 #show/hide control buttons
307 scope_mode_box.ShowItems(not mode)
308 xy_mode_box.ShowItems(mode)
310 parent.subscribe(XY_MODE_KEY, xy_mode_changed)
311 xy_mode_changed(parent[XY_MODE_KEY])
312 ##################################################
314 ##################################################
316 control_box.AddStretchSpacer()
318 sizer=control_box, parent=self,
319 true_label='Stop', false_label='Run',
320 ps=parent, key=RUNNING_KEY,
323 self.SetSizerAndFit(control_box)
325 def on_mouse_wheel(event):
326 if not parent[XY_MODE_KEY]:
327 if event.GetWheelRotation() < 0: self._on_incr_t_divs(event)
328 else: self._on_decr_t_divs(event)
329 parent.plotter.Bind(wx.EVT_MOUSEWHEEL, on_mouse_wheel)
331 ##################################################
333 ##################################################
335 def _on_incr_trigger_level(self, event):
336 self.parent[TRIGGER_LEVEL_KEY] += self.parent[Y_PER_DIV_KEY]/3.
337 def _on_decr_trigger_level(self, event):
338 self.parent[TRIGGER_LEVEL_KEY] -= self.parent[Y_PER_DIV_KEY]/3.
340 def _on_incr_t_divs(self, event):
341 self.parent[T_PER_DIV_KEY] = common.get_clean_incr(self.parent[T_PER_DIV_KEY])
342 def _on_decr_t_divs(self, event):
343 self.parent[T_PER_DIV_KEY] = common.get_clean_decr(self.parent[T_PER_DIV_KEY])
344 def _on_incr_x_divs(self, event):
345 self.parent[X_PER_DIV_KEY] = common.get_clean_incr(self.parent[X_PER_DIV_KEY])
346 def _on_decr_x_divs(self, event):
347 self.parent[X_PER_DIV_KEY] = common.get_clean_decr(self.parent[X_PER_DIV_KEY])
348 def _on_incr_y_divs(self, event):
349 self.parent[Y_PER_DIV_KEY] = common.get_clean_incr(self.parent[Y_PER_DIV_KEY])
350 def _on_decr_y_divs(self, event):
351 self.parent[Y_PER_DIV_KEY] = common.get_clean_decr(self.parent[Y_PER_DIV_KEY])
353 def _on_incr_x_off(self, event):
354 self.parent[X_OFF_KEY] = self.parent[X_OFF_KEY] + self.parent[X_PER_DIV_KEY]
355 def _on_decr_x_off(self, event):
356 self.parent[X_OFF_KEY] = self.parent[X_OFF_KEY] - self.parent[X_PER_DIV_KEY]
357 def _on_incr_y_off(self, event):
358 self.parent[Y_OFF_KEY] = self.parent[Y_OFF_KEY] + self.parent[Y_PER_DIV_KEY]
359 def _on_decr_y_off(self, event):
360 self.parent[Y_OFF_KEY] = self.parent[Y_OFF_KEY] - self.parent[Y_PER_DIV_KEY]
362 ##################################################
363 # Scope window with plotter and control panel
364 ##################################################
365 class scope_window(wx.Panel, pubsub.pubsub):
386 pubsub.pubsub.__init__(self)
388 assert num_inputs <= len(CHANNEL_COLOR_SPECS)
390 self.sampleses = None
391 self.num_inputs = num_inputs
392 autorange = not v_scale
393 self.autorange_ts = 0
394 v_scale = v_scale or 1
395 self.frame_rate_ts = 0
397 self.proxy(MSG_KEY, controller, msg_key)
398 self.proxy(SAMPLE_RATE_KEY, controller, sample_rate_key)
399 self.proxy(TRIGGER_LEVEL_KEY, controller, trigger_level_key)
400 self.proxy(TRIGGER_MODE_KEY, controller, trigger_mode_key)
401 self.proxy(TRIGGER_SLOPE_KEY, controller, trigger_slope_key)
402 self.proxy(TRIGGER_CHANNEL_KEY, controller, trigger_channel_key)
403 self.proxy(DECIMATION_KEY, controller, decimation_key)
405 self[RUNNING_KEY] = True
406 self[XY_MARKER_KEY] = 2.0
407 self[CHANNEL_OPTIONS_KEY] = 0
408 self[XY_MODE_KEY] = xy_mode
409 self[X_CHANNEL_KEY] = 0
410 self[Y_CHANNEL_KEY] = self.num_inputs-1
411 self[AUTORANGE_KEY] = autorange
412 self[T_PER_DIV_KEY] = t_scale
413 self[X_PER_DIV_KEY] = v_scale
414 self[Y_PER_DIV_KEY] = v_scale
421 self[FRAME_RATE_KEY] = frame_rate
422 self[TRIGGER_LEVEL_KEY] = 0
423 self[TRIGGER_CHANNEL_KEY] = 0
424 self[TRIGGER_MODE_KEY] = gr.gr_TRIG_MODE_AUTO
425 self[TRIGGER_SLOPE_KEY] = gr.gr_TRIG_SLOPE_POS
426 self[T_FRAC_OFF_KEY] = 0.5
427 for i in range(num_inputs):
428 self.proxy(common.index_key(AC_COUPLE_KEY, i), controller, common.index_key(ac_couple_key, i))
430 wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER)
431 self.plotter = plotter.channel_plotter(self)
432 self.plotter.SetSize(wx.Size(*size))
433 self.plotter.set_title(title)
434 self.plotter.enable_legend(True)
435 self.plotter.enable_point_label(True)
436 self.plotter.enable_grid_lines(True)
437 #setup the box with plot and controls
438 self.control_panel = control_panel(self)
439 main_box = wx.BoxSizer(wx.HORIZONTAL)
440 main_box.Add(self.plotter, 1, wx.EXPAND)
441 main_box.Add(self.control_panel, 0, wx.EXPAND)
442 self.SetSizerAndFit(main_box)
443 #register events for message
444 self.subscribe(MSG_KEY, self.handle_msg)
445 #register events for grid
446 for key in [common.index_key(MARKER_KEY, i) for i in range(self.num_inputs)] + [
447 TRIGGER_LEVEL_KEY, TRIGGER_MODE_KEY,
448 T_PER_DIV_KEY, X_PER_DIV_KEY, Y_PER_DIV_KEY,
449 T_OFF_KEY, X_OFF_KEY, Y_OFF_KEY,
450 T_DIVS_KEY, X_DIVS_KEY, Y_DIVS_KEY,
451 XY_MODE_KEY, AUTORANGE_KEY, T_FRAC_OFF_KEY,
452 TRIGGER_SHOW_KEY, XY_MARKER_KEY, X_CHANNEL_KEY, Y_CHANNEL_KEY,
453 ]: self.subscribe(key, self.update_grid)
457 def handle_msg(self, msg):
459 Handle the message from the scope sink message queue.
460 Plot the list of arrays of samples onto the grid.
461 Each samples array gets its own channel.
462 @param msg the time domain data as a character array
464 if not self[RUNNING_KEY]: return
466 if time.time() - self.frame_rate_ts < 1.0/self[FRAME_RATE_KEY]: return
467 #convert to floating point numbers
468 samples = numpy.fromstring(msg, numpy.float32)
469 #extract the trigger offset
470 self.trigger_offset = samples[-1]
471 samples = samples[:-1]
472 samps_per_ch = len(samples)/self.num_inputs
473 self.sampleses = [samples[samps_per_ch*i:samps_per_ch*(i+1)] for i in range(self.num_inputs)]
475 self.handle_samples()
476 self.frame_rate_ts = time.time()
478 def set_auto_trigger_level(self, *args):
480 Use the current trigger channel and samples to calculate the 50% level.
482 if not self.sampleses: return
483 samples = self.sampleses[self[TRIGGER_CHANNEL_KEY]]
484 self[TRIGGER_LEVEL_KEY] = (numpy.max(samples)+numpy.min(samples))/2
486 def handle_samples(self):
488 Handle the cached samples from the scope input.
489 Perform ac coupling, triggering, and auto ranging.
491 if not self.sampleses: return
492 sampleses = self.sampleses
493 if self[XY_MODE_KEY]:
494 self[DECIMATION_KEY] = 1
495 x_samples = sampleses[self[X_CHANNEL_KEY]]
496 y_samples = sampleses[self[Y_CHANNEL_KEY]]
498 if self[AUTORANGE_KEY] and time.time() - self.autorange_ts > AUTORANGE_UPDATE_RATE:
499 x_min, x_max = common.get_min_max(x_samples)
500 y_min, y_max = common.get_min_max(y_samples)
501 #adjust the x per div
502 x_per_div = common.get_clean_num((x_max-x_min)/self[X_DIVS_KEY])
503 if x_per_div != self[X_PER_DIV_KEY]: self[X_PER_DIV_KEY] = x_per_div; return
505 x_off = x_per_div*round((x_max+x_min)/2/x_per_div)
506 if x_off != self[X_OFF_KEY]: self[X_OFF_KEY] = x_off; return
507 #adjust the y per div
508 y_per_div = common.get_clean_num((y_max-y_min)/self[Y_DIVS_KEY])
509 if y_per_div != self[Y_PER_DIV_KEY]: self[Y_PER_DIV_KEY] = y_per_div; return
511 y_off = y_per_div*round((y_max+y_min)/2/y_per_div)
512 if y_off != self[Y_OFF_KEY]: self[Y_OFF_KEY] = y_off; return
513 self.autorange_ts = time.time()
515 self.plotter.set_waveform(
517 samples=(x_samples, y_samples),
518 color_spec=CHANNEL_COLOR_SPECS[0],
519 marker=self[XY_MARKER_KEY],
521 #turn off each waveform
522 for i, samples in enumerate(sampleses):
523 self.plotter.clear_waveform(channel='Ch%d'%(i+1))
526 if self[AUTORANGE_KEY] and time.time() - self.autorange_ts > AUTORANGE_UPDATE_RATE:
527 bounds = [common.get_min_max(samples) for samples in sampleses]
528 y_min = numpy.min([bound[0] for bound in bounds])
529 y_max = numpy.max([bound[1] for bound in bounds])
530 #adjust the y per div
531 y_per_div = common.get_clean_num((y_max-y_min)/self[Y_DIVS_KEY])
532 if y_per_div != self[Y_PER_DIV_KEY]: self[Y_PER_DIV_KEY] = y_per_div; return
534 y_off = y_per_div*round((y_max+y_min)/2/y_per_div)
535 if y_off != self[Y_OFF_KEY]: self[Y_OFF_KEY] = y_off; return
536 self.autorange_ts = time.time()
537 #number of samples to scale to the screen
538 actual_rate = self.get_actual_rate()
539 time_span = self[T_PER_DIV_KEY]*self[T_DIVS_KEY]
540 num_samps = int(round(time_span*actual_rate))
541 #handle the time offset
542 t_off = self[T_FRAC_OFF_KEY]*(len(sampleses[0])/actual_rate - time_span)
543 if t_off != self[T_OFF_KEY]: self[T_OFF_KEY] = t_off; return
544 samps_off = int(round(actual_rate*self[T_OFF_KEY]))
545 #adjust the decim so that we use about half the samps
546 self[DECIMATION_KEY] = int(round(
547 time_span*self[SAMPLE_RATE_KEY]/(0.5*len(sampleses[0]))
550 #num samps too small, auto increment the time
551 if num_samps < 2: self[T_PER_DIV_KEY] = common.get_clean_incr(self[T_PER_DIV_KEY])
552 #num samps in bounds, plot each waveform
553 elif num_samps <= len(sampleses[0]):
554 for i, samples in enumerate(sampleses):
556 self.plotter.set_waveform(
557 channel='Ch%d'%(i+1),
558 samples=samples[samps_off:num_samps+samps_off],
559 color_spec=CHANNEL_COLOR_SPECS[i],
560 marker=self[common.index_key(MARKER_KEY, i)],
561 trig_off=self.trigger_offset,
564 self.plotter.clear_waveform(channel='XY')
565 #keep trigger level within range
566 if self[TRIGGER_LEVEL_KEY] > self.get_y_max():
567 self[TRIGGER_LEVEL_KEY] = self.get_y_max(); return
568 if self[TRIGGER_LEVEL_KEY] < self.get_y_min():
569 self[TRIGGER_LEVEL_KEY] = self.get_y_min(); return
570 #disable the trigger channel
571 if not self[TRIGGER_SHOW_KEY] or self[XY_MODE_KEY] or self[TRIGGER_MODE_KEY] == gr.gr_TRIG_MODE_FREE:
572 self.plotter.clear_waveform(channel='Trig')
573 else: #show trigger channel
574 trigger_level = self[TRIGGER_LEVEL_KEY]
575 trigger_point = (len(self.sampleses[0])-1)/self.get_actual_rate()/2.0
576 self.plotter.set_waveform(
579 [self.get_t_min(), trigger_point, trigger_point, trigger_point, trigger_point, self.get_t_max()],
580 [trigger_level, trigger_level, self.get_y_max(), self.get_y_min(), trigger_level, trigger_level]
582 color_spec=TRIGGER_COLOR_SPEC,
585 self.plotter.update()
587 def get_actual_rate(self): return 1.0*self[SAMPLE_RATE_KEY]/self[DECIMATION_KEY]
588 def get_t_min(self): return self[T_OFF_KEY]
589 def get_t_max(self): return self[T_PER_DIV_KEY]*self[T_DIVS_KEY] + self[T_OFF_KEY]
590 def get_x_min(self): return -1*self[X_PER_DIV_KEY]*self[X_DIVS_KEY]/2.0 + self[X_OFF_KEY]
591 def get_x_max(self): return self[X_PER_DIV_KEY]*self[X_DIVS_KEY]/2.0 + self[X_OFF_KEY]
592 def get_y_min(self): return -1*self[Y_PER_DIV_KEY]*self[Y_DIVS_KEY]/2.0 + self[Y_OFF_KEY]
593 def get_y_max(self): return self[Y_PER_DIV_KEY]*self[Y_DIVS_KEY]/2.0 + self[Y_OFF_KEY]
595 def update_grid(self, *args):
597 Update the grid to reflect the current settings:
598 xy divisions, xy offset, xy mode setting
600 if self[T_FRAC_OFF_KEY] < 0: self[T_FRAC_OFF_KEY] = 0; return
601 if self[T_FRAC_OFF_KEY] > 1: self[T_FRAC_OFF_KEY] = 1; return
602 if self[XY_MODE_KEY]:
604 self.plotter.set_x_label('Ch%d'%(self[X_CHANNEL_KEY]+1))
605 self.plotter.set_x_grid(self.get_x_min(), self.get_x_max(), self[X_PER_DIV_KEY])
607 self.plotter.set_y_label('Ch%d'%(self[Y_CHANNEL_KEY]+1))
608 self.plotter.set_y_grid(self.get_y_min(), self.get_y_max(), self[Y_PER_DIV_KEY])
611 self.plotter.set_x_label('Time', 's')
612 self.plotter.set_x_grid(self.get_t_min(), self.get_t_max(), self[T_PER_DIV_KEY], True)
614 self.plotter.set_y_label('Counts')
615 self.plotter.set_y_grid(self.get_y_min(), self.get_y_max(), self[Y_PER_DIV_KEY])
616 #redraw current sample
617 self.handle_samples()