diff --git a/mainwindow.cpp b/mainwindow.cpp index 748da7b..73192a2 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -44,7 +44,7 @@ MainWindow::MainWindow() connect(dock->powerMaxSlider, SIGNAL(valueChanged(int)), plots, SLOT(setPowerMax(int))); connect(dock->powerMinSlider, SIGNAL(valueChanged(int)), plots, SLOT(setPowerMin(int))); connect(dock->cursorsCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableCursors); - connect(dock->timeScaleCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableTimeScale); + connect(dock->scalesCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableScales); connect(dock->cursorSymbolsSpinBox, static_cast(&QSpinBox::valueChanged), plots, &PlotView::setCursorSegments); // Connect dock outputs diff --git a/plotview.cpp b/plotview.cpp index 04b0734..a66f979 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -39,12 +39,13 @@ PlotView::PlotView(InputSource *input) : cursors(this), viewRange({0, 0}) setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); setMouseTracking(true); enableCursors(false); - enableTimeScale(true); connect(&cursors, SIGNAL(cursorsMoved()), this, SLOT(cursorsMoved())); spectrogramPlot = new SpectrogramPlot(std::shared_ptr>>(mainSampleSource)); auto tunerOutput = std::dynamic_pointer_cast>>(spectrogramPlot->output()); + enableScales(true); + addPlot(spectrogramPlot); viewport()->installEventFilter(this); @@ -373,8 +374,10 @@ void PlotView::paintEvent(QPaintEvent *event) if (cursorsEnabled) cursors.paintFront(painter, rect, viewRange); - if (timeScaleEnabled) + if (timeScaleEnabled) { paintTimeScale(painter, rect, viewRange); + } + #undef PLOT_LAYER } @@ -429,7 +432,6 @@ void PlotView::paintTimeScale(QPainter &painter, QRect &rect, range_t sam painter.restore(); } - int PlotView::plotsHeight() { int height = 0; @@ -491,12 +493,20 @@ void PlotView::updateView(bool reCenter) void PlotView::setSampleRate(off_t rate) { sampleRate = rate; + + if (spectrogramPlot != nullptr) + spectrogramPlot->setSampleRate(rate); + emitTimeSelection(); } -void PlotView::enableTimeScale(bool enabled) +void PlotView::enableScales(bool enabled) { timeScaleEnabled = enabled; + + if (spectrogramPlot != nullptr) + spectrogramPlot->enableScales(enabled); + viewport()->update(); } diff --git a/plotview.h b/plotview.h index 32922b3..139a0b3 100644 --- a/plotview.h +++ b/plotview.h @@ -45,7 +45,7 @@ signals: public slots: void cursorsMoved(); void enableCursors(bool enabled); - void enableTimeScale(bool enabled); + void enableScales(bool enabled); void invalidateEvent(); void repaint(); void setCursorSegments(int segments); diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 4aa92ea..00a85a3 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -58,9 +58,9 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent powerMinSlider->setRange(-140, 10); layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); - timeScaleCheckBox = new QCheckBox(widget); - timeScaleCheckBox->setCheckState(Qt::Checked); - layout->addRow(new QLabel(tr("Time overlay:")), timeScaleCheckBox); + scalesCheckBox = new QCheckBox(widget); + scalesCheckBox->setCheckState(Qt::Checked); + layout->addRow(new QLabel(tr("Scales:")), scalesCheckBox); // Time selection settings layout->addRow(new QLabel()); // TODO: find a better way to add an empty row? diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index 3b3bfee..527e878 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -68,5 +68,5 @@ public: QLabel *periodLabel; QLabel *symbolRateLabel; QLabel *symbolPeriodLabel; - QCheckBox *timeScaleCheckBox; + QCheckBox *scalesCheckBox; }; diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index 2f85de9..c024db2 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -37,6 +37,8 @@ SpectrogramPlot::SpectrogramPlot(std::shared_ptr { if (tunerEnabled()) tuner.paintFront(painter, rect, sampleRange); + + if (frequencyScaleEnabled) + paintFrequencyScale(painter, rect); +} + +void SpectrogramPlot::paintFrequencyScale(QPainter &painter, QRect &rect) +{ + // At which pixel is F_+sampleRate/2 + int y = rect.y(); + + int plotHeight = rect.height(); + + double bwPerPixel = (double)sampleRate / plotHeight; + int tickHeight = 50; + + int bwPerTick = 10 * pow(10, floor(log(bwPerPixel * tickHeight) / log(10))); + + if (bwPerTick < 1) { + return; + } + + painter.save(); + + QPen pen(Qt::white, 1, Qt::SolidLine); + painter.setPen(pen); + QFontMetrics fm(painter.font()); + + + int tick = 0; + + while (tick <= sampleRate / 2) { + + int tickpy = plotHeight / 2 - tick / bwPerPixel + y; + int tickny = plotHeight / 2 + tick / bwPerPixel + y; + + painter.drawLine(0, tickny, 30, tickny); + painter.drawLine(0, tickpy, 30, tickpy); + + if (tick != 0) { + char buf[128]; + + if (bwPerTick % 1000000 == 0) { + snprintf(buf, sizeof(buf), "-%d MHz", (int)tick / 1000000); + } else if(bwPerTick % 1000 == 0) { + snprintf(buf, sizeof(buf), "-%d kHz", tick / 1000); + } else { + snprintf(buf, sizeof(buf), "-%d Hz", tick); + } + + painter.drawText(5, tickny - 5, buf); + + buf[0] = ' '; + painter.drawText(5, tickpy + 15, buf); + } + + tick += bwPerTick; + } + + // Draw small ticks + bwPerTick /= 10; + + if (bwPerTick >= 1 ) { + tick = 0; + while (tick <= sampleRate / 2) { + + int tickpy = plotHeight / 2 - tick / bwPerPixel + y; + int tickny = plotHeight / 2 + tick / bwPerPixel + y; + + painter.drawLine(0, tickny, 3, tickny); + painter.drawLine(0, tickpy, 3, tickpy); + + tick += bwPerTick; + } + } + painter.restore(); } void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) @@ -226,6 +303,16 @@ void SpectrogramPlot::setZoomLevel(int zoom) zoomLevel = zoom; } +void SpectrogramPlot::setSampleRate(off_t rate) +{ + sampleRate = rate; +} + +void SpectrogramPlot::enableScales(bool enabled) +{ + frequencyScaleEnabled = enabled; +} + bool SpectrogramPlot::tunerEnabled() { return (tunerTransform->subscriberCount() > 0); diff --git a/spectrogramplot.h b/spectrogramplot.h index 8245421..8438b35 100644 --- a/spectrogramplot.h +++ b/spectrogramplot.h @@ -43,6 +43,8 @@ public: void paintFront(QPainter &painter, QRect &rect, range_t sampleRange) override; void paintMid(QPainter &painter, QRect &rect, range_t sampleRange) override; bool mouseEvent(QEvent::Type type, QMouseEvent event) override; + void setSampleRate(off_t sampleRate); + void enableScales(bool enabled); public slots: void setFFTSize(int size); @@ -66,6 +68,8 @@ private: int zoomLevel; float powerMax; float powerMin; + off_t sampleRate; + bool frequencyScaleEnabled; Tuner tuner; std::shared_ptr tunerTransform; @@ -78,6 +82,7 @@ private: std::vector getTunerTaps(); int linesPerTile(); bool tunerEnabled(); + void paintFrequencyScale(QPainter &painter, QRect &rect); }; class TileCacheKey