]> sigrok.org Git - pulseview.git/blob - pv/toolbars/samplingbar.cpp
550fc8482d1c7891a586adc8734dac3d684ac352
[pulseview.git] / pv / toolbars / samplingbar.cpp
1 /*
2  * This file is part of the PulseView project.
3  *
4  * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
5  *
6  * This program 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 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  */
20
21 #include <extdef.h>
22
23 #include <assert.h>
24
25 #include <QAction>
26 #include <QDebug>
27 #include <QHelpEvent>
28 #include <QToolTip>
29
30 #include "samplingbar.h"
31
32 #include <pv/devicemanager.h>
33 #include <pv/device/devinst.h>
34 #include <pv/popups/deviceoptions.h>
35 #include <pv/popups/channels.h>
36 #include <pv/util.h>
37
38 using std::map;
39 using std::max;
40 using std::min;
41 using std::shared_ptr;
42 using std::string;
43
44 namespace pv {
45 namespace toolbars {
46
47 const uint64_t SamplingBar::MinSampleCount = 100ULL;
48 const uint64_t SamplingBar::MaxSampleCount = 1000000000000ULL;
49 const uint64_t SamplingBar::DefaultSampleCount = 1000000;
50
51 SamplingBar::SamplingBar(SigSession &session, QWidget *parent) :
52         QToolBar("Sampling Bar", parent),
53         _session(session),
54         _device_selector(this),
55         _updating_device_selector(false),
56         _configure_button(this),
57         _configure_button_action(NULL),
58         _channels_button(this),
59         _sample_count(" samples", this),
60         _sample_rate("Hz", this),
61         _updating_sample_rate(false),
62         _updating_sample_count(false),
63         _sample_count_supported(false),
64         _icon_red(":/icons/status-red.svg"),
65         _icon_green(":/icons/status-green.svg"),
66         _icon_grey(":/icons/status-grey.svg"),
67         _run_stop_button(this)
68 {
69         connect(&_run_stop_button, SIGNAL(clicked()),
70                 this, SLOT(on_run_stop()));
71         connect(&_device_selector, SIGNAL(currentIndexChanged (int)),
72                 this, SLOT(on_device_selected()));
73         connect(&_sample_count, SIGNAL(value_changed()),
74                 this, SLOT(on_sample_count_changed()));
75         connect(&_sample_rate, SIGNAL(value_changed()),
76                 this, SLOT(on_sample_rate_changed()));
77
78         _sample_count.show_min_max_step(0, UINT64_MAX, 1);
79
80         set_capture_state(pv::SigSession::Stopped);
81
82         _configure_button.setIcon(QIcon::fromTheme("configure",
83                 QIcon(":/icons/configure.png")));
84
85         _channels_button.setIcon(QIcon::fromTheme("channels",
86                 QIcon(":/icons/channels.svg")));
87
88         _run_stop_button.setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
89
90         addWidget(&_device_selector);
91         _configure_button_action = addWidget(&_configure_button);
92         addWidget(&_channels_button);
93         addWidget(&_sample_count);
94         addWidget(&_sample_rate);
95
96         addWidget(&_run_stop_button);
97
98         _sample_count.installEventFilter(this);
99         _sample_rate.installEventFilter(this);
100 }
101
102 void SamplingBar::set_device_list(
103         const std::list< shared_ptr<pv::device::DevInst> > &devices,
104         shared_ptr<pv::device::DevInst> selected)
105 {
106         int selected_index = -1;
107
108         assert(selected);
109
110         _updating_device_selector = true;
111
112         _device_selector.clear();
113         _device_selector_map.clear();
114
115         for (shared_ptr<pv::device::DevInst> dev_inst : devices) {
116                 assert(dev_inst);
117                 const string title = dev_inst->format_device_title();
118                 const sr_dev_inst *sdi = dev_inst->dev_inst();
119                 assert(sdi);
120
121                 if (selected == dev_inst)
122                         selected_index = _device_selector.count();
123
124                 _device_selector_map[sdi] = dev_inst;
125                 _device_selector.addItem(title.c_str(),
126                         qVariantFromValue((void*)sdi));
127         }
128
129         // The selected device should have been in the list
130         assert(selected_index != -1);
131         _device_selector.setCurrentIndex(selected_index);
132
133         update_device_config_widgets();
134
135         _updating_device_selector = false;
136 }
137
138 shared_ptr<pv::device::DevInst> SamplingBar::get_selected_device() const
139 {
140         const int index = _device_selector.currentIndex();
141         if (index < 0)
142                 return shared_ptr<pv::device::DevInst>();
143
144         const sr_dev_inst *const sdi =
145                 (const sr_dev_inst*)_device_selector.itemData(
146                         index).value<void*>();
147         assert(sdi);
148
149         const auto iter = _device_selector_map.find(sdi);
150         if (iter == _device_selector_map.end())
151                 return shared_ptr<pv::device::DevInst>();
152
153         return shared_ptr<pv::device::DevInst>((*iter).second);
154 }
155
156 void SamplingBar::set_capture_state(pv::SigSession::capture_state state)
157 {
158         const QIcon *icons[] = {&_icon_grey, &_icon_red, &_icon_green};
159         _run_stop_button.setIcon(*icons[state]);
160         _run_stop_button.setText((state == pv::SigSession::Stopped) ?
161                 tr("Run") : tr("Stop"));
162 }
163
164 void SamplingBar::update_sample_rate_selector()
165 {
166         GVariant *gvar_dict, *gvar_list;
167         const uint64_t *elements = NULL;
168         gsize num_elements;
169
170         if (_updating_sample_rate)
171                 return;
172
173         const shared_ptr<device::DevInst> dev_inst = get_selected_device();
174         if (!dev_inst)
175                 return;
176
177         assert(!_updating_sample_rate);
178         _updating_sample_rate = true;
179
180         if (!(gvar_dict = dev_inst->list_config(NULL, SR_CONF_SAMPLERATE)))
181         {
182                 _sample_rate.show_none();
183                 _updating_sample_rate = false;
184                 return;
185         }
186
187         if ((gvar_list = g_variant_lookup_value(gvar_dict,
188                         "samplerate-steps", G_VARIANT_TYPE("at"))))
189         {
190                 elements = (const uint64_t *)g_variant_get_fixed_array(
191                                 gvar_list, &num_elements, sizeof(uint64_t));
192
193                 const uint64_t min = elements[0];
194                 const uint64_t max = elements[1];
195                 const uint64_t step = elements[2];
196
197                 g_variant_unref(gvar_list);
198
199                 assert(min > 0);
200                 assert(max > 0);
201                 assert(max > min);
202                 assert(step > 0);
203
204                 if (step == 1)
205                         _sample_rate.show_125_list(min, max);
206                 else
207                 {
208                         // When the step is not 1, we cam't make a 1-2-5-10
209                         // list of sample rates, because we may not be able to
210                         // make round numbers. Therefore in this case, show a
211                         // spin box.
212                         _sample_rate.show_min_max_step(min, max, step);
213                 }
214         }
215         else if ((gvar_list = g_variant_lookup_value(gvar_dict,
216                         "samplerates", G_VARIANT_TYPE("at"))))
217         {
218                 elements = (const uint64_t *)g_variant_get_fixed_array(
219                                 gvar_list, &num_elements, sizeof(uint64_t));
220                 _sample_rate.show_list(elements, num_elements);
221                 g_variant_unref(gvar_list);
222         }
223         _updating_sample_rate = false;
224
225         g_variant_unref(gvar_dict);
226         update_sample_rate_selector_value();
227 }
228
229 void SamplingBar::update_sample_rate_selector_value()
230 {
231         GVariant *gvar;
232         uint64_t samplerate;
233
234         if (_updating_sample_rate)
235                 return;
236
237         const shared_ptr<device::DevInst> dev_inst = get_selected_device();
238         if (!dev_inst)
239                 return;
240
241         if (!(gvar = dev_inst->get_config(NULL, SR_CONF_SAMPLERATE))) {
242                 qDebug() << "WARNING: Failed to get value of sample rate";
243                 return;
244         }
245         samplerate = g_variant_get_uint64(gvar);
246         g_variant_unref(gvar);
247
248         assert(!_updating_sample_rate);
249         _updating_sample_rate = true;
250         _sample_rate.set_value(samplerate);
251         _updating_sample_rate = false;
252 }
253
254 void SamplingBar::update_sample_count_selector()
255 {
256         GVariant *gvar;
257
258         if (_updating_sample_count)
259                 return;
260
261         const shared_ptr<device::DevInst> dev_inst = get_selected_device();
262         if (!dev_inst)
263                 return;
264
265         assert(!_updating_sample_count);
266         _updating_sample_count = true;
267
268         if (_sample_count_supported)
269         {
270                 uint64_t sample_count = _sample_count.value();
271                 uint64_t min_sample_count = 0;
272                 uint64_t max_sample_count = MaxSampleCount;
273
274                 if (sample_count == 0)
275                         sample_count = DefaultSampleCount;
276
277                 if ((gvar = dev_inst->list_config(NULL, SR_CONF_LIMIT_SAMPLES)))
278                 {
279                         g_variant_get(gvar, "(tt)",
280                                 &min_sample_count, &max_sample_count);
281                         g_variant_unref(gvar);
282                 }
283
284                 min_sample_count = min(max(min_sample_count, MinSampleCount),
285                         max_sample_count);
286
287                 _sample_count.show_125_list(
288                         min_sample_count, max_sample_count);
289
290                 if ((gvar = dev_inst->get_config(NULL, SR_CONF_LIMIT_SAMPLES)))
291                 {
292                         sample_count = g_variant_get_uint64(gvar);
293                         if (sample_count == 0)
294                                 sample_count = DefaultSampleCount;
295                         sample_count = min(max(sample_count, MinSampleCount),
296                                 max_sample_count);
297
298                         g_variant_unref(gvar);
299                 }
300
301                 _sample_count.set_value(sample_count);
302         }
303         else
304                 _sample_count.show_none();
305
306         _updating_sample_count = false;
307 }
308
309 void SamplingBar::update_device_config_widgets()
310 {
311         GVariant *gvar;
312
313         using namespace pv::popups;
314
315         const shared_ptr<device::DevInst> dev_inst = get_selected_device();
316         if (!dev_inst)
317                 return;
318
319         // Update the configure popup
320         DeviceOptions *const opts = new DeviceOptions(dev_inst, this);
321         _configure_button_action->setVisible(
322                 !opts->binding().properties().empty());
323         _configure_button.set_popup(opts);
324
325         // Update the channels popup
326         Channels *const channels = new Channels(_session, this);
327         _channels_button.set_popup(channels);
328
329         // Update supported options.
330         _sample_count_supported = false;
331
332         if ((gvar = dev_inst->list_config(NULL, SR_CONF_DEVICE_OPTIONS)))
333         {
334                 gsize num_opts;
335                 const int *const options =
336                         (const int32_t *)g_variant_get_fixed_array(
337                                 gvar, &num_opts, sizeof(int32_t));
338                 for (unsigned int i = 0; i < num_opts; i++)
339                 {
340                         switch (options[i]) {
341                         case SR_CONF_LIMIT_SAMPLES:
342                                 _sample_count_supported = true;
343                                 break;
344                         case SR_CONF_LIMIT_FRAMES:
345                                 dev_inst->set_config(NULL, SR_CONF_LIMIT_FRAMES,
346                                         g_variant_new_uint64(1));
347                                 break;
348                         }
349                 }
350         }
351
352         // Add notification of reconfigure events
353         disconnect(this, SLOT(on_config_changed()));
354         connect(dev_inst.get(), SIGNAL(config_changed()),
355                 this, SLOT(on_config_changed()));
356
357         // Update sweep timing widgets.
358         update_sample_count_selector();
359         update_sample_rate_selector();
360 }
361
362 void SamplingBar::commit_sample_count()
363 {
364         uint64_t sample_count = 0;
365
366         if (_updating_sample_count)
367                 return;
368
369         const shared_ptr<device::DevInst> dev_inst = get_selected_device();
370         if (!dev_inst)
371                 return;
372
373         sample_count = _sample_count.value();
374
375         // Set the sample count
376         assert(!_updating_sample_count);
377         _updating_sample_count = true;
378         if (_sample_count_supported &&
379                 !dev_inst->set_config(NULL, SR_CONF_LIMIT_SAMPLES,
380                 g_variant_new_uint64(sample_count))) {
381                 qDebug() << "Failed to configure sample count.";
382                 return;
383         }
384         _updating_sample_count = false;
385 }
386
387 void SamplingBar::commit_sample_rate()
388 {
389         uint64_t sample_rate = 0;
390
391         if (_updating_sample_rate)
392                 return;
393
394         const shared_ptr<device::DevInst> dev_inst = get_selected_device();
395         if (!dev_inst)
396                 return;
397
398         sample_rate = _sample_rate.value();
399         if (sample_rate == 0)
400                 return;
401
402         // Set the samplerate
403         assert(!_updating_sample_rate);
404         _updating_sample_rate = true;
405         if (!dev_inst->set_config(NULL, SR_CONF_SAMPLERATE,
406                 g_variant_new_uint64(sample_rate))) {
407                 qDebug() << "Failed to configure samplerate.";
408                 return;
409         }
410         _updating_sample_rate = false;
411 }
412
413 void SamplingBar::on_device_selected()
414 {
415         if (_updating_device_selector)
416                 return;
417
418         const shared_ptr<device::DevInst> dev_inst = get_selected_device();
419         if (!dev_inst)
420                 return;
421
422         _session.set_device(dev_inst);
423
424         update_device_config_widgets();
425 }
426
427 void SamplingBar::on_sample_count_changed()
428 {
429         commit_sample_count();
430 }
431
432 void SamplingBar::on_sample_rate_changed()
433 {
434         commit_sample_rate();
435 }
436
437 void SamplingBar::on_run_stop()
438 {
439         commit_sample_count();
440         commit_sample_rate();   
441         run_stop();
442 }
443
444 void SamplingBar::on_config_changed()
445 {
446         commit_sample_count();
447         update_sample_count_selector(); 
448         commit_sample_rate();   
449         update_sample_rate_selector();
450 }
451
452 bool SamplingBar::eventFilter(QObject *watched, QEvent *event)
453 {
454         if ((watched == &_sample_count || watched == &_sample_rate) &&
455                 (event->type() == QEvent::ToolTip)) {
456                 double sec = (double)_sample_count.value() / _sample_rate.value();
457                 QHelpEvent *help_event = static_cast<QHelpEvent*>(event);
458
459                 QString str = tr("Total sampling time: %1").arg(pv::util::format_second(sec));
460                 QToolTip::showText(help_event->globalPos(), str);
461
462                 return true;
463         }
464
465         return false;
466 }
467
468 } // namespace toolbars
469 } // namespace pv