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