1 /* -*- c++ -*- */
2 /*
3 * Copyright 2011-2013,2015 Free Software Foundation, Inc.
4 *
5 * This file is part of GNU Radio
6 *
7 * GNU Radio is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
10 * any later version.
11 *
12 * GNU Radio is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with GNU Radio; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "time_sink_f_impl.h"
28
29 #include <gnuradio/block_detail.h>
30 #include <gnuradio/buffer.h>
31 #include <gnuradio/fft/fft.h>
32 #include <gnuradio/io_signature.h>
33 #include <gnuradio/prefs.h>
34
35 #include <qwt_symbol.h>
36 #include <volk/volk.h>
37
38 #include <string.h>
39
40 namespace gr {
41 namespace qtgui {
42
make(int size,double samp_rate,const std::string & name,unsigned int nconnections,QWidget * parent)43 time_sink_f::sptr time_sink_f::make(int size,
44 double samp_rate,
45 const std::string& name,
46 unsigned int nconnections,
47 QWidget* parent)
48 {
49 return gnuradio::get_initial_sptr(
50 new time_sink_f_impl(size, samp_rate, name, nconnections, parent));
51 }
52
time_sink_f_impl(int size,double samp_rate,const std::string & name,unsigned int nconnections,QWidget * parent)53 time_sink_f_impl::time_sink_f_impl(int size,
54 double samp_rate,
55 const std::string& name,
56 unsigned int nconnections,
57 QWidget* parent)
58 : sync_block("time_sink_f",
59 io_signature::make(0, nconnections, sizeof(float)),
60 io_signature::make(0, 0, 0)),
61 d_size(size),
62 d_buffer_size(2 * size),
63 d_samp_rate(samp_rate),
64 d_name(name),
65 d_nconnections(nconnections),
66 d_parent(parent)
67 {
68 if (nconnections > 24)
69 throw std::runtime_error("time_sink_f only supports up to 24 inputs");
70
71 // Required now for Qt; argc must be greater than 0 and argv
72 // must have at least one valid character. Must be valid through
73 // life of the qApplication:
74 // http://harmattan-dev.nokia.com/docs/library/html/qt4/qapplication.html
75 d_argc = 1;
76 d_argv = new char;
77 d_argv[0] = '\0';
78
79 d_main_gui = NULL;
80
81 // setup PDU handling input port
82 message_port_register_in(pmt::mp("in"));
83 set_msg_handler(pmt::mp("in"), [this](pmt::pmt_t msg) { this->handle_pdus(msg); });
84
85 // +1 for the PDU buffer
86 for (unsigned int n = 0; n < d_nconnections + 1; n++) {
87 d_buffers.push_back(
88 (double*)volk_malloc(d_buffer_size * sizeof(double), volk_get_alignment()));
89 memset(d_buffers[n], 0, d_buffer_size * sizeof(double));
90
91 d_fbuffers.push_back(
92 (float*)volk_malloc(d_buffer_size * sizeof(float), volk_get_alignment()));
93 memset(d_fbuffers[n], 0, d_buffer_size * sizeof(float));
94 }
95
96 // Set alignment properties for VOLK
97 const int alignment_multiple = volk_get_alignment() / sizeof(float);
98 set_alignment(std::max(1, alignment_multiple));
99
100 d_tags = std::vector<std::vector<gr::tag_t>>(d_nconnections);
101
102 initialize();
103
104 d_main_gui->setNPoints(d_size); // setup GUI box with size
105 set_trigger_mode(TRIG_MODE_FREE, TRIG_SLOPE_POS, 0, 0, 0);
106
107 set_history(2); // so we can look ahead for the trigger slope
108 declare_sample_delay(1); // delay the tags for a history of 2
109 }
110
~time_sink_f_impl()111 time_sink_f_impl::~time_sink_f_impl()
112 {
113 if (!d_main_gui->isClosed())
114 d_main_gui->close();
115
116 // d_main_gui is a qwidget destroyed with its parent
117 for (unsigned int n = 0; n < d_nconnections + 1; n++) {
118 volk_free(d_buffers[n]);
119 volk_free(d_fbuffers[n]);
120 }
121
122 delete d_argv;
123 }
124
check_topology(int ninputs,int noutputs)125 bool time_sink_f_impl::check_topology(int ninputs, int noutputs)
126 {
127 return (unsigned int)(ninputs) == d_nconnections;
128 }
129
initialize()130 void time_sink_f_impl::initialize()
131 {
132 if (qApp != NULL) {
133 d_qApplication = qApp;
134 } else {
135 #if QT_VERSION >= 0x040500 && QT_VERSION < 0x050000
136 std::string style = prefs::singleton()->get_string("qtgui", "style", "raster");
137 QApplication::setGraphicsSystem(QString(style.c_str()));
138 #endif
139 d_qApplication = new QApplication(d_argc, &d_argv);
140 }
141
142 // If a style sheet is set in the prefs file, enable it here.
143 check_set_qss(d_qApplication);
144
145 unsigned int numplots = (d_nconnections > 0) ? d_nconnections : 1;
146 d_main_gui = new TimeDisplayForm(numplots, d_parent);
147 d_main_gui->setNPoints(d_size);
148 d_main_gui->setSampleRate(d_samp_rate);
149
150 if (!d_name.empty())
151 set_title(d_name);
152
153 // initialize update time to 10 times a second
154 set_update_time(0.1);
155 }
156
exec_()157 void time_sink_f_impl::exec_() { d_qApplication->exec(); }
158
qwidget()159 QWidget* time_sink_f_impl::qwidget() { return d_main_gui; }
160
161 #ifdef ENABLE_PYTHON
pyqwidget()162 PyObject* time_sink_f_impl::pyqwidget()
163 {
164 PyObject* w = PyLong_FromVoidPtr((void*)d_main_gui);
165 PyObject* retarg = Py_BuildValue("N", w);
166 return retarg;
167 }
168 #else
pyqwidget()169 void* time_sink_f_impl::pyqwidget() { return NULL; }
170 #endif
171
set_y_axis(double min,double max)172 void time_sink_f_impl::set_y_axis(double min, double max)
173 {
174 d_main_gui->setYaxis(min, max);
175 }
176
set_y_label(const std::string & label,const std::string & unit)177 void time_sink_f_impl::set_y_label(const std::string& label, const std::string& unit)
178 {
179 d_main_gui->setYLabel(label, unit);
180 }
181
set_update_time(double t)182 void time_sink_f_impl::set_update_time(double t)
183 {
184 // convert update time to ticks
185 gr::high_res_timer_type tps = gr::high_res_timer_tps();
186 d_update_time = t * tps;
187 d_main_gui->setUpdateTime(t);
188 d_last_time = 0;
189 }
190
set_title(const std::string & title)191 void time_sink_f_impl::set_title(const std::string& title)
192 {
193 d_main_gui->setTitle(title.c_str());
194 }
195
set_line_label(unsigned int which,const std::string & label)196 void time_sink_f_impl::set_line_label(unsigned int which, const std::string& label)
197 {
198 d_main_gui->setLineLabel(which, label.c_str());
199 }
200
set_line_color(unsigned int which,const std::string & color)201 void time_sink_f_impl::set_line_color(unsigned int which, const std::string& color)
202 {
203 d_main_gui->setLineColor(which, color.c_str());
204 }
205
set_line_width(unsigned int which,int width)206 void time_sink_f_impl::set_line_width(unsigned int which, int width)
207 {
208 d_main_gui->setLineWidth(which, width);
209 }
210
set_line_style(unsigned int which,int style)211 void time_sink_f_impl::set_line_style(unsigned int which, int style)
212 {
213 d_main_gui->setLineStyle(which, (Qt::PenStyle)style);
214 }
215
set_line_marker(unsigned int which,int marker)216 void time_sink_f_impl::set_line_marker(unsigned int which, int marker)
217 {
218 d_main_gui->setLineMarker(which, (QwtSymbol::Style)marker);
219 }
220
set_line_alpha(unsigned int which,double alpha)221 void time_sink_f_impl::set_line_alpha(unsigned int which, double alpha)
222 {
223 d_main_gui->setMarkerAlpha(which, (int)(255.0 * alpha));
224 }
225
set_trigger_mode(trigger_mode mode,trigger_slope slope,float level,float delay,int channel,const std::string & tag_key)226 void time_sink_f_impl::set_trigger_mode(trigger_mode mode,
227 trigger_slope slope,
228 float level,
229 float delay,
230 int channel,
231 const std::string& tag_key)
232 {
233 gr::thread::scoped_lock lock(d_setlock);
234
235 d_trigger_mode = mode;
236 d_trigger_slope = slope;
237 d_trigger_level = level;
238 d_trigger_delay = static_cast<int>(delay * d_samp_rate);
239 d_trigger_channel = channel;
240 d_trigger_tag_key = pmt::intern(tag_key);
241 d_triggered = false;
242 d_trigger_count = 0;
243
244 if ((d_trigger_delay < 0) || (d_trigger_delay >= d_size)) {
245 GR_LOG_WARN(
246 d_logger,
247 boost::format("Trigger delay (%1%) outside of display range (0:%2%).") %
248 (d_trigger_delay / d_samp_rate) % ((d_size - 1) / d_samp_rate));
249 d_trigger_delay = std::max(0, std::min(d_size - 1, d_trigger_delay));
250 delay = d_trigger_delay / d_samp_rate;
251 }
252
253 d_main_gui->setTriggerMode(d_trigger_mode);
254 d_main_gui->setTriggerSlope(d_trigger_slope);
255 d_main_gui->setTriggerLevel(d_trigger_level);
256 d_main_gui->setTriggerDelay(delay);
257 d_main_gui->setTriggerChannel(d_trigger_channel);
258 d_main_gui->setTriggerTagKey(tag_key);
259
260 _reset();
261 }
262
set_size(int width,int height)263 void time_sink_f_impl::set_size(int width, int height)
264 {
265 d_main_gui->resize(QSize(width, height));
266 }
267
title()268 std::string time_sink_f_impl::title() { return d_main_gui->title().toStdString(); }
269
line_label(unsigned int which)270 std::string time_sink_f_impl::line_label(unsigned int which)
271 {
272 return d_main_gui->lineLabel(which).toStdString();
273 }
274
line_color(unsigned int which)275 std::string time_sink_f_impl::line_color(unsigned int which)
276 {
277 return d_main_gui->lineColor(which).toStdString();
278 }
279
line_width(unsigned int which)280 int time_sink_f_impl::line_width(unsigned int which)
281 {
282 return d_main_gui->lineWidth(which);
283 }
284
line_style(unsigned int which)285 int time_sink_f_impl::line_style(unsigned int which)
286 {
287 return d_main_gui->lineStyle(which);
288 }
289
line_marker(unsigned int which)290 int time_sink_f_impl::line_marker(unsigned int which)
291 {
292 return d_main_gui->lineMarker(which);
293 }
294
line_alpha(unsigned int which)295 double time_sink_f_impl::line_alpha(unsigned int which)
296 {
297 return (double)(d_main_gui->markerAlpha(which)) / 255.0;
298 }
299
set_nsamps(const int newsize)300 void time_sink_f_impl::set_nsamps(const int newsize)
301 {
302 if (newsize != d_size) {
303 gr::thread::scoped_lock lock(d_setlock);
304
305 // Set new size and reset buffer index
306 // (throws away any currently held data, but who cares?)
307 d_size = newsize;
308 d_buffer_size = 2 * d_size;
309
310 // Resize buffers and replace data
311 for (unsigned int n = 0; n < d_nconnections + 1; n++) {
312 volk_free(d_buffers[n]);
313 d_buffers[n] = (double*)volk_malloc(d_buffer_size * sizeof(double),
314 volk_get_alignment());
315 memset(d_buffers[n], 0, d_buffer_size * sizeof(double));
316
317 volk_free(d_fbuffers[n]);
318 d_fbuffers[n] =
319 (float*)volk_malloc(d_buffer_size * sizeof(float), volk_get_alignment());
320 memset(d_fbuffers[n], 0, d_buffer_size * sizeof(float));
321 }
322
323 // If delay was set beyond the new boundary, pull it back.
324 if (d_trigger_delay >= d_size) {
325 GR_LOG_WARN(d_logger,
326 boost::format("Trigger delay (%1%) outside of display range "
327 "(0:%2%). Moving to 50%% point.") %
328 (d_trigger_delay / d_samp_rate) %
329 ((d_size - 1) / d_samp_rate));
330 d_trigger_delay = d_size - 1;
331 d_main_gui->setTriggerDelay(d_trigger_delay / d_samp_rate);
332 }
333
334 d_main_gui->setNPoints(d_size);
335 _reset();
336 }
337 }
338
set_samp_rate(const double samp_rate)339 void time_sink_f_impl::set_samp_rate(const double samp_rate)
340 {
341 gr::thread::scoped_lock lock(d_setlock);
342 d_samp_rate = samp_rate;
343 d_main_gui->setSampleRate(d_samp_rate);
344 }
345
nsamps() const346 int time_sink_f_impl::nsamps() const { return d_size; }
347
enable_stem_plot(bool en)348 void time_sink_f_impl::enable_stem_plot(bool en) { d_main_gui->setStem(en); }
349
enable_menu(bool en)350 void time_sink_f_impl::enable_menu(bool en) { d_main_gui->enableMenu(en); }
351
enable_grid(bool en)352 void time_sink_f_impl::enable_grid(bool en) { d_main_gui->setGrid(en); }
353
enable_autoscale(bool en)354 void time_sink_f_impl::enable_autoscale(bool en) { d_main_gui->autoScale(en); }
355
enable_semilogx(bool en)356 void time_sink_f_impl::enable_semilogx(bool en) { d_main_gui->setSemilogx(en); }
357
enable_semilogy(bool en)358 void time_sink_f_impl::enable_semilogy(bool en) { d_main_gui->setSemilogy(en); }
359
enable_control_panel(bool en)360 void time_sink_f_impl::enable_control_panel(bool en)
361 {
362 if (en)
363 d_main_gui->setupControlPanel();
364 else
365 d_main_gui->teardownControlPanel();
366 }
367
enable_tags(unsigned int which,bool en)368 void time_sink_f_impl::enable_tags(unsigned int which, bool en)
369 {
370 d_main_gui->setTagMenu(which, en);
371 }
372
enable_tags(bool en)373 void time_sink_f_impl::enable_tags(bool en)
374 {
375 for (unsigned int n = 0; n < d_nconnections; ++n) {
376 d_main_gui->setTagMenu(n, en);
377 }
378 }
379
enable_axis_labels(bool en)380 void time_sink_f_impl::enable_axis_labels(bool en) { d_main_gui->setAxisLabels(en); }
381
disable_legend()382 void time_sink_f_impl::disable_legend() { d_main_gui->disableLegend(); }
383
reset()384 void time_sink_f_impl::reset()
385 {
386 gr::thread::scoped_lock lock(d_setlock);
387 _reset();
388 }
389
_reset()390 void time_sink_f_impl::_reset()
391 {
392 unsigned int n;
393 if (d_trigger_delay) {
394 for (n = 0; n < d_nconnections; n++) {
395 // Move the tail of the buffers to the front. This section
396 // represents data that might have to be plotted again if a
397 // trigger occurs and we have a trigger delay set. The tail
398 // section is between (d_end-d_trigger_delay) and d_end.
399 memmove(d_fbuffers[n],
400 &d_fbuffers[n][d_end - d_trigger_delay],
401 d_trigger_delay * sizeof(float));
402
403 // Also move the offsets of any tags that occur in the tail
404 // section so they would be plotted again, too.
405 std::vector<gr::tag_t> tmp_tags;
406 for (size_t t = 0; t < d_tags[n].size(); t++) {
407 if (d_tags[n][t].offset > (uint64_t)(d_size - d_trigger_delay)) {
408 d_tags[n][t].offset =
409 d_tags[n][t].offset - (d_size - d_trigger_delay);
410 tmp_tags.push_back(d_tags[n][t]);
411 }
412 }
413 d_tags[n] = tmp_tags;
414 }
415 }
416 // Otherwise, just clear the local list of tags.
417 else {
418 for (n = 0; n < d_nconnections; n++) {
419 d_tags[n].clear();
420 }
421 }
422
423 // Reset the start and end indices.
424 d_start = 0;
425 d_end = d_size;
426
427 // Reset the trigger. If in free running mode, ignore the
428 // trigger delay and always set trigger to true.
429 if (d_trigger_mode == TRIG_MODE_FREE) {
430 d_index = 0;
431 d_triggered = true;
432 } else {
433 d_index = d_trigger_delay;
434 d_triggered = false;
435 }
436 }
437
_npoints_resize()438 void time_sink_f_impl::_npoints_resize()
439 {
440 int newsize = d_main_gui->getNPoints();
441 set_nsamps(newsize);
442 }
443
_adjust_tags(int adj)444 void time_sink_f_impl::_adjust_tags(int adj)
445 {
446 for (size_t n = 0; n < d_tags.size(); n++) {
447 for (size_t t = 0; t < d_tags[n].size(); t++) {
448 d_tags[n][t].offset += adj;
449 }
450 }
451 }
452
_gui_update_trigger()453 void time_sink_f_impl::_gui_update_trigger()
454 {
455 d_trigger_mode = d_main_gui->getTriggerMode();
456 d_trigger_slope = d_main_gui->getTriggerSlope();
457 d_trigger_level = d_main_gui->getTriggerLevel();
458 d_trigger_channel = d_main_gui->getTriggerChannel();
459 d_trigger_count = 0;
460
461 float delayf = d_main_gui->getTriggerDelay();
462 int delay = static_cast<int>(delayf * d_samp_rate);
463
464 if (delay != d_trigger_delay) {
465 // We restrict the delay to be within the window of time being
466 // plotted.
467 if ((delay < 0) || (delay >= d_size)) {
468 GR_LOG_WARN(
469 d_logger,
470 boost::format("Trigger delay (%1%) outside of display range (0:%2%).") %
471 (delay / d_samp_rate) % ((d_size - 1) / d_samp_rate));
472 delay = std::max(0, std::min(d_size - 1, delay));
473 delayf = delay / d_samp_rate;
474 }
475
476 d_trigger_delay = delay;
477 d_main_gui->setTriggerDelay(delayf);
478 _reset();
479 }
480
481 std::string tagkey = d_main_gui->getTriggerTagKey();
482 d_trigger_tag_key = pmt::intern(tagkey);
483 }
484
_test_trigger_tags(int nitems)485 void time_sink_f_impl::_test_trigger_tags(int nitems)
486 {
487 int trigger_index;
488
489 uint64_t nr = nitems_read(d_trigger_channel);
490 std::vector<gr::tag_t> tags;
491 get_tags_in_range(tags, d_trigger_channel, nr, nr + nitems + 1, d_trigger_tag_key);
492 if (!tags.empty()) {
493 trigger_index = tags[0].offset - nr;
494 int start = d_index + trigger_index - d_trigger_delay - 1;
495 if (start >= 0) {
496 d_triggered = true;
497 d_start = start;
498 d_end = d_start + d_size;
499 d_trigger_count = 0;
500 _adjust_tags(-d_start);
501 }
502 }
503 }
504
_test_trigger_norm(int nitems,gr_vector_const_void_star inputs)505 void time_sink_f_impl::_test_trigger_norm(int nitems, gr_vector_const_void_star inputs)
506 {
507 int trigger_index;
508 const float* in = (const float*)inputs[d_trigger_channel];
509 for (trigger_index = 0; trigger_index < nitems; trigger_index++) {
510 d_trigger_count++;
511
512 // Test if trigger has occurred based on the input stream,
513 // channel number, and slope direction
514 if (_test_trigger_slope(&in[trigger_index])) {
515 d_triggered = true;
516 d_start = d_index + trigger_index - d_trigger_delay;
517 d_end = d_start + d_size;
518 d_trigger_count = 0;
519 _adjust_tags(-d_start);
520 break;
521 }
522 }
523
524 // If using auto trigger mode, trigger periodically even
525 // without a trigger event.
526 if ((d_trigger_mode == TRIG_MODE_AUTO) && (d_trigger_count > d_size)) {
527 d_triggered = true;
528 d_trigger_count = 0;
529 }
530 }
531
_test_trigger_slope(const float * in) const532 bool time_sink_f_impl::_test_trigger_slope(const float* in) const
533 {
534 float x0, x1;
535 x0 = in[0];
536 x1 = in[1];
537
538 if (d_trigger_slope == TRIG_SLOPE_POS)
539 return ((x0 <= d_trigger_level) && (x1 > d_trigger_level));
540 else
541 return ((x0 >= d_trigger_level) && (x1 < d_trigger_level));
542 }
543
work(int noutput_items,gr_vector_const_void_star & input_items,gr_vector_void_star & output_items)544 int time_sink_f_impl::work(int noutput_items,
545 gr_vector_const_void_star& input_items,
546 gr_vector_void_star& output_items)
547 {
548 unsigned int n = 0, idx = 0;
549 const float* in;
550
551 _npoints_resize();
552 _gui_update_trigger();
553
554 gr::thread::scoped_lock lock(d_setlock);
555
556 int nfill = d_end - d_index; // how much room left in buffers
557 int nitems = std::min(noutput_items, nfill); // num items we can put in buffers
558
559 // If auto, normal, or tag trigger, look for the trigger
560 if ((d_trigger_mode != TRIG_MODE_FREE) && !d_triggered) {
561 // trigger off a tag key (first one found)
562 if (d_trigger_mode == TRIG_MODE_TAG) {
563 _test_trigger_tags(nitems);
564 }
565 // Normal or Auto trigger
566 else {
567 _test_trigger_norm(nitems, input_items);
568 }
569 }
570
571 // Copy data into the buffers.
572 for (n = 0; n < d_nconnections; n++) {
573 in = (const float*)input_items[idx];
574 memcpy(&d_fbuffers[n][d_index], &in[1], nitems * sizeof(float));
575 // volk_32f_convert_64f(&d_buffers[n][d_index],
576 // &in[1], nitems);
577
578 uint64_t nr = nitems_read(idx);
579 std::vector<gr::tag_t> tags;
580 get_tags_in_range(tags, idx, nr, nr + nitems + 1);
581 for (size_t t = 0; t < tags.size(); t++) {
582 tags[t].offset = tags[t].offset - nr + (d_index - d_start - 1);
583 }
584 d_tags[idx].insert(d_tags[idx].end(), tags.begin(), tags.end());
585 idx++;
586 }
587 d_index += nitems;
588
589 // If we've have a trigger and a full d_size of items in the buffers, plot.
590 if ((d_triggered) && (d_index == d_end)) {
591 // Copy data to be plotted to start of buffers.
592 for (n = 0; n < d_nconnections; n++) {
593 // memmove(d_buffers[n], &d_buffers[n][d_start], d_size*sizeof(double));
594 volk_32f_convert_64f(d_buffers[n], &d_fbuffers[n][d_start], d_size);
595 }
596
597 // Plot if we are able to update
598 if (gr::high_res_timer_now() - d_last_time > d_update_time) {
599 d_last_time = gr::high_res_timer_now();
600 d_qApplication->postEvent(d_main_gui,
601 new TimeUpdateEvent(d_buffers, d_size, d_tags));
602 }
603
604 // We've plotting, so reset the state
605 _reset();
606 }
607
608 // If we've filled up the buffers but haven't triggered, reset.
609 if (d_index == d_end) {
610 _reset();
611 }
612
613 return nitems;
614 }
615
handle_pdus(pmt::pmt_t msg)616 void time_sink_f_impl::handle_pdus(pmt::pmt_t msg)
617 {
618 size_t len;
619 pmt::pmt_t dict, samples;
620
621 // Test to make sure this is either a PDU or a uniform vector of
622 // samples. Get the samples PMT and the dictionary if it's a PDU.
623 // If not, we throw an error and exit.
624 if (pmt::is_pair(msg)) {
625 dict = pmt::car(msg);
626 samples = pmt::cdr(msg);
627 } else if (pmt::is_uniform_vector(msg)) {
628 samples = msg;
629 } else {
630 throw std::runtime_error("time_sink_c: message must be either "
631 "a PDU or a uniform vector of samples.");
632 }
633
634 len = pmt::length(samples);
635
636 const float* in;
637 if (pmt::is_f32vector(samples)) {
638 in = (const float*)pmt::f32vector_elements(samples, len);
639 } else {
640 throw std::runtime_error("time_sink_f: unknown data type "
641 "of samples; must be float.");
642 }
643
644 // Plot if we're past the last update time
645 if (gr::high_res_timer_now() - d_last_time > d_update_time) {
646 d_last_time = gr::high_res_timer_now();
647
648 set_nsamps(len);
649
650 volk_32f_convert_64f(d_buffers[d_nconnections], in, len);
651
652 std::vector<std::vector<gr::tag_t>> t;
653 d_qApplication->postEvent(d_main_gui, new TimeUpdateEvent(d_buffers, len, t));
654 }
655 }
656
657 } /* namespace qtgui */
658 } /* namespace gr */
659