diff options
24 files changed, 3373 insertions, 0 deletions
diff --git a/gr-qtgui/examples/pyqt_time_raster_b.py b/gr-qtgui/examples/pyqt_time_raster_b.py new file mode 100755 index 0000000000..b9db6e1e8e --- /dev/null +++ b/gr-qtgui/examples/pyqt_time_raster_b.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +import sys + +try: + from gnuradio import qtgui + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + print "Error: Program requires PyQt4 and gr-qtgui." + sys.exit(1) + +class dialog_box(QtGui.QWidget): + def __init__(self, display): + QtGui.QWidget.__init__(self, None) + self.setWindowTitle('PyQt Test GUI') + + self.boxlayout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight, self) + self.boxlayout.addWidget(display, 1) + + self.resize(800, 500) + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + self.qapp = QtGui.QApplication(sys.argv) + + data0 = 10*[0,] + 40*[1,0] + 10*[0,] + data0 += 10*[0,] + 40*[0,1] + 10*[0,] + data1 = 20*[0,] + [0,0,0,1,1,1,0,0,0,0] + 70*[0,] + + # Adjust these to change the layout of the plot. + # Can be set to fractions. + ncols = 100.25 + nrows = 100 + + fs = 200 + src0 = gr.vector_source_b(data0, True) + src1 = gr.vector_source_b(data1, True) + thr = gr.throttle(gr.sizeof_char, 50000) + head = gr.head(gr.sizeof_char, 10000000) + self.snk1 = qtgui.time_raster_sink_b(fs, nrows, ncols, [], [], + "Time Raster Example", 2) + + self.connect(src0, thr, (self.snk1, 0)) + self.connect(src1, (self.snk1, 1)) + + # Get the reference pointer to the SpectrumDisplayForm QWidget + pyQt = self.snk1.pyqwidget() + + # Wrap the pointer as a PyQt SIP object + # This can now be manipulated as a PyQt4.QtGui.QWidget + pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + self.main_box = dialog_box(pyWin) + self.main_box.show() + +if __name__ == "__main__": + tb = my_top_block(); + tb.start() + tb.qapp.exec_() + tb.stop() + diff --git a/gr-qtgui/examples/pyqt_time_raster_f.py b/gr-qtgui/examples/pyqt_time_raster_f.py new file mode 100755 index 0000000000..0b343e6a6e --- /dev/null +++ b/gr-qtgui/examples/pyqt_time_raster_f.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +import sys + +try: + from gnuradio import qtgui + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + print "Error: Program requires PyQt4 and gr-qtgui." + sys.exit(1) + +class dialog_box(QtGui.QWidget): + def __init__(self, display): + QtGui.QWidget.__init__(self, None) + self.setWindowTitle('PyQt Test GUI') + + self.boxlayout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight, self) + self.boxlayout.addWidget(display, 1) + + self.resize(800, 500) + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + self.qapp = QtGui.QApplication(sys.argv) + + data0 = 10*[0,] + 40*[1,0] + 10*[0,] + data0 += 10*[0,] + 40*[0,1] + 10*[0,] + data1 = 20*[0,] + [0,0,0,1,1,1,0,0,0,0] + 70*[0,] + + # Adjust these to change the layout of the plot. + # Can be set to fractions. + ncols = 100.25 + nrows = 100 + + fs = 200 + src0 = gr.vector_source_f(data0, True) + src1 = gr.vector_source_f(data1, True) + thr = gr.throttle(gr.sizeof_float, 50000) + hed = gr.head(gr.sizeof_float, 10000000) + self.snk1 = qtgui.time_raster_sink_f(fs, nrows, ncols, [], [], + "Float Time Raster Example", 2) + + self.connect(src0, thr, (self.snk1, 0)) + self.connect(src1, (self.snk1, 1)) + + # Get the reference pointer to the SpectrumDisplayForm QWidget + pyQt = self.snk1.pyqwidget() + + # Wrap the pointer as a PyQt SIP object + # This can now be manipulated as a PyQt4.QtGui.QWidget + pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + self.main_box = dialog_box(pyWin) + self.main_box.show() + +if __name__ == "__main__": + tb = my_top_block(); + tb.start() + tb.qapp.exec_() + tb.stop() + diff --git a/gr-qtgui/grc/qtgui_time_raster_x.xml b/gr-qtgui/grc/qtgui_time_raster_x.xml new file mode 100644 index 0000000000..e50810f29a --- /dev/null +++ b/gr-qtgui/grc/qtgui_time_raster_x.xml @@ -0,0 +1,106 @@ +<?xml version="1.0"?> +<!-- +################################################### +##QT GUI Sink +################################################### + --> +<block> + <name>QT GUI Time Raster Sink</name> + <key>qtgui_time_raster_sink_x</key> + <import>from PyQt4 import Qt</import> + <import>from gnuradio import qtgui</import> + <import>import sip</import> + <make>#set $win = 'self._%s_win'%$id +qtgui.$(type.fcn)( + $samp_rate, + $nrows, + $ncols, + $mult, + $offset, + $name, + $nconnections, + ) +self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget) +$(gui_hint()($win))</make> + <callback>set_num_rows($nrows)</callback> + <callback>set_num_cols($ncols)</callback> + <callback>set_multiplier($mult)</callback> + <callback>set_offset($offset)</callback> + <callback>set_update_time($t)</callback> + <callback>set_title($which, $title)</callback> + <callback>set_color($which, $color)</callback> + <param> + <name>Type</name> + <key>type</key> + <value>complex</value> + <type>enum</type> + <option><name>Byte</name><key>byte</key><opt>fcn:time_raster_sink_b</opt></option> + <option><name>Float</name><key>float</key><opt>fcn:time_raster_sink_f</opt></option> + </param> + <param> + <name>Name</name> + <key>name</key> + <value>QT GUI Plot</value> + <type>string</type> + </param> + <param> + <name>Sample Rate</name> + <key>samp_rate</key> + <value>1</value> + <type>real</type> + </param> + <param> + <name>Num. Rows</name> + <key>nrows</key> + <type>int</type> + </param> + <param> + <name>Num. Cols</name> + <key>ncols</key> + <type>int</type> + </param> + <param> + <name>Multiplier</name> + <key>mult</key> + <value>[]</value> + <type>real_vector</type> + <hide>part</hide> + </param> + <param> + <name>Offset</name> + <key>offset</key> + <value>[]</value> + <type>real_vector</type> + <hide>part</hide> + </param> + <param> + <name>Number of Inputs</name> + <key>nconnections</key> + <value>1</value> + <type>int</type> + <hide>part</hide> + </param> + <param> + <name>Update Rate</name> + <key>rate</key> + <value>10</value> + <type>real</type> + </param> + <param> + <name>GUI Hint</name> + <key>gui_hint</key> + <value></value> + <type>gui_hint</type> + <hide>part</hide> + </param> + <sink> + <name>in</name> + <type>$type</type> + <nports>$nconnections</nports> + </sink> + <doc> +The GUI hint can be used to position the widget within the application. \ +The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \ +Both the tab specification and the grid position are optional. + </doc> +</block> diff --git a/gr-qtgui/include/qtgui/CMakeLists.txt b/gr-qtgui/include/qtgui/CMakeLists.txt index d0d3d13fb5..f3ef6808e8 100644 --- a/gr-qtgui/include/qtgui/CMakeLists.txt +++ b/gr-qtgui/include/qtgui/CMakeLists.txt @@ -24,6 +24,8 @@ install(FILES api.h time_sink_c.h time_sink_f.h + time_raster_sink_b.h + time_raster_sink_f.h freq_sink_c.h freq_sink_f.h const_sink_c.h diff --git a/gr-qtgui/include/qtgui/time_raster_sink_b.h b/gr-qtgui/include/qtgui/time_raster_sink_b.h new file mode 100644 index 0000000000..6d76c00dd8 --- /dev/null +++ b/gr-qtgui/include/qtgui/time_raster_sink_b.h @@ -0,0 +1,100 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_TIME_RASTER_SINK_B_H +#define INCLUDED_QTGUI_TIME_RASTER_SINK_B_H + +#include <Python.h> +#include <qtgui/api.h> +#include <gr_sync_block.h> +#include <qapplication.h> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display multiple signals on a + * time_raster plot. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink that takes in byte + * streams and plots a time_raster (spectrogram) plot. + * + * Input stream: This expects a bit stream (0, 1 in the LSB of a + * byte). It will display packed bytes but the display will have + * to be autoscaled. + */ + class QTGUI_API time_raster_sink_b : virtual public gr_sync_block + { + public: + // gr::qtgui::time_raster_sink_b::sptr + typedef boost::shared_ptr<time_raster_sink_b> sptr; + + /*! + * \brief Build a bit time raster sink. + * + * \param samp_rate sample rate of signal + * \param cols number of cols to plot + * \param rows number of rows to plot + * \param mult vector of floats as a scaling multiplier for each input stream + * \param offset vector of floats as an offset for each input stream + * \param name title for the plot + * \param nconnections number of streams connected + * \param parent a QWidget parent object, if any + */ + static sptr make(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_line_label(const std::string &lable) = 0; + virtual void set_line_color(const std::string &color) = 0; + virtual void set_line_width(int width) = 0; + virtual void set_line_style(Qt::PenStyle style) = 0; + virtual void set_line_marker(QwtSymbol::Style marker) = 0; + + virtual void set_size(int width, int height) = 0; + + virtual void set_num_rows(double rows) = 0; + virtual void set_num_cols(double cols) = 0; + + virtual void set_multiplier(const std::vector<float> &mult) = 0; + virtual void set_offset(const std::vector<float> &offset) = 0; + + virtual void set_intensity_range(float min, float max) = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_TIME_RASTER_SINK_B_H */ diff --git a/gr-qtgui/include/qtgui/time_raster_sink_f.h b/gr-qtgui/include/qtgui/time_raster_sink_f.h new file mode 100644 index 0000000000..23a8912869 --- /dev/null +++ b/gr-qtgui/include/qtgui/time_raster_sink_f.h @@ -0,0 +1,97 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_TIME_RASTER_SINK_F_H +#define INCLUDED_QTGUI_TIME_RASTER_SINK_F_H + +#include <Python.h> +#include <qtgui/api.h> +#include <gr_sync_block.h> +#include <qapplication.h> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display multiple signals on a + * time_raster plot. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink that takes set of a floating + * point streams and plots a time_raster (spectrogram) plot. + * + */ + class QTGUI_API time_raster_sink_f : virtual public gr_sync_block + { + public: + // gr::qtgui::time_raster_sink_f::sptr + typedef boost::shared_ptr<time_raster_sink_f> sptr; + + /*! + * \brief Build a floating point time raster sink. + * + * \param samp_rate sample rate of signal + * \param cols number of cols to plot + * \param rows number of rows to plot + * \param mult vector of floats as a scaling multiplier for each input stream + * \param offset vector of floats as an offset for each input stream + * \param name title for the plot + * \param nconnections number of streams connected + * \param parent a QWidget parent object, if any + */ + static sptr make(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_line_label(const std::string &lable) = 0; + virtual void set_line_color(const std::string &color) = 0; + virtual void set_line_width(int width) = 0; + virtual void set_line_style(Qt::PenStyle style) = 0; + virtual void set_line_marker(QwtSymbol::Style marker) = 0; + + virtual void set_size(int width, int height) = 0; + + virtual void set_num_rows(double rows) = 0; + virtual void set_num_cols(double cols) = 0; + + virtual void set_multiplier(const std::vector<float> &mult) = 0; + virtual void set_offset(const std::vector<float> &offset) = 0; + + virtual void set_intensity_range(float min, float max) = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_TIME_RASTER_SINK_F_H */ diff --git a/gr-qtgui/lib/CMakeLists.txt b/gr-qtgui/lib/CMakeLists.txt index 9f1b1d69bb..d7a517c013 100644 --- a/gr-qtgui/lib/CMakeLists.txt +++ b/gr-qtgui/lib/CMakeLists.txt @@ -24,6 +24,7 @@ set(qtgui_moc_hdrs spectrumdisplayform.h displayform.h timedisplayform.h + timerasterdisplayform.h freqdisplayform.h constellationdisplayform.h waterfalldisplayform.h @@ -31,6 +32,7 @@ set(qtgui_moc_hdrs DisplayPlot.h FrequencyDisplayPlot.h TimeDomainDisplayPlot.h + TimeRasterDisplayPlot.h WaterfallDisplayPlot.h ConstellationDisplayPlot.h ) @@ -50,22 +52,28 @@ set(qtgui_srcs DisplayPlot.cc FrequencyDisplayPlot.cc TimeDomainDisplayPlot.cc + TimeRasterDisplayPlot.cc + timeRasterGlobalData.cc WaterfallDisplayPlot.cc waterfallGlobalData.cc ConstellationDisplayPlot.cc spectrumdisplayform.cc displayform.cc timedisplayform.cc + timerasterdisplayform.cc freqdisplayform.cc constellationdisplayform.cc waterfalldisplayform.cc SpectrumGUIClass.cc spectrumUpdateEvents.cc plot_waterfall.cc + plot_raster.cc sink_c_impl.cc sink_f_impl.cc time_sink_c_impl.cc time_sink_f_impl.cc + time_raster_sink_b_impl.cc + time_raster_sink_f_impl.cc freq_sink_c_impl.cc freq_sink_f_impl.cc const_sink_c_impl.cc @@ -125,13 +133,17 @@ install(FILES DisplayPlot.h FrequencyDisplayPlot.h TimeDomainDisplayPlot.h + TimeRasterDisplayPlot.h + timeRasterGlobalData.h WaterfallDisplayPlot.h waterfallGlobalData.h ConstellationDisplayPlot.h plot_waterfall.h + plot_raster.h spectrumdisplayform.h displayform.h timedisplayform.h + timerasterdisplayform.h freqdisplayform.h constellationdisplayform.h waterfalldisplayform.h diff --git a/gr-qtgui/lib/TimeRasterDisplayPlot.cc b/gr-qtgui/lib/TimeRasterDisplayPlot.cc new file mode 100644 index 0000000000..84ec31f6ef --- /dev/null +++ b/gr-qtgui/lib/TimeRasterDisplayPlot.cc @@ -0,0 +1,503 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIMERASTER_DISPLAY_PLOT_C +#define TIMERASTER_DISPLAY_PLOT_C + +#include <TimeRasterDisplayPlot.h> + +#include "qtgui_types.h" +#include <qwt_color_map.h> +#include <qwt_scale_draw.h> +#include <qwt_legend.h> +#include <qwt_legend_item.h> +#include <qwt_plot_layout.h> +#include <QColor> +#include <iostream> + +#include <boost/date_time/posix_time/posix_time.hpp> +namespace pt = boost::posix_time; + +#include <QDebug> + +/*********************************************************************** + * Text scale widget to provide X (time) axis text + **********************************************************************/ +class QwtXScaleDraw: public QwtScaleDraw, public TimeScaleData +{ +public: + QwtXScaleDraw():QwtScaleDraw(),TimeScaleData() { } + + virtual ~QwtXScaleDraw() { } + + virtual QwtText label(double value) const + { + double secs = double(value * getSecondsPerLine()); + return QwtText(QString("").sprintf("%.2f", secs)); + } + + virtual void initiateUpdate() + { + // Do this in one call rather than when zeroTime and secondsPerLine + // updates is to prevent the display from being updated too often... + invalidateCache(); + } +}; + +/*********************************************************************** + * Text scale widget to provide Y axis text + **********************************************************************/ +class QwtYScaleDraw: public QwtScaleDraw +{ +public: + QwtYScaleDraw(): QwtScaleDraw(), _rows(0) { } + + virtual ~QwtYScaleDraw() { } + + virtual QwtText label(double value) const + { + if(_rows > 0) + value = _rows - value; + return QwtText(QString("").sprintf("%.0f", value)); + } + + virtual void initiateUpdate() + { + // Do this in one call rather than when zeroTime and secondsPerLine + // updates is to prevent the display from being updated too often... + invalidateCache(); + } + + void setRows(double rows) { rows>0 ? _rows = rows : _rows = 0; } + +private: + double _rows; +}; + + +/*********************************************************************** + * Widget to provide mouse pointer coordinate text + **********************************************************************/ +class TimeRasterZoomer: public QwtPlotZoomer, public TimeScaleData +{ +public: + TimeRasterZoomer(QwtPlotCanvas* canvas, double rows, double cols) + : QwtPlotZoomer(canvas), TimeScaleData(), + d_rows(static_cast<double>(rows)), d_cols(static_cast<double>(cols)) + { + setTrackerMode(QwtPicker::AlwaysOn); + } + + virtual ~TimeRasterZoomer() + { + } + + virtual void updateTrackerText() + { + updateDisplay(); + } + + void setUnitType(const std::string &type) + { + _unitType = type; + } + + void setColumns(const double cols) + { + d_cols = cols; + } + + void setRows(const double rows) + { + d_rows = rows; + } + +protected: + using QwtPlotZoomer::trackerText; + virtual QwtText trackerText( QPoint const &p ) const + { + QwtDoublePoint dp = QwtPlotZoomer::invTransform(p); + double x = dp.x() * getSecondsPerLine(); + //double y = dp.y() * getSecondsPerLine() * d_cols; + double y = floor(d_rows - dp.y()); + QwtText t(QString("%1 s, %2") + .arg(x, 0, 'f', 2) + .arg(y, 0, 'f', 0)); + return t; + } + +private: + std::string _unitType; + double d_rows, d_cols; +}; + +/********************************************************************* +* Main time raster plot widget +*********************************************************************/ +TimeRasterDisplayPlot::TimeRasterDisplayPlot(int nplots, + double samp_rate, + double rows, double cols, + QWidget* parent) + : DisplayPlot(nplots, parent) +{ + _zoomer = NULL; // need this for proper init + + resize(parent->width(), parent->height()); + + d_samp_rate = samp_rate; + d_cols = cols; + d_rows = rows; + _numPoints = d_cols; + + setAxisScaleDraw(QwtPlot::xBottom, new QwtXScaleDraw()); + setAxisScaleDraw(QwtPlot::yLeft, new QwtYScaleDraw()); + + double sec_per_samp = 1.0/d_samp_rate; + QwtYScaleDraw* yScale = (QwtYScaleDraw*)axisScaleDraw(QwtPlot::yLeft); + yScale->setRows(d_rows); + + QwtXScaleDraw* xScale = (QwtXScaleDraw*)axisScaleDraw(QwtPlot::xBottom); + xScale->setSecondsPerLine(sec_per_samp); + + for(int i = 0; i < _nplots; i++) { + d_data.push_back(new TimeRasterData(d_rows, d_cols)); + d_raster.push_back(new PlotTimeRaster("Raster")); + d_raster[i]->setData(d_data[i]); + + // a hack around the fact that we aren't using plot curves for the + // raster plots. + _plot_curve.push_back(new QwtPlotCurve(QString("Data"))); + + d_raster[i]->attach(this); + + d_color_map_type.push_back(INTENSITY_COLOR_MAP_TYPE_BLACK_HOT); + setAlpha(i, 255/_nplots); + } + + // Set bottom plot with no transparency as a base + setAlpha(0, 255); + + // LeftButton for the zooming + // MidButton for the panning + // RightButton: zoom out by 1 + // Ctrl+RighButton: zoom out to full size + _zoomer = new TimeRasterZoomer(canvas(), d_rows, d_cols); +#if QWT_VERSION < 0x060000 + _zoomer->setSelectionFlags(QwtPicker::RectSelection | QwtPicker::DragSelection); +#endif + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); + _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, + Qt::RightButton); + + const QColor c(Qt::red); + _zoomer->setRubberBandPen(c); + _zoomer->setTrackerPen(c); + + // Set intensity color now (needed _zoomer before we could do this). + // We've made sure the old type is different than here so we'll + // force and update. + for(int i = 0; i < _nplots; i++) { + setIntensityColorMapType(i, INTENSITY_COLOR_MAP_TYPE_WHITE_HOT, + QColor("white"), QColor("white")); + } + + _updateIntensityRangeDisplay(); + + reset(); +} + +TimeRasterDisplayPlot::~TimeRasterDisplayPlot() +{ +} + +void +TimeRasterDisplayPlot::reset() +{ + for(int i = 0; i < _nplots; i++) { + d_data[i]->resizeData(d_rows, d_cols); + d_data[i]->reset(); + } + + setAxisScale(QwtPlot::xBottom, 0, d_rows); + setAxisScale(QwtPlot::yLeft, 0, d_cols); + + double sec_per_samp = 1.0/d_samp_rate; + QwtYScaleDraw* yScale = (QwtYScaleDraw*)axisScaleDraw(QwtPlot::yLeft); + yScale->setRows(d_rows); + + QwtXScaleDraw* xScale = (QwtXScaleDraw*)axisScaleDraw(QwtPlot::xBottom); + xScale->setSecondsPerLine(sec_per_samp); + + // Load up the new base zoom settings + if(_zoomer) { + ((TimeRasterZoomer*)_zoomer)->setColumns(d_cols); + ((TimeRasterZoomer*)_zoomer)->setRows(d_rows); + ((TimeRasterZoomer*)_zoomer)->setSecondsPerLine(sec_per_samp); + //((TimeRasterZoomer*)_zoomer)-sSetUnitType(strunits); + + QwtDoubleRect newSize = _zoomer->zoomBase(); + newSize.setLeft(0); + newSize.setWidth(d_cols); + newSize.setBottom(0); + newSize.setHeight(d_rows); + + _zoomer->zoom(newSize); + _zoomer->setZoomBase(newSize); + _zoomer->zoom(0); + } +} + +void +TimeRasterDisplayPlot::setNumRows(double rows) +{ + d_rows = rows; + reset(); +} + +void +TimeRasterDisplayPlot::setNumCols(double cols) +{ + d_cols = cols; + reset(); +} + +void +TimeRasterDisplayPlot::setAlpha(int which, int alpha) +{ + d_raster[which]->setAlpha(alpha); +} + +double +TimeRasterDisplayPlot::numRows() const +{ + return d_rows; +} + +double +TimeRasterDisplayPlot::numCols() const +{ + return d_cols; +} + +void +TimeRasterDisplayPlot::setPlotDimensions(const double rows, const double cols, + const double units, const std::string &strunits) +{ + bool rst = false; + if((rows != d_rows) || (cols != d_cols)) + rst = true; + + d_rows = rows; + d_cols = cols; + + if((axisScaleDraw(QwtPlot::xBottom) != NULL) && (_zoomer != NULL)) { + if(rst) { + reset(); + } + } +} + +void +TimeRasterDisplayPlot::plotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints) +{ + if(!_stop) { + if(numDataPoints > 0) { + for(int i = 0; i < _nplots; i++) { + d_data[i]->addData(dataPoints[i], numDataPoints); + d_raster[i]->invalidateCache(); + d_raster[i]->itemChanged(); + } + + replot(); + } + } +} + +void +TimeRasterDisplayPlot::plotNewData(const double* dataPoints, + const int64_t numDataPoints) +{ + std::vector<double*> vecDataPoints; + vecDataPoints.push_back((double*)dataPoints); + plotNewData(vecDataPoints, numDataPoints); +} + +void +TimeRasterDisplayPlot::setIntensityRange(const double minIntensity, + const double maxIntensity) +{ + for(int i = 0; i < _nplots; i++) { +#if QWT_VERSION < 0x060000 + d_data[i]->setRange(QwtDoubleInterval(minIntensity, maxIntensity)); +#else + d_data[i]->setInterval(Qt::ZAxis, QwtInterval(minIntensity, maxIntensity)); +#endif + + emit updatedLowerIntensityLevel(minIntensity); + emit updatedUpperIntensityLevel(maxIntensity); + + _updateIntensityRangeDisplay(); + } +} + +void +TimeRasterDisplayPlot::replot() +{ + // Update the x-axis display + if(axisWidget(QwtPlot::yLeft) != NULL) { + axisWidget(QwtPlot::yLeft)->update(); + } + + // Update the y-axis display + if(axisWidget(QwtPlot::xBottom) != NULL) { + axisWidget(QwtPlot::xBottom)->update(); + } + + if(_zoomer != NULL) { + ((TimeRasterZoomer*)_zoomer)->updateTrackerText(); + } + + QwtPlot::replot(); +} + +int +TimeRasterDisplayPlot::getIntensityColorMapType(int which) const +{ + if(which >= (int)d_color_map_type.size()) + throw std::runtime_error("TimerasterDisplayPlot::GetIntesityColorMap: invalid which.\n"); + + return d_color_map_type[which]; +} + +void +TimeRasterDisplayPlot::setIntensityColorMapType(const int which, + const int newType, + const QColor lowColor, + const QColor highColor) +{ + if(which >= (int)d_color_map_type.size()) + throw std::runtime_error("TimerasterDisplayPlot::setIntesityColorMap: invalid which.\n"); + + if((d_color_map_type[which] != newType) || + ((newType == INTENSITY_COLOR_MAP_TYPE_USER_DEFINED) && + (lowColor.isValid() && highColor.isValid()))) { + switch(newType) { + case INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR: { + d_color_map_type[which] = newType; + + d_raster[which]->setColorMap(new ColorMap_MultiColor()); + if(_zoomer) + _zoomer->setTrackerPen(QColor(Qt::black)); + break; + } + case INTENSITY_COLOR_MAP_TYPE_WHITE_HOT: { + d_color_map_type[which] = newType; + d_raster[which]->setColorMap(new ColorMap_WhiteHot()); + break; + } + case INTENSITY_COLOR_MAP_TYPE_BLACK_HOT: { + d_color_map_type[which] = newType; + d_raster[which]->setColorMap(new ColorMap_BlackHot()); + break; + } + case INTENSITY_COLOR_MAP_TYPE_INCANDESCENT: { + d_color_map_type[which] = newType; + d_raster[which]->setColorMap(new ColorMap_Incandescent()); + break; + } + case INTENSITY_COLOR_MAP_TYPE_USER_DEFINED: { + d_low_intensity = lowColor; + d_high_intensity = highColor; + d_color_map_type[which] = newType; + d_raster[which]->setColorMap(new ColorMap_UserDefined(lowColor, highColor)); + break; + } + default: break; + } + + _updateIntensityRangeDisplay(); + } +} + +const QColor +TimeRasterDisplayPlot::getUserDefinedLowIntensityColor() const +{ + return d_low_intensity; +} + +const QColor +TimeRasterDisplayPlot::getUserDefinedHighIntensityColor() const +{ + return d_high_intensity; +} + +void +TimeRasterDisplayPlot::_updateIntensityRangeDisplay() +{ + QwtScaleWidget *rightAxis = axisWidget(QwtPlot::yRight); + rightAxis->setTitle("Intensity"); + rightAxis->setColorBarEnabled(true); + + for(int i = 0; i < _nplots; i++) { +#if QWT_VERSION < 0x060000 + rightAxis->setColorMap(d_raster[i]->data()->range(), + d_raster[i]->colorMap()); + setAxisScale(QwtPlot::yRight, + d_raster[i]->data()->range().minValue(), + d_raster[i]->data()->range().maxValue()); +#else + QwtInterval intv = d_raster[i]->interval(Qt::ZAxis); + switch(d_color_map_type[i]) { + case INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR: + rightAxis->setColorMap(intv, new ColorMap_MultiColor()); break; + case INTENSITY_COLOR_MAP_TYPE_WHITE_HOT: + rightAxis->setColorMap(intv, new ColorMap_WhiteHot()); break; + case INTENSITY_COLOR_MAP_TYPE_BLACK_HOT: + rightAxis->setColorMap(intv, new ColorMap_BlackHot()); break; + case INTENSITY_COLOR_MAP_TYPE_INCANDESCENT: + rightAxis->setColorMap(intv, new ColorMap_Incandescent()); break; + case INTENSITY_COLOR_MAP_TYPE_USER_DEFINED: + rightAxis->setColorMap(intv, new ColorMap_UserDefined(d_low_intensity, + d_high_intensity)); + break; + default: + rightAxis->setColorMap(intv, new ColorMap_MultiColor()); break; + } + setAxisScale(QwtPlot::yRight, intv.minValue(), intv.maxValue()); +#endif + + enableAxis(QwtPlot::yRight); + + plotLayout()->setAlignCanvasToScales(true); + + // Tell the display to redraw everything + d_raster[i]->invalidateCache(); + d_raster[i]->itemChanged(); + } + + // Draw again + replot(); +} + +#endif /* TIMERASTER_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/lib/TimeRasterDisplayPlot.h b/gr-qtgui/lib/TimeRasterDisplayPlot.h new file mode 100644 index 0000000000..3e0ac1cfdb --- /dev/null +++ b/gr-qtgui/lib/TimeRasterDisplayPlot.h @@ -0,0 +1,98 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIMERASTER_DISPLAY_PLOT_H +#define TIMERASTER_DISPLAY_PLOT_H + +#include <stdint.h> +#include <cstdio> +#include <vector> +#include <qwt_plot_rasteritem.h> +#include "DisplayPlot.h" +#include "timeRasterGlobalData.h" +#include "plot_raster.h" +#include <gruel/high_res_timer.h> + +#if QWT_VERSION < 0x060000 +#include <plot_waterfall.h> +#else +#include <qwt_compat.h> +#endif + +class TimeRasterDisplayPlot: public DisplayPlot +{ + Q_OBJECT + +public: + TimeRasterDisplayPlot(int nplots, + double samp_rate, + double rows, double cols, + QWidget*); + virtual ~TimeRasterDisplayPlot(); + + void reset(); + + void setNumRows(double rows); + void setNumCols(double cols); + void setAlpha(int which, int alpha); + + double numRows() const; + double numCols() const; + + void setPlotDimensions(const double rows, const double cols, + const double units, const std::string &strunits); + + void plotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints); + + void plotNewData(const double* dataPoints, + const int64_t numDataPoints); + + void setIntensityRange(const double minIntensity, + const double maxIntensity); + + void replot(void); + + int getIntensityColorMapType(int) const; + void setIntensityColorMapType(const int, const int, const QColor, const QColor); + const QColor getUserDefinedLowIntensityColor() const; + const QColor getUserDefinedHighIntensityColor() const; + +signals: + void updatedLowerIntensityLevel(const double); + void updatedUpperIntensityLevel(const double); + +private: + void _updateIntensityRangeDisplay(); + + std::vector<TimeRasterData*> d_data; + std::vector<PlotTimeRaster*> d_raster; + + double d_samp_rate; + double d_rows, d_cols; + + std::vector<int> d_color_map_type; + QColor d_low_intensity; + QColor d_high_intensity; +}; + +#endif /* TIMERASTER_DISPLAY_PLOT_H */ diff --git a/gr-qtgui/lib/plot_raster.cc b/gr-qtgui/lib/plot_raster.cc new file mode 100644 index 0000000000..688ce6597e --- /dev/null +++ b/gr-qtgui/lib/plot_raster.cc @@ -0,0 +1,334 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <iostream> +#include <qimage.h> +#include <qpen.h> +#include <qpainter.h> +#include "qwt_painter.h" +#include "qwt_scale_map.h" +#include "qwt_color_map.h" +#include "plot_raster.h" + +#if QWT_VERSION < 0x060000 +#include "qwt_double_interval.h" +#endif + +typedef QVector<QRgb> QwtColorTable; + +class PlotTimeRasterImage: public QImage +{ + // This class hides some Qt3/Qt4 API differences +public: + PlotTimeRasterImage(const QSize &size, QwtColorMap::Format format): + QImage(size, format == QwtColorMap::RGB + ? QImage::Format_ARGB32 : QImage::Format_Indexed8 ) + { + } + + PlotTimeRasterImage(const QImage &other): + QImage(other) + { + } + + void initColorTable(const QImage& other) + { + setColorTable(other.colorTable()); + } +}; + +class PlotTimeRaster::PrivateData +{ +public: + PrivateData() + { + data = NULL; + colorMap = new QwtLinearColorMap(); + } + + ~PrivateData() + { + delete colorMap; + } + + TimeRasterData *data; + QwtColorMap *colorMap; +}; + +/*! + Sets the following item attributes: + - QwtPlotItem::AutoScale: true + - QwtPlotItem::Legend: false + + The z value is initialized to 8.0. + + \param title Title + + \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ() +*/ +PlotTimeRaster::PlotTimeRaster(const QString &title) + : QwtPlotRasterItem(title) +{ + d_data = new PrivateData(); + + setItemAttribute(QwtPlotItem::AutoScale, true); + setItemAttribute(QwtPlotItem::Legend, false); + + setZ(1.0); +} + +//! Destructor +PlotTimeRaster::~PlotTimeRaster() +{ + delete d_data; +} + +const TimeRasterData* +PlotTimeRaster::data()const +{ + return d_data->data; +} + +void +PlotTimeRaster::setData(TimeRasterData *data) +{ + d_data->data = data; +} + +//! \return QwtPlotItem::Rtti_PlotSpectrogram +int +PlotTimeRaster::rtti() const +{ + return QwtPlotItem::Rtti_PlotGrid; +} + +/*! + Change the color map + + Often it is useful to display the mapping between intensities and + colors as an additional plot axis, showing a color bar. + + \param map Color Map + + \sa colorMap(), QwtScaleWidget::setColorBarEnabled(), + QwtScaleWidget::setColorMap() +*/ +void +PlotTimeRaster::setColorMap(const QwtColorMap *map) +{ + delete d_data->colorMap; + d_data->colorMap = (QwtColorMap*)map; + + invalidateCache(); + itemChanged(); +} + +/*! + \return Color Map used for mapping the intensity values to colors + \sa setColorMap() +*/ +const QwtColorMap &PlotTimeRaster::colorMap() const +{ + return *d_data->colorMap; +} + +/*! + \return Bounding rect of the data + \sa QwtRasterData::boundingRect +*/ +#if QWT_VERSION < 0x060000 +QwtDoubleRect PlotTimeRaster::boundingRect() const +{ + return d_data->data->boundingRect(); +} +#endif + +/*! + \brief Returns the recommended raster for a given rect. + + F.e the raster hint is used to limit the resolution of + the image that is rendered. + + \param rect Rect for the raster hint + \return data().rasterHint(rect) +*/ +#if QWT_VERSION < 0x060000 +QSize PlotTimeRaster::rasterHint(const QwtDoubleRect &rect) const +{ + return d_data->data->rasterHint(rect); +} +#endif + +/*! + \brief Render an image from the data and color map. + + The area is translated into a rect of the paint device. + For each pixel of this rect the intensity is mapped + into a color. + + \param xMap X-Scale Map + \param yMap Y-Scale Map + \param area Area that should be rendered in scale coordinates. + + \return A QImage::Format_Indexed8 or QImage::Format_ARGB32 depending + on the color map. + + \sa QwtRasterData::intensity(), QwtColorMap::rgb(), + QwtColorMap::colorIndex() +*/ +#if QWT_VERSION < 0x060000 +QImage PlotTimeRaster::renderImage(const QwtScaleMap &xMap, + const QwtScaleMap &yMap, + const QwtDoubleRect &area) const +#else +QImage PlotTimeRaster::renderImage(const QwtScaleMap &xMap, + const QwtScaleMap &yMap, + const QRectF &area, + const QSize &size) const +#endif +{ + if(area.isEmpty()) + return QImage(); + +#if QWT_VERSION < 0x060000 + QRect rect = transform(xMap, yMap, area); + const QSize res = d_data->data->rasterHint(area); +#else + QRect rect = QwtScaleMap::transform(xMap, yMap, area).toRect(); + const QSize res(-1,-1); + +#endif + + QwtScaleMap xxMap = xMap; + QwtScaleMap yyMap = yMap; + + if(res.isValid()) { + /* + It is useless to render an image with a higher resolution + than the data offers. Of course someone will have to + scale this image later into the size of the given rect, but f.e. + in case of postscript this will done on the printer. + */ + rect.setSize(rect.size().boundedTo(res)); + + int px1 = rect.x(); + int px2 = rect.x() + rect.width(); + if(xMap.p1() > xMap.p2()) + qSwap(px1, px2); + + double sx1 = area.x(); + double sx2 = area.x() + area.width(); + if(xMap.s1() > xMap.s2()) + qSwap(sx1, sx2); + + int py1 = rect.y(); + int py2 = rect.y() + rect.height(); + if(yMap.p1() > yMap.p2()) + qSwap(py1, py2); + + double sy1 = area.y(); + double sy2 = area.y() + area.height(); + if(yMap.s1() > yMap.s2()) + qSwap(sy1, sy2); + + xxMap.setPaintInterval(px1, px2); + xxMap.setScaleInterval(sx1, sx2); + yyMap.setPaintInterval(py1, py2); + yyMap.setScaleInterval(sy1, sy2); + } + + PlotTimeRasterImage image(rect.size(), d_data->colorMap->format()); + +#if QWT_VERSION < 0x060000 + const QwtDoubleInterval intensityRange = d_data->data->range(); +#else + const QwtInterval intensityRange = d_data->data->interval(Qt::ZAxis); +#endif + if(!intensityRange.isValid()) + return image; + + d_data->data->initRaster(area, rect.size()); + + if(d_data->colorMap->format() == QwtColorMap::RGB) { + for(int y = rect.top(); y <= rect.bottom(); y++) { + const double ty = yyMap.invTransform(y); + + QRgb *line = (QRgb *)image.scanLine(y - rect.top()); + + for(int x = rect.left(); x <= rect.right(); x++) { + const double tx = xxMap.invTransform(x); + + *line++ = d_data->colorMap->rgb(intensityRange, + d_data->data->value(tx, ty)); + } + } + d_data->data->incrementResidual(); + } + else if(d_data->colorMap->format() == QwtColorMap::Indexed) { + image.setColorTable(d_data->colorMap->colorTable(intensityRange)); + + for(int y = rect.top(); y <= rect.bottom(); y++) { + const double ty = yyMap.invTransform(y); + + unsigned char *line = image.scanLine(y - rect.top()); + for(int x = rect.left(); x <= rect.right(); x++) { + const double tx = xxMap.invTransform(x); + + *line++ = d_data->colorMap->colorIndex(intensityRange, + d_data->data->value(tx, ty)); + } + } + } + + d_data->data->discardRaster(); + + // Mirror the image in case of inverted maps + + const bool hInvert = xxMap.p1() > xxMap.p2(); + const bool vInvert = yyMap.p1() > yyMap.p2(); + if(hInvert || vInvert) { + image = image.mirrored(hInvert, vInvert); + } + + return image; +} + +/*! + \brief Draw the raster + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas in painter coordinates + + \sa setDisplayMode, renderImage, + QwtPlotRasterItem::draw, drawContourLines +*/ + +void PlotTimeRaster::draw(QPainter *painter, + const QwtScaleMap &xMap, + const QwtScaleMap &yMap, + const QRect &canvasRect) const +{ + QwtPlotRasterItem::draw(painter, xMap, yMap, canvasRect); +} + diff --git a/gr-qtgui/lib/plot_raster.h b/gr-qtgui/lib/plot_raster.h new file mode 100644 index 0000000000..40763e4b00 --- /dev/null +++ b/gr-qtgui/lib/plot_raster.h @@ -0,0 +1,90 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef PLOT_TIMERASTER_H +#define PLOT_TIMERASTER_H + +#include <qglobal.h> +#include <timeRasterGlobalData.h> +#include <qwt_plot_rasteritem.h> + +#if QWT_VERSION >= 0x060000 +#include <qwt_point_3d.h> // doesn't seem necessary, but is... +#include <qwt_compat.h> +#endif + +class QwtColorMap; + +/*! + \brief A plot item, which displays a time raster. + + A time raster displays threedimenional data, where the 3rd dimension + ( the intensity ) is displayed using colors. The colors are calculated + from the values using a color map. + + \sa QwtRasterData, QwtColorMap +*/ + +class PlotTimeRaster: public QwtPlotRasterItem +{ +public: + explicit PlotTimeRaster(const QString &title = QString::null); + virtual ~PlotTimeRaster(); + + const TimeRasterData* data()const; + + void setData(TimeRasterData *data); + + void setColorMap(const QwtColorMap *map); + + const QwtColorMap &colorMap() const; + +#if QWT_VERSION < 0x060000 + virtual QwtDoubleRect boundingRect() const; + virtual QSize rasterHint(const QwtDoubleRect &) const; +#endif + + virtual int rtti() const; + + virtual void draw(QPainter *p, + const QwtScaleMap &xMap, + const QwtScaleMap &yMap, + const QRect &rect) const; + +protected: +#if QWT_VERSION < 0x060000 + QImage renderImage(const QwtScaleMap &xMap, + const QwtScaleMap &yMap, + const QwtDoubleRect &rect) const; +#else + QImage renderImage(const QwtScaleMap &xMap, + const QwtScaleMap &yMap, + const QRectF &rect, + const QSize &size=QSize(0,0)) const; +#endif + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/gr-qtgui/lib/spectrumUpdateEvents.cc b/gr-qtgui/lib/spectrumUpdateEvents.cc index 92dd807147..5e03de685b 100644 --- a/gr-qtgui/lib/spectrumUpdateEvents.cc +++ b/gr-qtgui/lib/spectrumUpdateEvents.cc @@ -373,4 +373,48 @@ WaterfallUpdateEvent::getDataTimestamp() const return _dataTimestamp; } + +/***************************************************************************/ + + +TimeRasterUpdateEvent::TimeRasterUpdateEvent(const std::vector<double*> dataPoints, + const uint64_t numDataPoints) + : QEvent(QEvent::Type(SpectrumUpdateEventType)) +{ + if(numDataPoints < 1) { + _numDataPoints = 1; + } + else { + _numDataPoints = numDataPoints; + } + + _nplots = dataPoints.size(); + for(size_t i = 0; i < _nplots; i++) { + _dataPoints.push_back(new double[_numDataPoints]); + if(numDataPoints > 0) { + memcpy(_dataPoints[i], dataPoints[i], + _numDataPoints*sizeof(double)); + } + } +} + +TimeRasterUpdateEvent::~TimeRasterUpdateEvent() +{ + for(size_t i = 0; i < _nplots; i++) { + delete[] _dataPoints[i]; + } +} + +const std::vector<double*> +TimeRasterUpdateEvent::getPoints() const +{ + return _dataPoints; +} + +uint64_t +TimeRasterUpdateEvent::getNumDataPoints() const +{ + return _numDataPoints; +} + #endif /* SPECTRUM_UPDATE_EVENTS_C */ diff --git a/gr-qtgui/lib/spectrumUpdateEvents.h b/gr-qtgui/lib/spectrumUpdateEvents.h index e663980bc6..78cc275178 100644 --- a/gr-qtgui/lib/spectrumUpdateEvents.h +++ b/gr-qtgui/lib/spectrumUpdateEvents.h @@ -212,4 +212,31 @@ private: }; +/********************************************************************/ + + +class TimeRasterUpdateEvent: public QEvent +{ +public: + TimeRasterUpdateEvent(const std::vector<double*> dataPoints, + const uint64_t numDataPoints); + ~TimeRasterUpdateEvent(); + + int which() const; + const std::vector<double*> getPoints() const; + uint64_t getNumDataPoints() const; + bool getRepeatDataFlag() const; + + static QEvent::Type Type() + { return QEvent::Type(SpectrumUpdateEventType); } + +protected: + +private: + size_t _nplots; + std::vector<double*> _dataPoints; + uint64_t _numDataPoints; +}; + + #endif /* SPECTRUM_UPDATE_EVENTS_H */ diff --git a/gr-qtgui/lib/timeRasterGlobalData.cc b/gr-qtgui/lib/timeRasterGlobalData.cc new file mode 100644 index 0000000000..27cb41d403 --- /dev/null +++ b/gr-qtgui/lib/timeRasterGlobalData.cc @@ -0,0 +1,252 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIMERASTER_GLOBAL_DATA_CPP +#define TIMERASTER_GLOBAL_DATA_CPP + +#include <timeRasterGlobalData.h> +#include <cstdio> +#include <cmath> +#include <iostream> + +TimeRasterData::TimeRasterData(const double rows, const double cols) +#if QWT_VERSION < 0x060000 + : QwtRasterData(QwtDoubleRect(0, 0, rows, cols)) +#else + : QwtRasterData() +#endif +{ + d_nitems = 0; + d_rows = rows; + d_cols = cols; + d_resid = 0; + + // We add 1 here so we always have the next row already started + // (helps when d_cols is fractional and we have to slide). + d_totalitems = static_cast<int>((d_rows+1)*floor(d_cols)); + d_data_size = d_totalitems + static_cast<int>(floor(d_cols)); + + d_intensityRange = QwtDoubleInterval(0.0, 10.0); + d_data = new double[d_data_size]; + +#if QWT_VERSION >= 0x060000 + setInterval(Qt::XAxis, QwtInterval(0, cols)); + setInterval(Qt::YAxis, QwtInterval(0, rows)); + setInterval(Qt::ZAxis, QwtInterval(0.0, 10.0)); +#endif + + reset(); +} + +TimeRasterData::~TimeRasterData() +{ + delete [] d_data; +} + +void TimeRasterData::reset() +{ + d_resid = 0; + memset(d_data, 0x0, d_totalitems*sizeof(double)); +} + +void TimeRasterData::copy(const TimeRasterData* rhs) +{ +#if QWT_VERSION < 0x060000 + if((d_cols != rhs->getNumCols()) || + (boundingRect() != rhs->boundingRect()) ){ + d_cols = rhs->getNumCols(); + d_rows = rhs->getNumRows(); + d_totalitems = static_cast<int>((d_rows+1)*floor(d_cols)); + d_data_size = d_totalitems + static_cast<int>(floor(d_cols)); + setBoundingRect(rhs->boundingRect()); + delete [] d_data; + _data = new double[d_data_size]; + } +#else + if((d_cols != rhs->getNumCols()) || (d_rows != rhs->getNumRows())) { + d_cols = rhs->getNumCols(); + d_rows = rhs->getNumRows(); + d_totalitems = static_cast<int>((d_rows+1)*floor(d_cols)); + d_data_size = d_totalitems + static_cast<int>(floor(d_cols)); + delete [] d_data; + d_data = new double[d_data_size]; + } +#endif + + reset(); + setDataBuffer(rhs->getDataBuffer()); + +#if QWT_VERSION < 0x060000 + setRange(rhs->range()); +#else + setInterval(Qt::XAxis, rhs->interval(Qt::XAxis)); + setInterval(Qt::YAxis, rhs->interval(Qt::YAxis)); + setInterval(Qt::ZAxis, rhs->interval(Qt::ZAxis)); +#endif +} + +void TimeRasterData::resizeData(const double rows, const double cols) +{ +#if QWT_VERSION < 0x060000 + if((cols != getNumCols()) || (boundingRect().width() != cols) || + (rows != getNumRows()) || (boundingRect().height() != cols)) { + setBoundingRect(QwtDoubleRect(0, 0, cols, rows)); + d_cols = cols; + d_rows = rows; + d_totalitems = static_cast<int>((d_rows+1)*floor(d_cols)); + d_data_size = d_totalitems + static_cast<int>(floor(d_cols)); + delete [] d_data; + d_data = new double[d_data_size]; + } + +#else + if((cols != getNumCols()) || (interval(Qt::XAxis).width() != cols) || + (rows != getNumRows()) || (interval(Qt::YAxis).width() != rows)) { + + setInterval(Qt::XAxis, QwtInterval(0, cols)); + setInterval(Qt::YAxis, QwtInterval(0, rows)); + + d_cols = cols; + d_rows = rows; + d_totalitems = static_cast<int>((d_rows+1)*floor(d_cols)); + d_data_size = d_totalitems + static_cast<int>(floor(d_cols)); + + delete [] d_data; + d_data = new double[d_data_size]; + } +#endif + + reset(); +} + +QwtRasterData *TimeRasterData::copy() const +{ +#if QWT_VERSION < 0x060000 + TimeRasterData* returnData = \ + new TimeRasterData(d_cols, d_rows); +#else + TimeRasterData* returnData = \ + new TimeRasterData(d_cols, d_rows); +#endif + + returnData->copy(this); + return returnData; +} + + +#if QWT_VERSION < 0x060000 +QwtDoubleInterval TimeRasterData::range() const +{ + return d_intensityRange; +} + +void TimeRasterData::setRange(const QwtDoubleInterval& newRange) +{ + d_intensityRange = newRange; +} + +#endif + + +double +TimeRasterData::value(double x, double y) const +{ + double returnValue = 0.0; + +#if QWT_VERSION < 0x060000 + double top = boundingRect().top(); + double bottom = top - boundingRect().height(); + double left = boundingRect().left(); + double right = left - boundingRect().width(); +#else + double top = interval(Qt::YAxis).maxValue(); + double bottom = interval(Qt::YAxis).minValue(); + double left = interval(Qt::XAxis).minValue(); + double right = interval(Qt::XAxis).maxValue(); +#endif + + if((x < left) || (x > right) || (y < bottom) || (y > top)) + return 0.0; + + double _y = floor(top - y); + double _loc = _y*(d_cols) + x + d_resid; + int location = static_cast<int>(_loc); + + if((location > -1) && (location < d_data_size)) { + returnValue = d_data[location]; + } + + return returnValue; +} + +void +TimeRasterData::incrementResidual() +{ + // After a full set of rows are drawn, we want to add up the + // residual due to any fractional value of d_cols to appropriately + // shift the next data in. + double icols = floor(d_cols); + d_resid = fmod(d_resid + (d_cols - icols) * d_rows, icols); +} + +double +TimeRasterData::getNumCols() const +{ + return d_cols; +} + +double +TimeRasterData::getNumRows() const +{ + return d_rows; +} + +void +TimeRasterData::addData(const double* data, + const int dataSize) +{ + int cols = static_cast<int>(d_cols); + if(dataSize == cols) { + if(d_nitems < d_totalitems) { + memcpy(&d_data[d_nitems], data, cols*sizeof(double)); + d_nitems += cols; + } + else { + memmove(d_data, &d_data[cols], d_totalitems*sizeof(double)); + memcpy(&d_data[d_nitems], data, cols*sizeof(double)); + } + } +} + +double* +TimeRasterData::getDataBuffer() const +{ + return d_data; +} + +void +TimeRasterData::setDataBuffer(const double* newData) +{ + memcpy(d_data, newData, d_totalitems*sizeof(double)); +} + +#endif /* TIMERASTER_GLOBAL_DATA_CPP */ diff --git a/gr-qtgui/lib/timeRasterGlobalData.h b/gr-qtgui/lib/timeRasterGlobalData.h new file mode 100644 index 0000000000..7b450c033b --- /dev/null +++ b/gr-qtgui/lib/timeRasterGlobalData.h @@ -0,0 +1,81 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIMERASTER_GLOBAL_DATA_HPP +#define TIMERASTER_GLOBAL_DATA_HPP + +#include <qwt_raster_data.h> +#include <inttypes.h> + +#if QWT_VERSION >= 0x060000 +#include <qwt_point_3d.h> // doesn't seem necessary, but is... +#include <qwt_compat.h> +#endif + +class TimeRasterData: public QwtRasterData +{ +public: + TimeRasterData(const double rows, const double cols); + virtual ~TimeRasterData(); + + virtual void reset(); + virtual void copy(const TimeRasterData*); + + virtual void resizeData(const double rows, const double cols); + + virtual QwtRasterData *copy() const; + +#if QWT_VERSION < 0x060000 + virtual QwtDoubleInterval range() const; + virtual void setRange(const QwtDoubleInterval&); +#endif + + virtual double value(double x, double y) const; + + virtual double getNumCols()const; + virtual double getNumRows()const; + + virtual void addData(const double*, const int); + + virtual double* getDataBuffer()const; + virtual void setDataBuffer(const double*); + + void incrementResidual(); + +protected: + + double* d_data; + double d_rows, d_cols; + double d_resid; + int d_nitems, d_totalitems, d_data_size; + +#if QWT_VERSION < 0x060000 + QwtDoubleInterval d_intensityRange; +#else + QwtInterval d_intensityRange; +#endif + +private: + +}; + +#endif /* TIMERASTER_GLOBAL_DATA_HPP */ diff --git a/gr-qtgui/lib/time_raster_sink_b_impl.cc b/gr-qtgui/lib/time_raster_sink_b_impl.cc new file mode 100644 index 0000000000..46d60ba3f5 --- /dev/null +++ b/gr-qtgui/lib/time_raster_sink_b_impl.cc @@ -0,0 +1,332 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "time_raster_sink_b_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> + +namespace gr { + namespace qtgui { + + time_raster_sink_b::sptr + time_raster_sink_b::make(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new time_raster_sink_b_impl(samp_rate, + rows, cols, + mult, offset, + name, + nconnections, + parent)); + } + + time_raster_sink_b_impl::time_raster_sink_b_impl(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent) + : gr_sync_block("time_raster_sink_b", + gr_make_io_signature(1, -1, sizeof(char)), + gr_make_io_signature(0, 0, 0)), + d_name(name), d_nconnections(nconnections), d_parent(parent), + d_rows(rows), d_cols(cols), + d_mult(std::vector<float>(nconnections,1)), + d_offset(std::vector<float>(nconnections,0)), + d_samp_rate(samp_rate) + { + d_main_gui = NULL; + + d_index = 0; + + d_scale = 1.0f; + + d_icols = static_cast<int>(ceil(d_cols)); + d_tmpflt = fft::malloc_float(d_icols); + memset(d_tmpflt, 0, d_icols*sizeof(float)); + + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_double(d_icols)); + memset(d_residbufs[i], 0, d_icols*sizeof(double)); + } + + set_multiplier(mult); + set_offset(offset); + + initialize(); + } + + time_raster_sink_b_impl::~time_raster_sink_b_impl() + { + fft::free(d_tmpflt); + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + } + } + + bool + time_raster_sink_b_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == d_nconnections; + } + + void + time_raster_sink_b_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc = 0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + // Create time raster plot; as a bit input, we expect to see 1's + // and 0's from each stream, so we set the maximum intensity + // (zmax) to the number of connections so after adding the + // streams, the max will the the max of 1's from all streams. + d_main_gui = new TimeRasterDisplayForm(d_nconnections, + d_samp_rate, + d_rows, d_cols, + 1, d_parent); + + // initialize update time to 10 times a second + set_update_time(0.1); + d_last_time = 0; + } + + void + time_raster_sink_b_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + time_raster_sink_b_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + time_raster_sink_b_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + time_raster_sink_b_impl::set_update_time(double t) + { + //convert update time to ticks + gruel::high_res_timer_type tps = gruel::high_res_timer_tps(); + d_update_time = t * tps; + d_main_gui->setUpdateTime(t); + } + + void + time_raster_sink_b_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + time_raster_sink_b_impl::set_line_label(const std::string &label) + { + d_main_gui->setLineLabel(0, label.c_str()); + } + + void + time_raster_sink_b_impl::set_line_color(const std::string &color) + { + d_main_gui->setLineColor(0, color.c_str()); + } + + void + time_raster_sink_b_impl::set_line_width(int width) + { + d_main_gui->setLineWidth(0, width); + } + + void + time_raster_sink_b_impl::set_line_style(Qt::PenStyle style) + { + d_main_gui->setLineStyle(0, style); + } + + void + time_raster_sink_b_impl::set_line_marker(QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(0, marker); + } + + void + time_raster_sink_b_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + void + time_raster_sink_b_impl::set_num_rows(double rows) + { + d_main_gui->setNumRows(rows); + } + + void + time_raster_sink_b_impl::set_num_cols(double cols) + { + d_main_gui->setNumCols(cols); + } + + void + time_raster_sink_b_impl::set_multiplier(const std::vector<float> &mult) + { + if(mult.size() == 0) { + for(int i = 0; i < d_nconnections; i++) { + d_mult[i] = 1.0f; + } + } + else if(mult.size() == (size_t)d_nconnections) { + for(int i = 0; i < d_nconnections; i++) { + d_mult[i] = mult[i]; + } + } + else { + throw std::runtime_error("time_raster_sink_b_impl::set_multiplier incorrect dimensions.\n"); + } + } + + void + time_raster_sink_b_impl::set_offset(const std::vector<float> &offset) + { + if(offset.size() == 0) { + for(int i = 0; i < d_nconnections; i++) { + d_offset[i] = 0.0f; + } + } + else if(offset.size() == (size_t)d_nconnections) { + for(int i = 0; i < d_nconnections; i++) { + d_offset[i] = offset[i]; + } + } + else { + throw std::runtime_error("time_raster_sink_b_impl::set_offset incorrect dimensions.\n"); + } + } + + void + time_raster_sink_b_impl::set_intensity_range(float min, float max) + { + d_main_gui->setIntensityRange(min, max); + } + + int + time_raster_sink_b_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int n=0, j=0, idx=0; + const int8_t *in = (const int8_t*)input_items[0]; + + unsigned int cols = d_main_gui->numCols(); + if(d_cols != cols) { + d_cols = cols; + d_index = 0; + d_icols = static_cast<int>(ceil(d_cols)); + fft::free(d_tmpflt); + d_tmpflt = fft::malloc_float(d_icols); + memset(d_tmpflt, 0, d_icols*sizeof(float)); + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + d_residbufs[i] = fft::malloc_double(d_icols); + memset(d_residbufs[i], 0, d_icols*sizeof(double)); + } + } + + for(int i = 0; i < noutput_items; i += d_icols) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_icols-d_index; + idx = 0; + + // If we have enough input for one full plot, do it + if(datasize >= resid) { + + // Fill up residbufs with d_size number of items + for(n = 0; n < d_nconnections; n++) { + in = (const int8_t*)input_items[idx++]; + volk_8i_s32f_convert_32f(d_tmpflt, &in[j], + d_scale, resid); + + // Scale and add offset + volk_32f_s32f_multiply_32f(d_tmpflt, d_tmpflt, d_mult[n], resid); + for(unsigned int s = 0; s < resid; s++) + d_tmpflt[s] = d_tmpflt[s] + d_offset[n]; + + volk_32f_convert_64f_u(&d_residbufs[n][d_index], + d_tmpflt, resid); + } + + d_qApplication->postEvent(d_main_gui, + new TimeRasterUpdateEvent(d_residbufs, d_icols)); + + d_index = 0; + j += resid; + } + // Otherwise, copy what we received into the residbufs for next time + // because we set the output_multiple, this should never need to be called + else { + for(n = 0; n < d_nconnections; n++) { + in = (const int8_t*)input_items[idx++]; + volk_8i_s32f_convert_32f(d_tmpflt, &in[j], + d_scale, datasize); + + // Scale and add offset + volk_32f_s32f_multiply_32f(d_tmpflt, d_tmpflt, d_mult[n], datasize); + for(unsigned int s = 0; s < datasize; s++) + d_tmpflt[s] = d_tmpflt[s] + d_offset[n]; + + volk_32f_convert_64f(&d_residbufs[n][d_index], + d_tmpflt, datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/time_raster_sink_b_impl.h b/gr-qtgui/lib/time_raster_sink_b_impl.h new file mode 100644 index 0000000000..248eb8dd67 --- /dev/null +++ b/gr-qtgui/lib/time_raster_sink_b_impl.h @@ -0,0 +1,104 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_TIME_RASTER_SINK_B_IMPL_H +#define INCLUDED_QTGUI_TIME_RASTER_SINK_B_IMPL_H + +#include <qtgui/time_raster_sink_b.h> +#include <filter/firdes.h> +#include <fft/fft.h> +#include <gruel/high_res_timer.h> +#include <gruel/thread.h> +#include <timerasterdisplayform.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API time_raster_sink_b_impl : public time_raster_sink_b + { + private: + void initialize(); + + std::string d_name; + int d_nconnections; + + int d_index; + std::vector<double*> d_residbufs; + + float d_scale; + float *d_tmpflt; + + QWidget *d_parent; + TimeRasterDisplayForm *d_main_gui; + + int d_icols; + double d_rows, d_cols; + std::vector<float> d_mult; + std::vector<float> d_offset; + double d_samp_rate; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + public: + time_raster_sink_b_impl(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + ~time_raster_sink_b_impl(); + + bool check_topology (int ninputs, int noutputs); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(const std::string &label); + void set_line_color(const std::string &color); + void set_line_width(int width); + void set_line_style(Qt::PenStyle style); + void set_line_marker(QwtSymbol::Style marker); + + void set_size(int width, int height); + + void set_num_rows(double rows); + void set_num_cols(double cols); + + void set_multiplier(const std::vector<float> &mult); + void set_offset(const std::vector<float> &offset); + + void set_intensity_range(float min, float max); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_TIME_RASTER_SINK_B_IMPL_H */ diff --git a/gr-qtgui/lib/time_raster_sink_c_impl.cc b/gr-qtgui/lib/time_raster_sink_c_impl.cc new file mode 100644 index 0000000000..ca739961fe --- /dev/null +++ b/gr-qtgui/lib/time_raster_sink_c_impl.cc @@ -0,0 +1,219 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "time_raster_sink_c_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> + +namespace gr { + namespace qtgui { + + time_raster_sink_c::sptr + time_raster_sink_c::make(unsignedint rows, + unsigned int cols, + const std::string &name, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new time_raster_sink_c_impl(rows, cols, + name, parent)); + } + + time_raster_sink_c_impl::time_raster_sink_c_impl(unsignedint rows, + unsigned int cols, + const std::string &name, + QWidget *parent) + : gr_sync_block("time_raster_sink_c", + gr_make_io_signature(1, -1, sizeof(gr_complex)), + gr_make_io_signature(0, 0, 0)), + d_name(name), d_nconnections(1), d_parent(parent), + d_rows(rows), d_cols(cols) + { + d_main_gui = NULL; + + d_index = 0; + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_complex(rows)); + memset(d_residbufs[i], 0, rows*sizeof(float)); + } + + initialize(); + } + + time_raster_sink_c_impl::~time_raster_sink_c_impl() + { + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + } + } + + void + time_raster_sink_c_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required) + { + unsigned int ninputs = ninput_items_required.size(); + for (unsigned int i = 0; i < ninputs; i++) { + ninput_items_required[i] = std::min(d_rows, 8191); + } + } + + void + time_raster_sink_c_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc = 0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + d_main_gui = new TimeRasterDisplayForm(d_nconnections, d_rows, d_cols, d_parent); + + // initialize update time to 10 times a second + set_update_time(0.1); + d_last_time = 0; + } + + void + time_raster_sink_c_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + time_raster_sink_c_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + time_raster_sink_c_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + time_raster_sink_c_impl::set_update_time(double t) + { + //convert update time to ticks + gruel::high_res_timer_type tps = gruel::high_res_timer_tps(); + d_update_time = t * tps; + d_main_gui->setUpdateTime(t); + } + + void + time_raster_sink_c_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + time_raster_sink_c_impl::set_line_label(const std::string &label) + { + d_main_gui->setLineLabel(0, label.c_str()); + } + + void + time_raster_sink_c_impl::set_line_color(const std::string &color) + { + d_main_gui->setLineColor(0, color.c_str()); + } + + void + time_raster_sink_c_impl::set_line_width(int width) + { + d_main_gui->setLineWidth(0, width); + } + + void + time_raster_sink_c_impl::set_line_style(Qt::PenStyle style) + { + d_main_gui->setLineStyle(0, style); + } + + void + time_raster_sink_c_impl::set_line_marker(QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(0, marker); + } + + void + time_raster_sink_c_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + int + time_raster_sink_c_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int j=0; + const gr_complex *in = (const gr_complex*)input_items[0]; + + for(int i=0; i < noutput_items; i+=d_cols) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_cols-d_index; + + // If we have enough input for one full column + if(datasize >= resid) { + + for(int n = 0; n < d_nconnections; n++) { + in = (const gr_complex*)input_items[n]; + memcpy(d_residbufs[n]+d_index, &in[j], sizeof(gr_complex)*resid); + } + + if(gruel::high_res_timer_now() - d_last_time > d_update_time) { + d_last_time = gruel::high_res_timer_now(); + d_qApplication->postEvent(d_main_gui, + new TimeRasterUpdateEvent(d_residbufs, + d_fftsize, + d_last_time)); + } + + d_index = 0; + j += resid; + } + // Otherwise, copy what we received into the residbuf for next time + else { + for(int n = 0; n < d_nconnections; n++) { + in = (const gr_complex*)input_items[n]; + memcpy(d_residbufs[n]+d_index, &in[j], sizeof(gr_complex)*datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/time_raster_sink_c_impl.h b/gr-qtgui/lib/time_raster_sink_c_impl.h new file mode 100644 index 0000000000..3e2a732940 --- /dev/null +++ b/gr-qtgui/lib/time_raster_sink_c_impl.h @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_TIME_RASTER_SINK_C_IMPL_H +#define INCLUDED_QTGUI_TIME_RASTER_SINK_C_IMPL_H + +#include <qtgui/time_raster_sink_c.h> +#include <filter/firdes.h> +#include <fft/fft.h> +#include <gruel/high_res_timer.h> +#include <gruel/thread.h> +#include <time_rasterdisplayform.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API time_raster_sink_c_impl : public time_raster_sink_c + { + private: + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + void initialize(); + + std::string d_name; + int d_nconnections; + + int d_index; + std::vector<gr_complex*> d_residbufs; + float *d_fbuf; + + QWidget *d_parent; + TimeRasterDisplayForm *d_main_gui; + + unsigned int d_rows, d_cols; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + public: + time_raster_sink_c_impl(unsignedint rows, + unsigned int cols, + const std::string &name, + QWidget *parent=NULL); + ~time_raster_sink_c_impl(); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(const std::string &label); + void set_line_color(const std::string &color); + void set_line_width(int width); + void set_line_style(Qt::PenStyle style); + void set_line_marker(QwtSymbol::Style marker); + + void set_size(int width, int height); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_TIME_RASTER_SINK_C_IMPL_H */ diff --git a/gr-qtgui/lib/time_raster_sink_f_impl.cc b/gr-qtgui/lib/time_raster_sink_f_impl.cc new file mode 100644 index 0000000000..be1e66da12 --- /dev/null +++ b/gr-qtgui/lib/time_raster_sink_f_impl.cc @@ -0,0 +1,326 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "time_raster_sink_f_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> + +namespace gr { + namespace qtgui { + + time_raster_sink_f::sptr + time_raster_sink_f::make(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new time_raster_sink_f_impl(samp_rate, + rows, cols, + mult, offset, + name, + nconnections, + parent)); + } + + time_raster_sink_f_impl::time_raster_sink_f_impl(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent) + : gr_sync_block("time_raster_sink_f", + gr_make_io_signature(1, -1, sizeof(float)), + gr_make_io_signature(0, 0, 0)), + d_name(name), d_nconnections(nconnections), d_parent(parent), + d_rows(rows), d_cols(cols), + d_mult(std::vector<float>(nconnections,1)), + d_offset(std::vector<float>(nconnections,0)), + d_samp_rate(samp_rate) + { + d_main_gui = NULL; + + d_index = 0; + + d_icols = static_cast<int>(ceil(d_cols)); + d_tmpflt = fft::malloc_float(d_icols); + memset(d_tmpflt, 0, d_icols*sizeof(float)); + + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_double(d_icols)); + memset(d_residbufs[i], 0, d_icols*sizeof(double)); + } + + set_multiplier(mult); + set_offset(offset); + + initialize(); + } + + time_raster_sink_f_impl::~time_raster_sink_f_impl() + { + fft::free(d_tmpflt); + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + } + } + + bool + time_raster_sink_f_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == d_nconnections; + } + + void + time_raster_sink_f_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc = 0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + // Create time raster plot; as a bit input, we expect to see 1's + // and 0's from each stream, so we set the maximum intensity + // (zmax) to the number of connections so after adding the + // streams, the max will the the max of 1's from all streams. + d_main_gui = new TimeRasterDisplayForm(d_nconnections, + d_samp_rate, + d_rows, d_cols, + 1, d_parent); + + // initialize update time to 10 times a second + set_update_time(0.1); + d_last_time = 0; + } + + void + time_raster_sink_f_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + time_raster_sink_f_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + time_raster_sink_f_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + time_raster_sink_f_impl::set_update_time(double t) + { + //convert update time to ticks + gruel::high_res_timer_type tps = gruel::high_res_timer_tps(); + d_update_time = t * tps; + d_main_gui->setUpdateTime(t); + } + + void + time_raster_sink_f_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + time_raster_sink_f_impl::set_line_label(const std::string &label) + { + d_main_gui->setLineLabel(0, label.c_str()); + } + + void + time_raster_sink_f_impl::set_line_color(const std::string &color) + { + d_main_gui->setLineColor(0, color.c_str()); + } + + void + time_raster_sink_f_impl::set_line_width(int width) + { + d_main_gui->setLineWidth(0, width); + } + + void + time_raster_sink_f_impl::set_line_style(Qt::PenStyle style) + { + d_main_gui->setLineStyle(0, style); + } + + void + time_raster_sink_f_impl::set_line_marker(QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(0, marker); + } + + void + time_raster_sink_f_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + void + time_raster_sink_f_impl::set_num_rows(double rows) + { + d_main_gui->setNumRows(rows); + } + + void + time_raster_sink_f_impl::set_num_cols(double cols) + { + d_main_gui->setNumCols(cols); + } + + void + time_raster_sink_f_impl::set_multiplier(const std::vector<float> &mult) + { + if(mult.size() == 0) { + for(int i = 0; i < d_nconnections; i++) { + d_mult[i] = 1.0f; + } + } + else if(mult.size() == (size_t)d_nconnections) { + for(int i = 0; i < d_nconnections; i++) { + d_mult[i] = mult[i]; + } + } + else { + throw std::runtime_error("time_raster_sink_f_impl::set_multiplier incorrect dimensions.\n"); + } + } + + void + time_raster_sink_f_impl::set_offset(const std::vector<float> &offset) + { + if(offset.size() == 0) { + for(int i = 0; i < d_nconnections; i++) { + d_offset[i] = 0.0f; + } + } + else if(offset.size() == (size_t)d_nconnections) { + for(int i = 0; i < d_nconnections; i++) { + d_offset[i] = offset[i]; + } + } + else { + throw std::runtime_error("time_raster_sink_f_impl::set_offset incorrect dimensions.\n"); + } + } + + void + time_raster_sink_f_impl::set_intensity_range(float min, float max) + { + d_main_gui->setIntensityRange(min, max); + } + + int + time_raster_sink_f_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int n=0, j=0, idx=0; + const float *in = (const float*)input_items[0]; + + unsigned int cols = d_main_gui->numCols(); + if(d_cols != cols) { + d_cols = cols; + d_index = 0; + d_icols = static_cast<int>(ceil(d_cols)); + fft::free(d_tmpflt); + d_tmpflt = fft::malloc_float(d_icols); + memset(d_tmpflt, 0, d_icols*sizeof(float)); + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + d_residbufs[i] = fft::malloc_double(d_icols); + memset(d_residbufs[i], 0, d_icols*sizeof(double)); + } + } + + for(int i = 0; i < noutput_items; i += d_icols) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_icols-d_index; + idx = 0; + + // If we have enough input for one full plot, do it + if(datasize >= resid) { + + // Fill up residbufs with d_size number of items + for(n = 0; n < d_nconnections; n++) { + in = (const float*)input_items[idx++]; + + // Scale and add offset + volk_32f_s32f_multiply_32f(d_tmpflt, &in[j], d_mult[n], resid); + for(unsigned int s = 0; s < resid; s++) + d_tmpflt[s] = d_tmpflt[s] + d_offset[n]; + + volk_32f_convert_64f_u(&d_residbufs[n][d_index], + d_tmpflt, resid); + } + + d_qApplication->postEvent(d_main_gui, + new TimeRasterUpdateEvent(d_residbufs, d_cols)); + + d_index = 0; + j += resid; + } + // Otherwise, copy what we received into the residbufs for next time + // because we set the output_multiple, this should never need to be called + else { + for(n = 0; n < d_nconnections; n++) { + in = (const float*)input_items[idx++]; + + // Scale and add offset + volk_32f_s32f_multiply_32f(d_tmpflt, &in[j], d_mult[n], datasize); + for(unsigned int s = 0; s < datasize; s++) + d_tmpflt[s] = d_tmpflt[s] + d_offset[n]; + + volk_32f_convert_64f(&d_residbufs[n][d_index], + d_tmpflt, datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/time_raster_sink_f_impl.h b/gr-qtgui/lib/time_raster_sink_f_impl.h new file mode 100644 index 0000000000..c14a5c1147 --- /dev/null +++ b/gr-qtgui/lib/time_raster_sink_f_impl.h @@ -0,0 +1,103 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_TIME_RASTER_SINK_F_IMPL_H +#define INCLUDED_QTGUI_TIME_RASTER_SINK_F_IMPL_H + +#include <qtgui/time_raster_sink_f.h> +#include <filter/firdes.h> +#include <fft/fft.h> +#include <gruel/high_res_timer.h> +#include <gruel/thread.h> +#include <timerasterdisplayform.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API time_raster_sink_f_impl : public time_raster_sink_f + { + private: + void initialize(); + + std::string d_name; + int d_nconnections; + + int d_index; + std::vector<double*> d_residbufs; + + float *d_tmpflt; + + QWidget *d_parent; + TimeRasterDisplayForm *d_main_gui; + + int d_icols; + double d_rows, d_cols; + std::vector<float> d_mult; + std::vector<float> d_offset; + double d_samp_rate; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + public: + time_raster_sink_f_impl(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + ~time_raster_sink_f_impl(); + + bool check_topology(int ninputs, int noutputs); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(const std::string &label); + void set_line_color(const std::string &color); + void set_line_width(int width); + void set_line_style(Qt::PenStyle style); + void set_line_marker(QwtSymbol::Style marker); + + void set_size(int width, int height); + + void set_num_rows(double rows); + void set_num_cols(double cols); + + void set_multiplier(const std::vector<float> &mult); + void set_offset(const std::vector<float> &offset); + + void set_intensity_range(float min, float max); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_TIME_RASTER_SINK_F_IMPL_H */ diff --git a/gr-qtgui/lib/timerasterdisplayform.cc b/gr-qtgui/lib/timerasterdisplayform.cc new file mode 100644 index 0000000000..b7f1dba2f6 --- /dev/null +++ b/gr-qtgui/lib/timerasterdisplayform.cc @@ -0,0 +1,199 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <cmath> +#include <QColorDialog> +#include <QMessageBox> +#include <timerasterdisplayform.h> +#include <iostream> + +TimeRasterDisplayForm::TimeRasterDisplayForm(int nplots, + double samp_rate, + double rows, double cols, + double zmax, + QWidget* parent) + : DisplayForm(nplots, parent) +{ + _layout = new QGridLayout(this); + _displayPlot = new TimeRasterDisplayPlot(nplots, samp_rate, rows, cols, this); + _layout->addWidget(_displayPlot, 0, 0); + setLayout(_layout); + + _min_val = 10; + _max_val = -10; + + // We don't use the normal menus that are part of the displayform. + // Clear them out to get rid of their resources. + for(int i = 0; i < nplots; i++) { + _lines_menu[i]->clear(); + } + _line_title_act.clear(); + _line_color_menu.clear(); + _line_width_menu.clear(); + _line_style_menu.clear(); + _line_marker_menu.clear(); + _marker_alpha_menu.clear(); + + // Now create our own menus + for(int i = 0; i < nplots; i++) { + ColorMapMenu *colormap = new ColorMapMenu(i, this); + connect(colormap, SIGNAL(whichTrigger(int, const int, const QColor&, const QColor&)), + this, SLOT(setColorMap(int, const int, const QColor&, const QColor&))); + _lines_menu[i]->addMenu(colormap); + + _marker_alpha_menu.push_back(new MarkerAlphaMenu(i, this)); + connect(_marker_alpha_menu[i], SIGNAL(whichTrigger(int, int)), + this, SLOT(setAlpha(int, int))); + _lines_menu[i]->addMenu(_marker_alpha_menu[i]); + } + + QAction *autoscale_act = new QAction("Auto Scale", this); + autoscale_act->setStatusTip(tr("Autoscale intensity range")); + connect(autoscale_act, SIGNAL(triggered()), this, SLOT(autoScale())); + _menu->addAction(autoscale_act); + + PopupMenu *colsmenu = new PopupMenu("Num. Columns", this); + _menu->addAction(colsmenu); + connect(colsmenu, SIGNAL(whichTrigger(QString)), + this, SLOT(setNumCols(QString))); + + PopupMenu *rowsmenu = new PopupMenu("Num. Rows", this); + _menu->addAction(rowsmenu); + connect(rowsmenu, SIGNAL(whichTrigger(QString)), + this, SLOT(setNumRows(QString))); + + getPlot()->setIntensityRange(0, zmax); + + Reset(); + + connect(_displayPlot, SIGNAL(plotPointSelected(const QPointF)), + this, SLOT(onPlotPointSelected(const QPointF))); +} + +TimeRasterDisplayForm::~TimeRasterDisplayForm() +{ + // Don't worry about deleting Display Plots - they are deleted when + // parents are deleted +} + +TimeRasterDisplayPlot* +TimeRasterDisplayForm::getPlot() +{ + return ((TimeRasterDisplayPlot*)_displayPlot); +} + +double +TimeRasterDisplayForm::numRows() +{ + return getPlot()->numRows(); +} + +double +TimeRasterDisplayForm::numCols() +{ + return getPlot()->numCols(); +} + +void +TimeRasterDisplayForm::newData(const QEvent *updateEvent) +{ + TimeRasterUpdateEvent *event = (TimeRasterUpdateEvent*)updateEvent; + const std::vector<double*> dataPoints = event->getPoints(); + const uint64_t numDataPoints = event->getNumDataPoints(); + + _min_val = 10; + _max_val = -10; + for(size_t i=0; i < dataPoints.size(); i++) { + double *min_val = std::min_element(&dataPoints[i][0], &dataPoints[i][numDataPoints-1]); + double *max_val = std::max_element(&dataPoints[i][0], &dataPoints[i][numDataPoints-1]); + if(*min_val < _min_val) + _min_val = *min_val; + if(*max_val > _max_val) + _max_val = *max_val; + } + + getPlot()->plotNewData(dataPoints, numDataPoints); +} + +void +TimeRasterDisplayForm::customEvent( QEvent * e) +{ + if(e->type() == TimeRasterUpdateEvent::Type()) { + newData(e); + } +} + +void +TimeRasterDisplayForm::setNumRows(double rows) +{ + getPlot()->setNumRows(rows); +} + +void +TimeRasterDisplayForm::setNumRows(QString rows) +{ + getPlot()->setNumRows(rows.toDouble()); +} + +void +TimeRasterDisplayForm::setNumCols(double cols) +{ + getPlot()->setNumCols(cols); +} + +void +TimeRasterDisplayForm::setNumCols(QString cols) +{ + getPlot()->setNumCols(cols.toDouble()); +} + +void +TimeRasterDisplayForm::setColorMap(int which, + const int newType, + const QColor lowColor, + const QColor highColor) +{ + getPlot()->setIntensityColorMapType(which, newType, + lowColor, highColor); +} + +void +TimeRasterDisplayForm::setAlpha(int which, int alpha) +{ + getPlot()->setAlpha(which, alpha); +} + +void +TimeRasterDisplayForm::setIntensityRange(const double minIntensity, + const double maxIntensity) +{ + getPlot()->setIntensityRange(minIntensity, maxIntensity); +} + +void +TimeRasterDisplayForm::autoScale() +{ + double min_int = _min_val; + double max_int = _max_val; + + getPlot()->setIntensityRange(min_int, max_int); +} diff --git a/gr-qtgui/lib/timerasterdisplayform.h b/gr-qtgui/lib/timerasterdisplayform.h new file mode 100644 index 0000000000..e136f5faf8 --- /dev/null +++ b/gr-qtgui/lib/timerasterdisplayform.h @@ -0,0 +1,81 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIMERASTER_DISPLAY_FORM_H +#define TIMERASTER_DISPLAY_FORM_H + +#include <spectrumUpdateEvents.h> +#include <TimeRasterDisplayPlot.h> +#include <QtGui/QtGui> +#include <vector> +#include <filter/firdes.h> + +#include "displayform.h" + +class TimeRasterDisplayForm : public DisplayForm +{ + Q_OBJECT + + public: + TimeRasterDisplayForm(int nplots=1, + double samp_rate=1, + double rows=1, + double cols=1, + double zmax=1, + QWidget* parent = 0); + ~TimeRasterDisplayForm(); + + TimeRasterDisplayPlot* getPlot(); + + double numRows(); + double numCols(); + +public slots: + void customEvent(QEvent *e); + + void setNumRows(double rows); + void setNumCols(double cols); + + void setNumRows(QString rows); + void setNumCols(QString cols); + + void setIntensityRange(const double minIntensity, + const double maxIntensity); + + void setColorMap(int which, + const int newType, + const QColor lowColor, + const QColor highColor); + + void setAlpha(int which, int alpha); + + void autoScale(); + +private slots: + void newData(const QEvent *updateEvent); + +private: + double _min_val; + double _max_val; +}; + +#endif /* TIMERASTER_DISPLAY_FORM_H */ diff --git a/gr-qtgui/swig/qtgui_swig.i b/gr-qtgui/swig/qtgui_swig.i index 20675503e2..4e1d1969e2 100644 --- a/gr-qtgui/swig/qtgui_swig.i +++ b/gr-qtgui/swig/qtgui_swig.i @@ -36,6 +36,8 @@ #include "qtgui/sink_f.h" #include "qtgui/time_sink_c.h" #include "qtgui/time_sink_f.h" +#include "qtgui/time_raster_sink_b.h" +#include "qtgui/time_raster_sink_f.h" #include "qtgui/freq_sink_c.h" #include "qtgui/freq_sink_f.h" #include "qtgui/const_sink_c.h" @@ -47,6 +49,8 @@ %include "qtgui/sink_f.h" %include "qtgui/time_sink_c.h" %include "qtgui/time_sink_f.h" +%include "qtgui/time_raster_sink_b.h" +%include "qtgui/time_raster_sink_f.h" %include "qtgui/freq_sink_c.h" %include "qtgui/freq_sink_f.h" %include "qtgui/const_sink_c.h" @@ -57,6 +61,8 @@ GR_SWIG_BLOCK_MAGIC2(qtgui, sink_c); GR_SWIG_BLOCK_MAGIC2(qtgui, sink_f); GR_SWIG_BLOCK_MAGIC2(qtgui, time_sink_c); GR_SWIG_BLOCK_MAGIC2(qtgui, time_sink_f); +GR_SWIG_BLOCK_MAGIC2(qtgui, time_raster_sink_b); +GR_SWIG_BLOCK_MAGIC2(qtgui, time_raster_sink_f); GR_SWIG_BLOCK_MAGIC2(qtgui, freq_sink_c); GR_SWIG_BLOCK_MAGIC2(qtgui, freq_sink_f); GR_SWIG_BLOCK_MAGIC2(qtgui, const_sink_c); |