diff --git a/CMakeLists.txt b/CMakeLists.txt index 8620b3e..6987842 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ list(APPEND inspectrum_sources samplebuffer.cpp spectrogram.cpp spectrogramcontrols.cpp + traceplot.cpp ) INCLUDE(FindPkgConfig) diff --git a/plot.cpp b/plot.cpp new file mode 100644 index 0000000..9643982 --- /dev/null +++ b/plot.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016, Mike Walters + * + * This file is part of inspectrum. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#include "plot.h" \ No newline at end of file diff --git a/plot.h b/plot.h new file mode 100644 index 0000000..850f5fa --- /dev/null +++ b/plot.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016, Mike Walters + * + * This file is part of inspectrum. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#pragma once + +#include +#include +#include "util.h" + +class Plot : public QObject +{ + +public: + virtual void paintBack(QPainter &painter, QRect &rect, range_t sampleRange) = 0; + virtual void paintMid(QPainter &painter, QRect &rect, range_t sampleRange) = 0; + virtual void paintFront(QPainter &painter, QRect &rect, range_t sampleRange) = 0; +}; diff --git a/plotview.cpp b/plotview.cpp index a20a2a0..db22de4 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -28,6 +28,7 @@ #include "grsamplebuffer.h" #include "memory_sink.h" #include "memory_source.h" +#include "traceplot.h" PlotView::PlotView() { @@ -41,7 +42,7 @@ PlotView::PlotView() void PlotView::refreshSources() { - sampleSources.clear(); + plots.clear(); gr::top_block_sptr iq_tb = gr::make_top_block("multiply"); auto iq_mem_source = gr::blocks::memory_source::make(8); @@ -68,8 +69,16 @@ void PlotView::refreshSources() quad_demod_tb->connect(quad_demod_mem_source, 0, quad_demod, 0); quad_demod_tb->connect(quad_demod, 0, quad_demod_mem_sink, 0); - sampleSources.emplace_back(new GRSampleBuffer, std::complex>(mainSampleSource, iq_tb, iq_mem_source, iq_mem_sink)); - sampleSources.emplace_back(new GRSampleBuffer, float>(dynamic_cast>*>(sampleSources[0].get()), quad_demod_tb, quad_demod_mem_source, quad_demod_mem_sink)); + auto iq_src = std::make_shared, std::complex>>(mainSampleSource, iq_tb, iq_mem_source, iq_mem_sink); + plots.emplace_back(new TracePlot(iq_src)); + + plots.emplace_back( + new TracePlot( + std::make_shared, float>>( + dynamic_cast>*>(iq_src.get()), quad_demod_tb, quad_demod_mem_source, quad_demod_mem_sink + ) + ) + ); update(); } @@ -113,42 +122,21 @@ void PlotView::paintEvent(QPaintEvent *event) painter.fillRect(rect, Qt::black); // Split space equally between waveforms for now - int waveHeight = height() / sampleSources.size(); - int wave = 0; - for (auto&& sampleSource : sampleSources) { - QRect waveRect = QRect(0, wave * waveHeight, width(), waveHeight); - off_t length = lastSample - firstSample; - if (auto src = dynamic_cast>*>(sampleSource.get())) { - auto samples = src->getSamples(firstSample, length); - painter.setPen(Qt::red); - plot(&painter, waveRect, reinterpret_cast(samples.get()), length, 2); - painter.setPen(Qt::blue); - plot(&painter, waveRect, reinterpret_cast(samples.get())+1, length, 2); - } else if (auto src = dynamic_cast*>(sampleSource.get())) { - auto samples = src->getSamples(firstSample, length); - painter.setPen(Qt::green); - plot(&painter, waveRect, samples.get(), length, 1); - } - wave++; + int plotHeight = height() / plots.size(); + +#define PLOT_LAYER(paintFunc) \ + { \ + int plotCount = 0; \ + for (auto&& plot : plots) { \ + QRect rect = QRect(0, plotCount * plotHeight, width(), plotHeight); \ + plot->paintFunc(painter, rect, {firstSample, lastSample}); \ + plotCount++; \ + } \ } + + PLOT_LAYER(paintBack); + PLOT_LAYER(paintMid); + PLOT_LAYER(paintFront); + +#undef PLOT_LAYER } - -void PlotView::plot(QPainter *painter, QRect &rect, float *samples, off_t count, int step = 1) -{ - int xprev = 0; - int yprev = 0; - for (off_t i = 0; i < count; i++) { - float sample = samples[i*step]; - int x = (float)i / count * rect.width(); - int y = rect.height() - ((sample * rect.height()/2) + rect.height()/2); - - if (x < 0) x = 0; - if (y < 0) y = 0; - if (x >= rect.width()-1) x = rect.width()-2; - if (y >= rect.height()-1) y = rect.height()-2; - - painter->drawLine(xprev + rect.x(), yprev + rect.y(), x + rect.x(), y + rect.y()); - xprev = x; - yprev = y; - } -} \ No newline at end of file diff --git a/plotview.h b/plotview.h index 64f5d2a..7548895 100644 --- a/plotview.h +++ b/plotview.h @@ -22,6 +22,7 @@ #include #include #include "inputsource.h" +#include "plot.h" class PlotView : public QDockWidget { @@ -41,7 +42,7 @@ protected: private: SampleSource> *mainSampleSource = nullptr; - std::vector> sampleSources; + std::vector> plots; off_t firstSample = 0; off_t lastSample = 0; bool selection = false; @@ -50,5 +51,4 @@ private: QRgb colormap[255]; void refreshSources(); - void plot(QPainter *painter, QRect &rect, float *samples, off_t count, int step); }; \ No newline at end of file diff --git a/traceplot.cpp b/traceplot.cpp new file mode 100644 index 0000000..9387f6f --- /dev/null +++ b/traceplot.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016, Mike Walters + * + * This file is part of inspectrum. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#include "samplesource.h" +#include "traceplot.h" + +TracePlot::TracePlot(std::shared_ptr source) : sampleSource(source) { + +} + +void TracePlot::paintBack(QPainter &painter, QRect &rect, range_t sampleRange) +{ + +} + +void TracePlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) +{ + auto firstSample = sampleRange.minimum; + auto length = sampleRange.length(); + + // Is it a 2-channel (complex) trace? + if (auto src = dynamic_cast>*>(sampleSource.get())) { + auto samples = src->getSamples(firstSample, length); + painter.setPen(Qt::red); + plotTrace(painter, rect, reinterpret_cast(samples.get()), length, 2); + painter.setPen(Qt::blue); + plotTrace(painter, rect, reinterpret_cast(samples.get())+1, length, 2); + + // Otherwise is it single channel? + } else if (auto src = dynamic_cast*>(sampleSource.get())) { + auto samples = src->getSamples(firstSample, length); + painter.setPen(Qt::green); + plotTrace(painter, rect, samples.get(), length, 1); + } else { + throw std::runtime_error("TracePlot::paintMid: Unsupported source type"); + } +} + +void TracePlot::plotTrace(QPainter &painter, QRect &rect, float *samples, off_t count, int step = 1) +{ + int xprev = 0; + int yprev = 0; + for (off_t i = 0; i < count; i++) { + float sample = samples[i*step]; + int x = (float)i / count * rect.width(); + int y = rect.height() - ((sample * rect.height()/2) + rect.height()/2); + + if (x < 0) x = 0; + if (y < 0) y = 0; + if (x >= rect.width()-1) x = rect.width()-2; + if (y >= rect.height()-1) y = rect.height()-2; + + painter.drawLine(xprev + rect.x(), yprev + rect.y(), x + rect.x(), y + rect.y()); + xprev = x; + yprev = y; + } +} + +void TracePlot::paintFront(QPainter &painter, QRect &rect, range_t sampleRange) +{ + +} diff --git a/traceplot.h b/traceplot.h new file mode 100644 index 0000000..4e34eb9 --- /dev/null +++ b/traceplot.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016, Mike Walters + * + * This file is part of inspectrum. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#pragma once +#include +#include "abstractsamplesource.h" +#include "plot.h" +#include "util.h" + +class TracePlot : public Plot +{ + Q_OBJECT + +public: + TracePlot(std::shared_ptr source); + + void paintBack(QPainter &painter, QRect &rect, range_t sampleRange); + void paintMid(QPainter &painter, QRect &rect, range_t sampleRange); + void paintFront(QPainter &painter, QRect &rect, range_t sampleRange); + +private: + std::shared_ptr sampleSource; + + void plotTrace(QPainter &painter, QRect &rect, float *samples, off_t count, int step); +};