From c57b705cf907ad3329da629f9d4dbbd82dc53c08 Mon Sep 17 00:00:00 2001 From: trondeau Date: Sat, 28 Feb 2009 04:15:05 +0000 Subject: [PATCH] Adding a constellation plot and an example for viewing constellations of digital modulations. git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@10537 221aa14e-8319-0410-a670-987f0aec2ac5 --- gr-qtgui/src/lib/ConstellationDisplayPlot.cc | 167 +++++++++++++++++++ gr-qtgui/src/lib/ConstellationDisplayPlot.h | 49 ++++++ gr-qtgui/src/lib/Makefile.am | 3 + gr-qtgui/src/lib/spectrumdisplayform.cc | 34 ++-- gr-qtgui/src/lib/spectrumdisplayform.h | 2 + gr-qtgui/src/lib/spectrumdisplayform.ui | 61 ++++--- gr-qtgui/src/python/qt_digital.py | 40 +++++ 7 files changed, 324 insertions(+), 32 deletions(-) create mode 100644 gr-qtgui/src/lib/ConstellationDisplayPlot.cc create mode 100644 gr-qtgui/src/lib/ConstellationDisplayPlot.h create mode 100755 gr-qtgui/src/python/qt_digital.py diff --git a/gr-qtgui/src/lib/ConstellationDisplayPlot.cc b/gr-qtgui/src/lib/ConstellationDisplayPlot.cc new file mode 100644 index 00000000..c422c8f5 --- /dev/null +++ b/gr-qtgui/src/lib/ConstellationDisplayPlot.cc @@ -0,0 +1,167 @@ +#ifndef CONSTELLATION_DISPLAY_PLOT_C +#define CONSTELLATION_DISPLAY_PLOT_C + +#include + +#include +#include + + +class ConstellationDisplayZoomer: public QwtPlotZoomer +{ +public: + ConstellationDisplayZoomer(QwtPlotCanvas* canvas):QwtPlotZoomer(canvas) + { + setTrackerMode(QwtPicker::AlwaysOn); + } + + virtual ~ConstellationDisplayZoomer(){ + + } + + virtual void updateTrackerText(){ + updateDisplay(); + } + +protected: + virtual QwtText trackerText( const QwtDoublePoint& p ) const + { + QwtText t(QString("Sample %1, %2 V").arg(p.x(), 0, 'f', 0).arg(p.y(), 0, 'f', 4)); + + return t; + } +}; + +ConstellationDisplayPlot::ConstellationDisplayPlot(QWidget* parent):QwtPlot(parent){ + timespec_reset(&_lastReplot); + + resize(parent->width(), parent->height()); + + _displayIntervalTime = (1.0/10.0); // 1/10 of a second between updates + + _numPoints = 1024; + _realDataPoints = new double[_numPoints]; + _imagDataPoints = new double[_numPoints]; + + // Disable polygon clipping + QwtPainter::setDeviceClipping(false); + + // We don't need the cache here + canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false); + canvas()->setPaintAttribute(QwtPlotCanvas::PaintPacked, false); + + QPalette palette; + palette.setColor(canvas()->backgroundRole(), QColor("white")); + canvas()->setPalette(palette); + + setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); + setAxisScale(QwtPlot::xBottom, -1.0, 1.0); + setAxisTitle(QwtPlot::xBottom, "In-phase"); + + setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + setAxisScale(QwtPlot::yLeft, -1.0, 1.0); + setAxisTitle(QwtPlot::yLeft, "Quadrature"); + + // Automatically deleted when parent is deleted + _plot_curve = new QwtPlotCurve("Constellation Points"); + _plot_curve->attach(this); + _plot_curve->setPen(QPen(Qt::blue, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + _plot_curve->setStyle(QwtPlotCurve::Dots); + _plot_curve->setRawData(_realDataPoints, _imagDataPoints, _numPoints); + + memset(_realDataPoints, 0x0, _numPoints*sizeof(double)); + memset(_imagDataPoints, 0x0, _numPoints*sizeof(double)); + + replot(); + + _zoomer = new ConstellationDisplayZoomer(canvas()); +#if QT_VERSION < 0x040000 + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); +#else + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); +#endif + _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, + Qt::RightButton); + + _panner = new QwtPlotPanner(canvas()); + _panner->setAxisEnabled(QwtPlot::yRight, false); + _panner->setMouseButton(Qt::MidButton); + + // Avoid jumping when labels with more/less digits + // appear/disappear when scrolling vertically + + const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font()); + QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft); + sd->setMinimumExtent( fm.width("100.00") ); + + const QColor c(Qt::darkRed); + _zoomer->setRubberBandPen(c); + _zoomer->setTrackerPen(c); + + QwtLegend* legendDisplay = new QwtLegend(this); + legendDisplay->setItemMode(QwtLegend::CheckableItem); + insertLegend(legendDisplay); + + connect(this, SIGNAL( legendChecked(QwtPlotItem *, bool ) ), this, SLOT( LegendEntryChecked(QwtPlotItem *, bool ) )); +} + +ConstellationDisplayPlot::~ConstellationDisplayPlot(){ + delete[] _realDataPoints; + delete[] _imagDataPoints; + + // _fft_plot_curves deleted when parent deleted + // _zoomer and _panner deleted when parent deleted +} + + + +void ConstellationDisplayPlot::replot(){ + + const timespec startTime = get_highres_clock(); + + QwtPlot::replot(); + + double differenceTime = (diff_timespec(get_highres_clock(), startTime)); + + differenceTime *= 99.0; + // Require at least a 10% duty cycle + if(differenceTime > (1.0/10.0)){ + _displayIntervalTime = differenceTime; + } +} + +void ConstellationDisplayPlot::PlotNewData(const double* realDataPoints, const double* imagDataPoints, const int64_t numDataPoints){ + if(numDataPoints > 0){ + + if(numDataPoints != _numPoints){ + _numPoints = numDataPoints; + + delete[] _realDataPoints; + delete[] _imagDataPoints; + _realDataPoints = new double[_numPoints]; + _imagDataPoints = new double[_numPoints]; + + _plot_curve->setRawData(_realDataPoints, _imagDataPoints, _numPoints); + } + memcpy(_realDataPoints, realDataPoints, numDataPoints*sizeof(double)); + memcpy(_imagDataPoints, imagDataPoints, numDataPoints*sizeof(double)); + + } + + // Allow at least a 50% duty cycle + if(diff_timespec(get_highres_clock(), _lastReplot) > _displayIntervalTime){ + // Only replot the screen if it is visible + if(isVisible()){ + replot(); + } + _lastReplot = get_highres_clock(); + } +} + +void ConstellationDisplayPlot::LegendEntryChecked(QwtPlotItem* plotItem, bool on){ + plotItem->setVisible(!on); +} + +#endif /* CONSTELLATION_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/src/lib/ConstellationDisplayPlot.h b/gr-qtgui/src/lib/ConstellationDisplayPlot.h new file mode 100644 index 00000000..d7604ea8 --- /dev/null +++ b/gr-qtgui/src/lib/ConstellationDisplayPlot.h @@ -0,0 +1,49 @@ +#ifndef CONSTELLATION_DISPLAY_PLOT_HPP +#define CONSTELLATION_DISPLAY_PLOT_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class ConstellationDisplayPlot:public QwtPlot{ + Q_OBJECT + +public: + ConstellationDisplayPlot(QWidget*); + virtual ~ConstellationDisplayPlot(); + + void PlotNewData(const double* realDataPoints, const double* imagDataPoints, + const int64_t numDataPoints); + + virtual void replot(); + +protected slots: + void LegendEntryChecked(QwtPlotItem *plotItem, bool on); + +protected: + +private: + QwtPlotCurve* _plot_curve; + + QwtPlotPanner* _panner; + QwtPlotZoomer* _zoomer; + + double* _realDataPoints; + double* _imagDataPoints; + + timespec _lastReplot; + + int64_t _numPoints; + + double _displayIntervalTime; +}; + +#endif /* CONSTELLATION_DISPLAY_PLOT_HPP */ diff --git a/gr-qtgui/src/lib/Makefile.am b/gr-qtgui/src/lib/Makefile.am index 41281e68..034eff05 100644 --- a/gr-qtgui/src/lib/Makefile.am +++ b/gr-qtgui/src/lib/Makefile.am @@ -54,6 +54,7 @@ QMAKE_SOURCES = \ TimeDomainDisplayPlot_moc.cc \ WaterfallDisplayPlot_moc.cc \ Waterfall3DDisplayPlot_moc.cc \ + ConstellationDisplayPlot_moc.cc \ spectrumdisplayform_ui.h endif @@ -81,6 +82,7 @@ libqtgui_la_SOURCES = \ WaterfallDisplayPlot.cc \ Waterfall3DDisplayPlot.cc \ waterfallGlobalData.cc \ + ConstellationDisplayPlot.cc \ spectrumdisplayform.cc \ SpectrumGUIClass.cc \ spectrumUpdateEvents.cc \ @@ -100,6 +102,7 @@ grinclude_HEADERS = \ WaterfallDisplayPlot.h \ Waterfall3DDisplayPlot.h \ waterfallGlobalData.h \ + ConstellationDisplayPlot.h \ highResTimeFunctions.h \ plot_waterfall.h \ spectrumdisplayform.h \ diff --git a/gr-qtgui/src/lib/spectrumdisplayform.cc b/gr-qtgui/src/lib/spectrumdisplayform.cc index e0b43ae3..a898015b 100644 --- a/gr-qtgui/src/lib/spectrumdisplayform.cc +++ b/gr-qtgui/src/lib/spectrumdisplayform.cc @@ -15,6 +15,7 @@ SpectrumDisplayForm::SpectrumDisplayForm(QWidget* parent) : QDialog(parent){ _waterfallDisplayPlot = new WaterfallDisplayPlot(Tab2PlotDisplayFrame); _waterfall3DDisplayPlot = new Waterfall3DDisplayPlot(Waterfall3DPlotDisplayFrame); _timeDomainDisplayPlot = new TimeDomainDisplayPlot(TimeDomainDisplayFrame); + _constellationDisplayPlot = new ConstellationDisplayPlot(ConstellationDisplayFrame); _numRealDataPoints = 1024; _realFFTDataPoints = new double[_numRealDataPoints]; _averagedValues = new double[_numRealDataPoints]; @@ -42,8 +43,10 @@ SpectrumDisplayForm::SpectrumDisplayForm(QWidget* parent) : QDialog(parent){ _noiseFloorAmplitude = -HUGE_VAL; - connect(_waterfallDisplayPlot, SIGNAL(UpdatedLowerIntensityLevel(const double)), _frequencyDisplayPlot, SLOT(SetLowerIntensityLevel(const double))); - connect(_waterfallDisplayPlot, SIGNAL(UpdatedUpperIntensityLevel(const double)), _frequencyDisplayPlot, SLOT(SetUpperIntensityLevel(const double))); + connect(_waterfallDisplayPlot, SIGNAL(UpdatedLowerIntensityLevel(const double)), + _frequencyDisplayPlot, SLOT(SetLowerIntensityLevel(const double))); + connect(_waterfallDisplayPlot, SIGNAL(UpdatedUpperIntensityLevel(const double)), + _frequencyDisplayPlot, SLOT(SetUpperIntensityLevel(const double))); _frequencyDisplayPlot->SetLowerIntensityLevel(-200); _frequencyDisplayPlot->SetUpperIntensityLevel(-200); @@ -72,7 +75,9 @@ SpectrumDisplayForm::~SpectrumDisplayForm(){ delete _historyVector; } -void SpectrumDisplayForm::setSystem( SpectrumGUIClass * newSystem, const uint64_t numFFTDataPoints, const uint64_t numTimeDomainDataPoints ) +void SpectrumDisplayForm::setSystem( SpectrumGUIClass * newSystem, + const uint64_t numFFTDataPoints, + const uint64_t numTimeDomainDataPoints ) { ResizeBuffers(numFFTDataPoints, numTimeDomainDataPoints); @@ -168,6 +173,9 @@ void SpectrumDisplayForm::newFrequencyData( const SpectrumUpdateEvent* spectrumU _timeDomainDisplayPlot->PlotNewData(realTimeDomainDataPoints, imagTimeDomainDataPoints, numTimeDomainDataPoints); + _constellationDisplayPlot->PlotNewData(realTimeDomainDataPoints, + imagTimeDomainDataPoints, + numTimeDomainDataPoints); } // Don't update the repeated data for the waterfall if(!repeatDataFlag){ @@ -199,27 +207,29 @@ void SpectrumDisplayForm::resizeEvent( QResizeEvent *e ) SpectrumTypeTab->resize( e->size().width(), SpectrumTypeTab->height()); // Tell the TabXFreqDisplay to resize - Tab1PlotDisplayFrame->resize(e->size().width()-4, + Tab1PlotDisplayFrame->resize(e->size().width()-4, Tab1PlotDisplayFrame->height()); Tab2PlotDisplayFrame->resize(e->size().width()-4, Tab2PlotDisplayFrame->height()); - Waterfall3DPlotDisplayFrame->resize(e->size().width()-4, + Waterfall3DPlotDisplayFrame->resize(e->size().width()-4, Waterfall3DPlotDisplayFrame->height()); - TimeDomainDisplayFrame->resize(e->size().width()-4, + TimeDomainDisplayFrame->resize(e->size().width()-4, TimeDomainDisplayFrame->height()); - _frequencyDisplayPlot->resize( Tab1PlotDisplayFrame->width()-4, + _frequencyDisplayPlot->resize( Tab1PlotDisplayFrame->width()-4, Tab1PlotDisplayFrame->height()); - _waterfallDisplayPlot->resize( Tab2PlotDisplayFrame->width()-4, + _waterfallDisplayPlot->resize( Tab2PlotDisplayFrame->width()-4, Tab2PlotDisplayFrame->height()); - _waterfall3DDisplayPlot->resize( Waterfall3DPlotDisplayFrame->width()-4, + _waterfall3DDisplayPlot->resize( Waterfall3DPlotDisplayFrame->width()-4, Waterfall3DPlotDisplayFrame->height()); - _timeDomainDisplayPlot->resize( TimeDomainDisplayFrame->width()-4, + _timeDomainDisplayPlot->resize( TimeDomainDisplayFrame->width()-4, TimeDomainDisplayFrame->height()); // Move the IntensityWheels and Labels - WaterfallMaximumIntensityLabel->move(width() - 5 - WaterfallMaximumIntensityLabel->width(), + WaterfallMaximumIntensityLabel->move(width() - 5 - + WaterfallMaximumIntensityLabel->width(), WaterfallMaximumIntensityLabel->y()); - WaterfallMinimumIntensityLabel->move(width() - 5 - WaterfallMinimumIntensityLabel->width(), + WaterfallMinimumIntensityLabel->move(width() - 5 - + WaterfallMinimumIntensityLabel->width(), WaterfallMinimumIntensityLabel->y()); WaterfallMaximumIntensityWheel->resize(WaterfallMaximumIntensityLabel->x() - 5 - WaterfallMaximumIntensityWheel->x(), diff --git a/gr-qtgui/src/lib/spectrumdisplayform.h b/gr-qtgui/src/lib/spectrumdisplayform.h index 97b14a6c..6e57e2ce 100644 --- a/gr-qtgui/src/lib/spectrumdisplayform.h +++ b/gr-qtgui/src/lib/spectrumdisplayform.h @@ -11,6 +11,7 @@ class SpectrumGUIClass; #include #include #include +#include #include #include @@ -73,6 +74,7 @@ private: WaterfallDisplayPlot* _waterfallDisplayPlot; Waterfall3DDisplayPlot* _waterfall3DDisplayPlot; TimeDomainDisplayPlot* _timeDomainDisplayPlot; + ConstellationDisplayPlot* _constellationDisplayPlot; SpectrumGUIClass* _system; bool _systemSpecifiedFlag; double _centerFrequency; diff --git a/gr-qtgui/src/lib/spectrumdisplayform.ui b/gr-qtgui/src/lib/spectrumdisplayform.ui index aac323da..561be64c 100644 --- a/gr-qtgui/src/lib/spectrumdisplayform.ui +++ b/gr-qtgui/src/lib/spectrumdisplayform.ui @@ -358,7 +358,7 @@ - + 215 @@ -373,16 +373,16 @@ Qt::WheelFocus - + true - + 200.000000000000000 - + 20.000000000000000 - + 0.000000000000000 @@ -418,7 +418,7 @@ QFrame::Plain - + 215 @@ -427,16 +427,16 @@ 24 - + true - + 200.000000000000000 - + 20.000000000000000 - + 0.000000000000000 @@ -525,7 +525,7 @@ Auto Scale - + 215 @@ -534,16 +534,16 @@ 24 - + true - + 200.000000000000000 - + 20.000000000000000 - + 0.000000000000000 @@ -614,7 +614,7 @@ - + 215 @@ -629,16 +629,16 @@ Qt::WheelFocus - + true - + 200.000000000000000 - + 20.000000000000000 - + 0.000000000000000 @@ -664,6 +664,27 @@ + + + Constellation Display + + + + + 5 + 5 + 620 + 340 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + diff --git a/gr-qtgui/src/python/qt_digital.py b/gr-qtgui/src/python/qt_digital.py new file mode 100755 index 00000000..f7635b4a --- /dev/null +++ b/gr-qtgui/src/python/qt_digital.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +from gnuradio import gr, blks2 +from gnuradio.qtgui import qtgui +import scipy + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + sps = 2 + excess_bw = 0.35 + gray_code = True + + fftsize = 2048 + + data = scipy.random.randint(0, 255, 1000) + src = gr.vector_source_b(data, True) + mod = blks2.dqpsk_mod(sps, excess_bw, gray_code, False, False) + + rrctaps = gr.firdes.root_raised_cosine(1, sps, 1, excess_bw, 21) + rx_rrc = gr.fir_filter_ccf(sps, rrctaps) + + thr = gr.throttle(gr.sizeof_gr_complex, 10*fftsize) + self.snk_tx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS, -1/2, 1/2) + self.snk_rx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS, -1/2, 1/2) + + self.connect(src, mod, self.snk_tx) + self.connect(mod, rx_rrc, thr, self.snk_rx) + + self.snk_tx.initialize() + qapp = self.snk_tx.get_qapplication() + self.snk_rx.initialize(qapp) + +if __name__ == "__main__": + tb = my_top_block(); + tb.start() + tb.snk_tx.start_app(); + #tb.wait(); + -- 2.30.2