1 /* -*- c++ -*- */
2 /*
3 * Copyright 2012,2014-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 "const_sink_c_impl.h"
28
29 #include <gnuradio/io_signature.h>
30 #include <gnuradio/prefs.h>
31 #include <qwt_symbol.h>
32 #include <string.h>
33 #include <volk/volk.h>
34
35 namespace gr {
36 namespace qtgui {
37
38 const_sink_c::sptr
make(int size,const std::string & name,int nconnections,QWidget * parent)39 const_sink_c::make(int size, const std::string& name, int nconnections, QWidget* parent)
40 {
41 return gnuradio::get_initial_sptr(
42 new const_sink_c_impl(size, name, nconnections, parent));
43 }
44
const_sink_c_impl(int size,const std::string & name,int nconnections,QWidget * parent)45 const_sink_c_impl::const_sink_c_impl(int size,
46 const std::string& name,
47 int nconnections,
48 QWidget* parent)
49 : sync_block("const_sink_c",
50 io_signature::make(0, nconnections, sizeof(gr_complex)),
51 io_signature::make(0, 0, 0)),
52 d_size(size),
53 d_buffer_size(2 * size),
54 d_name(name),
55 d_nconnections(nconnections),
56 d_parent(parent)
57 {
58 // Required now for Qt; argc must be greater than 0 and argv
59 // must have at least one valid character. Must be valid through
60 // life of the qApplication:
61 // http://harmattan-dev.nokia.com/docs/library/html/qt4/qapplication.html
62 d_argc = 1;
63 d_argv = new char;
64 d_argv[0] = '\0';
65
66 d_main_gui = NULL;
67
68 d_index = 0;
69
70 // setup PDU handling input port
71 message_port_register_in(pmt::mp("in"));
72 set_msg_handler(pmt::mp("in"), [this](pmt::pmt_t msg) { this->handle_pdus(msg); });
73
74 for (int i = 0; i < d_nconnections; i++) {
75 d_residbufs_real.push_back(
76 (double*)volk_malloc(d_buffer_size * sizeof(double), volk_get_alignment()));
77 d_residbufs_imag.push_back(
78 (double*)volk_malloc(d_buffer_size * sizeof(double), volk_get_alignment()));
79 memset(d_residbufs_real[i], 0, d_buffer_size * sizeof(double));
80 memset(d_residbufs_imag[i], 0, d_buffer_size * sizeof(double));
81 }
82
83 // Used for PDU message input
84 d_residbufs_real.push_back(
85 (double*)volk_malloc(d_buffer_size * sizeof(double), volk_get_alignment()));
86 d_residbufs_imag.push_back(
87 (double*)volk_malloc(d_buffer_size * sizeof(double), volk_get_alignment()));
88 memset(d_residbufs_real[d_nconnections], 0, d_buffer_size * sizeof(double));
89 memset(d_residbufs_imag[d_nconnections], 0, d_buffer_size * sizeof(double));
90
91 // Set alignment properties for VOLK
92 const int alignment_multiple = volk_get_alignment() / sizeof(gr_complex);
93 set_alignment(std::max(1, alignment_multiple));
94
95 initialize();
96
97 set_trigger_mode(TRIG_MODE_FREE, TRIG_SLOPE_POS, 0, 0);
98
99 set_history(2); // so we can look ahead for the trigger slope
100 }
101
~const_sink_c_impl()102 const_sink_c_impl::~const_sink_c_impl()
103 {
104 if (!d_main_gui->isClosed())
105 d_main_gui->close();
106
107 // d_main_gui is a qwidget destroyed with its parent
108 for (int i = 0; i < d_nconnections + 1; i++) {
109 volk_free(d_residbufs_real[i]);
110 volk_free(d_residbufs_imag[i]);
111 }
112
113 delete d_argv;
114 }
115
check_topology(int ninputs,int noutputs)116 bool const_sink_c_impl::check_topology(int ninputs, int noutputs)
117 {
118 return ninputs == d_nconnections;
119 }
120
initialize()121 void const_sink_c_impl::initialize()
122 {
123 if (qApp != NULL) {
124 d_qApplication = qApp;
125 } else {
126 #if QT_VERSION >= 0x040500 && QT_VERSION < 0x050000
127 std::string style = prefs::singleton()->get_string("qtgui", "style", "raster");
128 QApplication::setGraphicsSystem(QString(style.c_str()));
129 #endif
130 d_qApplication = new QApplication(d_argc, &d_argv);
131 }
132
133 // If a style sheet is set in the prefs file, enable it here.
134 check_set_qss(d_qApplication);
135
136 int numplots = (d_nconnections > 0) ? d_nconnections : 1;
137 d_main_gui = new ConstellationDisplayForm(numplots, d_parent);
138 d_main_gui->setNPoints(d_size);
139
140 if (!d_name.empty())
141 set_title(d_name);
142
143 // initialize update time to 10 times a second
144 set_update_time(0.1);
145 }
146
exec_()147 void const_sink_c_impl::exec_() { d_qApplication->exec(); }
148
qwidget()149 QWidget* const_sink_c_impl::qwidget() { return d_main_gui; }
150
151 #ifdef ENABLE_PYTHON
pyqwidget()152 PyObject* const_sink_c_impl::pyqwidget()
153 {
154 PyObject* w = PyLong_FromVoidPtr((void*)d_main_gui);
155 PyObject* retarg = Py_BuildValue("N", w);
156 return retarg;
157 }
158 #else
pyqwidget()159 void* const_sink_c_impl::pyqwidget() { return NULL; }
160 #endif
161
set_y_axis(double min,double max)162 void const_sink_c_impl::set_y_axis(double min, double max)
163 {
164 d_main_gui->setYaxis(min, max);
165 }
166
set_x_axis(double min,double max)167 void const_sink_c_impl::set_x_axis(double min, double max)
168 {
169 d_main_gui->setXaxis(min, max);
170 }
171
set_update_time(double t)172 void const_sink_c_impl::set_update_time(double t)
173 {
174 // convert update time to ticks
175 gr::high_res_timer_type tps = gr::high_res_timer_tps();
176 d_update_time = t * tps;
177 d_main_gui->setUpdateTime(t);
178 d_last_time = 0;
179 }
180
set_title(const std::string & title)181 void const_sink_c_impl::set_title(const std::string& title)
182 {
183 d_main_gui->setTitle(title.c_str());
184 }
185
set_line_label(unsigned int which,const std::string & label)186 void const_sink_c_impl::set_line_label(unsigned int which, const std::string& label)
187 {
188 d_main_gui->setLineLabel(which, label.c_str());
189 }
190
set_line_color(unsigned int which,const std::string & color)191 void const_sink_c_impl::set_line_color(unsigned int which, const std::string& color)
192 {
193 d_main_gui->setLineColor(which, color.c_str());
194 }
195
set_line_width(unsigned int which,int width)196 void const_sink_c_impl::set_line_width(unsigned int which, int width)
197 {
198 d_main_gui->setLineWidth(which, width);
199 }
200
set_line_style(unsigned int which,int style)201 void const_sink_c_impl::set_line_style(unsigned int which, int style)
202 {
203 d_main_gui->setLineStyle(which, (Qt::PenStyle)style);
204 }
205
set_line_marker(unsigned int which,int marker)206 void const_sink_c_impl::set_line_marker(unsigned int which, int marker)
207 {
208 d_main_gui->setLineMarker(which, (QwtSymbol::Style)marker);
209 }
210
set_line_alpha(unsigned int which,double alpha)211 void const_sink_c_impl::set_line_alpha(unsigned int which, double alpha)
212 {
213 d_main_gui->setMarkerAlpha(which, (int)(255.0 * alpha));
214 }
215
set_trigger_mode(trigger_mode mode,trigger_slope slope,float level,int channel,const std::string & tag_key)216 void const_sink_c_impl::set_trigger_mode(trigger_mode mode,
217 trigger_slope slope,
218 float level,
219 int channel,
220 const std::string& tag_key)
221 {
222 gr::thread::scoped_lock lock(d_setlock);
223
224 d_trigger_mode = mode;
225 d_trigger_slope = slope;
226 d_trigger_level = level;
227 d_trigger_channel = channel;
228 d_trigger_tag_key = pmt::intern(tag_key);
229 d_triggered = false;
230 d_trigger_count = 0;
231
232 d_main_gui->setTriggerMode(d_trigger_mode);
233 d_main_gui->setTriggerSlope(d_trigger_slope);
234 d_main_gui->setTriggerLevel(d_trigger_level);
235 d_main_gui->setTriggerChannel(d_trigger_channel);
236 d_main_gui->setTriggerTagKey(tag_key);
237
238 _reset();
239 }
240
set_size(int width,int height)241 void const_sink_c_impl::set_size(int width, int height)
242 {
243 d_main_gui->resize(QSize(width, height));
244 }
245
title()246 std::string const_sink_c_impl::title() { return d_main_gui->title().toStdString(); }
247
line_label(unsigned int which)248 std::string const_sink_c_impl::line_label(unsigned int which)
249 {
250 return d_main_gui->lineLabel(which).toStdString();
251 }
252
line_color(unsigned int which)253 std::string const_sink_c_impl::line_color(unsigned int which)
254 {
255 return d_main_gui->lineColor(which).toStdString();
256 }
257
line_width(unsigned int which)258 int const_sink_c_impl::line_width(unsigned int which)
259 {
260 return d_main_gui->lineWidth(which);
261 }
262
line_style(unsigned int which)263 int const_sink_c_impl::line_style(unsigned int which)
264 {
265 return d_main_gui->lineStyle(which);
266 }
267
line_marker(unsigned int which)268 int const_sink_c_impl::line_marker(unsigned int which)
269 {
270 return d_main_gui->lineMarker(which);
271 }
272
line_alpha(unsigned int which)273 double const_sink_c_impl::line_alpha(unsigned int which)
274 {
275 return (double)(d_main_gui->markerAlpha(which)) / 255.0;
276 }
277
set_nsamps(const int newsize)278 void const_sink_c_impl::set_nsamps(const int newsize)
279 {
280 gr::thread::scoped_lock lock(d_setlock);
281
282 if (newsize != d_size) {
283 // Set new size and reset buffer index
284 // (throws away any currently held data, but who cares?)
285 d_size = newsize;
286 d_buffer_size = 2 * d_size;
287 d_index = 0;
288
289 // Resize residbuf and replace data
290 // +1 to handle PDU message input buffers
291 for (int i = 0; i < d_nconnections + 1; i++) {
292 volk_free(d_residbufs_real[i]);
293 volk_free(d_residbufs_imag[i]);
294 d_residbufs_real[i] = (double*)volk_malloc(d_buffer_size * sizeof(double),
295 volk_get_alignment());
296 d_residbufs_imag[i] = (double*)volk_malloc(d_buffer_size * sizeof(double),
297 volk_get_alignment());
298
299 memset(d_residbufs_real[i], 0, d_buffer_size * sizeof(double));
300 memset(d_residbufs_imag[i], 0, d_buffer_size * sizeof(double));
301 }
302
303 d_main_gui->setNPoints(d_size);
304 _reset();
305 }
306 }
307
nsamps() const308 int const_sink_c_impl::nsamps() const { return d_size; }
309
enable_menu(bool en)310 void const_sink_c_impl::enable_menu(bool en) { d_main_gui->enableMenu(en); }
311
enable_autoscale(bool en)312 void const_sink_c_impl::enable_autoscale(bool en) { d_main_gui->autoScale(en); }
313
enable_grid(bool en)314 void const_sink_c_impl::enable_grid(bool en) { d_main_gui->setGrid(en); }
315
enable_axis_labels(bool en)316 void const_sink_c_impl::enable_axis_labels(bool en) { d_main_gui->setAxisLabels(en); }
317
disable_legend()318 void const_sink_c_impl::disable_legend() { d_main_gui->disableLegend(); }
319
reset()320 void const_sink_c_impl::reset()
321 {
322 gr::thread::scoped_lock lock(d_setlock);
323 _reset();
324 }
325
_reset()326 void const_sink_c_impl::_reset()
327 {
328 // Reset the start and end indices.
329 d_start = 0;
330 d_end = d_size;
331 d_index = 0;
332
333 // Reset the trigger.
334 if (d_trigger_mode == TRIG_MODE_FREE) {
335 d_triggered = true;
336 } else {
337 d_triggered = false;
338 }
339 }
340
_npoints_resize()341 void const_sink_c_impl::_npoints_resize()
342 {
343 int newsize = d_main_gui->getNPoints();
344 set_nsamps(newsize);
345 }
346
_gui_update_trigger()347 void const_sink_c_impl::_gui_update_trigger()
348 {
349 d_trigger_mode = d_main_gui->getTriggerMode();
350 d_trigger_slope = d_main_gui->getTriggerSlope();
351 d_trigger_level = d_main_gui->getTriggerLevel();
352 d_trigger_channel = d_main_gui->getTriggerChannel();
353 d_trigger_count = 0;
354
355 std::string tagkey = d_main_gui->getTriggerTagKey();
356 d_trigger_tag_key = pmt::intern(tagkey);
357 }
358
_test_trigger_tags(int nitems)359 void const_sink_c_impl::_test_trigger_tags(int nitems)
360 {
361 int trigger_index;
362
363 uint64_t nr = nitems_read(d_trigger_channel);
364 std::vector<gr::tag_t> tags;
365 get_tags_in_range(tags, d_trigger_channel, nr, nr + nitems, d_trigger_tag_key);
366 if (!tags.empty()) {
367 d_triggered = true;
368 trigger_index = tags[0].offset - nr;
369 d_start = d_index + trigger_index;
370 d_end = d_start + d_size;
371 d_trigger_count = 0;
372 }
373 }
374
_test_trigger_norm(int nitems,gr_vector_const_void_star inputs)375 void const_sink_c_impl::_test_trigger_norm(int nitems, gr_vector_const_void_star inputs)
376 {
377 int trigger_index;
378 const gr_complex* in = (const gr_complex*)inputs[d_trigger_channel];
379 for (trigger_index = 0; trigger_index < nitems - 1; trigger_index++) {
380 d_trigger_count++;
381
382 // Test if trigger has occurred based on the input stream,
383 // channel number, and slope direction
384 if (_test_trigger_slope(&in[trigger_index])) {
385 d_triggered = true;
386 d_start = d_index + trigger_index;
387 d_end = d_start + d_size;
388 d_trigger_count = 0;
389 break;
390 }
391 }
392
393 // If using auto trigger mode, trigger periodically even
394 // without a trigger event.
395 if ((d_trigger_mode == TRIG_MODE_AUTO) && (d_trigger_count > d_size)) {
396 d_triggered = true;
397 d_trigger_count = 0;
398 }
399 }
400
_test_trigger_slope(const gr_complex * in) const401 bool const_sink_c_impl::_test_trigger_slope(const gr_complex* in) const
402 {
403 float x0, x1;
404 x0 = abs(in[0]);
405 x1 = abs(in[1]);
406
407 if (d_trigger_slope == TRIG_SLOPE_POS)
408 return ((x0 <= d_trigger_level) && (x1 > d_trigger_level));
409 else
410 return ((x0 >= d_trigger_level) && (x1 < d_trigger_level));
411 }
412
work(int noutput_items,gr_vector_const_void_star & input_items,gr_vector_void_star & output_items)413 int const_sink_c_impl::work(int noutput_items,
414 gr_vector_const_void_star& input_items,
415 gr_vector_void_star& output_items)
416 {
417 int n = 0;
418 const gr_complex* in;
419
420 _npoints_resize();
421 _gui_update_trigger();
422
423 int nfill = d_end - d_index; // how much room left in buffers
424 int nitems = std::min(noutput_items, nfill); // num items we can put in buffers
425
426 // If auto, normal, or tag trigger, look for the trigger
427 if ((d_trigger_mode != TRIG_MODE_FREE) && !d_triggered) {
428 // trigger off a tag key (first one found)
429 if (d_trigger_mode == TRIG_MODE_TAG) {
430 _test_trigger_tags(nitems);
431 }
432 // Normal or Auto trigger
433 else {
434 _test_trigger_norm(nitems, input_items);
435 }
436 }
437
438 // Copy data into the buffers.
439 for (n = 0; n < d_nconnections; n++) {
440 in = (const gr_complex*)input_items[n];
441 volk_32fc_deinterleave_64f_x2(&d_residbufs_real[n][d_index],
442 &d_residbufs_imag[n][d_index],
443 &in[history() - 1],
444 nitems);
445 }
446 d_index += nitems;
447
448
449 // If we have a trigger and a full d_size of items in the buffers, plot.
450 if ((d_triggered) && (d_index == d_end)) {
451 // Copy data to be plotted to start of buffers.
452 for (n = 0; n < d_nconnections; n++) {
453 memmove(d_residbufs_real[n],
454 &d_residbufs_real[n][d_start],
455 d_size * sizeof(double));
456 memmove(d_residbufs_imag[n],
457 &d_residbufs_imag[n][d_start],
458 d_size * sizeof(double));
459 }
460
461 // Plot if we are able to update
462 if (gr::high_res_timer_now() - d_last_time > d_update_time) {
463 d_last_time = gr::high_res_timer_now();
464 d_qApplication->postEvent(
465 d_main_gui,
466 new ConstUpdateEvent(d_residbufs_real, d_residbufs_imag, d_size));
467 }
468
469 // We've plotting, so reset the state
470 _reset();
471 }
472
473 // If we've filled up the buffers but haven't triggered, reset.
474 if (d_index == d_end) {
475 _reset();
476 }
477
478 return nitems;
479 }
480
handle_pdus(pmt::pmt_t msg)481 void const_sink_c_impl::handle_pdus(pmt::pmt_t msg)
482 {
483 size_t len = 0;
484 pmt::pmt_t dict, samples;
485
486 // Test to make sure this is either a PDU or a uniform vector of
487 // samples. Get the samples PMT and the dictionary if it's a PDU.
488 // If not, we throw an error and exit.
489 if (pmt::is_pair(msg)) {
490 dict = pmt::car(msg);
491 samples = pmt::cdr(msg);
492 } else if (pmt::is_uniform_vector(msg)) {
493 samples = msg;
494 } else {
495 throw std::runtime_error("const_sink_c: message must be either "
496 "a PDU or a uniform vector of samples.");
497 }
498
499 len = pmt::length(samples);
500
501 const gr_complex* in;
502 if (pmt::is_c32vector(samples)) {
503 in = (const gr_complex*)pmt::c32vector_elements(samples, len);
504 } else {
505 throw std::runtime_error("const_sink_c: unknown data type "
506 "of samples; must be complex.");
507 }
508
509 set_nsamps(len);
510
511 // Plot if we're past the last update time
512 if (gr::high_res_timer_now() - d_last_time > d_update_time) {
513 d_last_time = gr::high_res_timer_now();
514
515 // Copy data into the buffers.
516 volk_32fc_deinterleave_64f_x2(
517 d_residbufs_real[d_nconnections], d_residbufs_imag[d_nconnections], in, len);
518
519 d_qApplication->postEvent(
520 d_main_gui, new ConstUpdateEvent(d_residbufs_real, d_residbufs_imag, len));
521 }
522 }
523
524 } /* namespace qtgui */
525 } /* namespace gr */
526