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