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()