diff --git a/src/inputsource.cpp b/src/inputsource.cpp index ebbf749..e0f0882 100644 --- a/src/inputsource.cpp +++ b/src/inputsource.cpp @@ -38,6 +38,7 @@ #include #include #include +#include class ComplexF32SampleAdapter : public SampleAdapter { @@ -264,7 +265,9 @@ void InputSource::readMetaData(const QString &filename) auto datatype = global["core:datatype"].toString(); - if (datatype.compare("cf32_le") == 0) { + if (datatype.compare("cf64_le") == 0) { + sampleAdapter = std::make_unique(); + } else if (datatype.compare("cf32_le") == 0) { sampleAdapter = std::make_unique(); } else if (datatype.compare("ci32_le") == 0) { sampleAdapter = std::make_unique(); @@ -339,10 +342,23 @@ void InputSource::readMetaData(const QString &filename) auto frequencyRange = range_t{freq_lower_edge, freq_upper_edge}; auto label = sigmf_annotation["core:label"].toString(); + if (label.isEmpty()) { + label = sigmf_annotation["core:description"].toString(); + } auto comment = sigmf_annotation["core:comment"].toString(); - annotationList.emplace_back(sampleRange, frequencyRange, label, comment); + auto sigmf_color = sigmf_annotation["presentation:color"].toString(); + // SigMF uses the format "#RRGGBBAA" for alpha-channel colors, QT uses "#AARRGGBB" + if ((sigmf_color.at(0) == '#') && (sigmf_color.length()) == 9) { + sigmf_color = "#" + sigmf_color.mid(7,2) + sigmf_color.mid(1,6); + } + auto boxColor = QString::fromStdString("white"); + if (QColor::isValidColor(sigmf_color)) { + boxColor = sigmf_color; + } + + annotationList.emplace_back(sampleRange, frequencyRange, label, comment, boxColor); } } } @@ -397,13 +413,8 @@ void InputSource::openFile(const char *filename) annotationList.clear(); QString metaFilename; - if (suffix == "sigmf-meta") { + if (suffix == "sigmf-meta" || suffix == "sigmf-data" || suffix == "sigmf-") { dataFilename = fileInfo.path() + "/" + fileInfo.completeBaseName() + ".sigmf-data"; - metaFilename = filename; - readMetaData(metaFilename); - } - else if (suffix == "sigmf-data") { - dataFilename = filename; metaFilename = fileInfo.path() + "/" + fileInfo.completeBaseName() + ".sigmf-meta"; readMetaData(metaFilename); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index dc62d76..da6bfe6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -52,7 +52,9 @@ MainWindow::MainWindow() connect(dock->scalesCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableScales); connect(dock->annosCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableAnnotations); connect(dock->annosCheckBox, &QCheckBox::stateChanged, dock, &SpectrogramControls::enableAnnotations); + connect(dock->annoLabelCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableAnnoLabels); connect(dock->commentsCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableAnnotationCommentsTooltips); + connect(dock->annoColorCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableAnnoColors); connect(dock->cursorSymbolsSpinBox, static_cast(&QSpinBox::valueChanged), plots, &PlotView::setCursorSegments); // Connect dock outputs diff --git a/src/plotview.cpp b/src/plotview.cpp index d1c6bea..d05b33c 100644 --- a/src/plotview.cpp +++ b/src/plotview.cpp @@ -51,6 +51,8 @@ PlotView::PlotView(InputSource *input) : cursors(this), viewRange({0, 0}) enableScales(true); enableAnnotations(true); + enableAnnoLabels(true); + enableAnnoColors(true); enableAnnotationCommentsTooltips(true); addPlot(spectrogramPlot); @@ -425,6 +427,7 @@ void PlotView::setCursorSegments(int segments) selectedSamples.maximum = selectedSamples.minimum + (segments * sampPerSeg + 0.5f); cursors.setSegments(segments); + updateView(); emitTimeSelection(); } @@ -432,6 +435,9 @@ void PlotView::setCursorSegments(int segments) void PlotView::setFFTAndZoom(int size, int zoom) { auto oldSamplesPerColumn = samplesPerColumn(); + float oldPlotCenter = (verticalScrollBar()->value() + viewport()->height() / 2.0) / plotsHeight(); + if (verticalScrollBar()->maximum() == 0) + oldPlotCenter = 0.5; // Set new FFT size fftSize = size; @@ -439,15 +445,22 @@ void PlotView::setFFTAndZoom(int size, int zoom) spectrogramPlot->setFFTSize(size); // Set new zoom level - zoomLevel = zoom; - if (spectrogramPlot != nullptr) - spectrogramPlot->setZoomLevel(zoom); + zoomLevel = std::max(1,zoom); + nfftSkip = std::max(1,-1*zoom); + if (spectrogramPlot != nullptr) { + spectrogramPlot->setZoomLevel(zoomLevel); + spectrogramPlot->setSkip(nfftSkip); + } // Update horizontal (time) scrollbar horizontalScrollBar()->setSingleStep(10); horizontalScrollBar()->setPageStep(100); updateView(true, samplesPerColumn() < oldSamplesPerColumn); + + // maintain the relative position of the vertical scroll bar + if (verticalScrollBar()->maximum()) + verticalScrollBar()->setValue((int )(oldPlotCenter * plotsHeight() - viewport()->height() / 2.0 + 0.5f)); } void PlotView::setPowerMin(int power) @@ -568,7 +581,7 @@ void PlotView::resizeEvent(QResizeEvent * event) size_t PlotView::samplesPerColumn() { - return fftSize / zoomLevel; + return fftSize * nfftSkip / zoomLevel; } void PlotView::scrollContentsBy(int dx, int dy) @@ -604,6 +617,7 @@ void PlotView::updateView(bool reCenter, bool expanding) } horizontalScrollBar()->setMaximum(std::max(0, sampleToColumn(mainSampleSource->count()) - width())); verticalScrollBar()->setMaximum(std::max(0, plotsHeight() - viewport()->height())); + if (expanding) { updateViewRange(reCenter); } @@ -647,6 +661,14 @@ void PlotView::enableAnnotations(bool enabled) viewport()->update(); } +void PlotView::enableAnnoLabels(bool enabled) +{ + if (spectrogramPlot != nullptr) + spectrogramPlot->enableAnnoLabels(enabled); + + viewport()->update(); +} + void PlotView::enableAnnotationCommentsTooltips(bool enabled) { annotationCommentsEnabled = enabled; @@ -654,6 +676,14 @@ void PlotView::enableAnnotationCommentsTooltips(bool enabled) viewport()->update(); } +void PlotView::enableAnnoColors(bool enabled) +{ + if (spectrogramPlot != nullptr) + spectrogramPlot->enableAnnoColors(enabled); + + viewport()->update(); +} + int PlotView::sampleToColumn(size_t sample) { return sample / samplesPerColumn(); diff --git a/src/plotview.h b/src/plotview.h index 326d547..c80ff1c 100644 --- a/src/plotview.h +++ b/src/plotview.h @@ -47,7 +47,9 @@ public slots: void enableCursors(bool enabled); void enableScales(bool enabled); void enableAnnotations(bool enabled); + void enableAnnoLabels(bool enabled); void enableAnnotationCommentsTooltips(bool enabled); + void enableAnnoColors(bool enabled); void invalidateEvent() override; void repaint(); void setCursorSegments(int segments); @@ -77,6 +79,7 @@ public slots: int fftSize = 1024; int zoomLevel = 1; + int nfftSkip = 1; int powerMin; int powerMax; bool cursorsEnabled; diff --git a/src/samplesource.h b/src/samplesource.h index 3f0b299..58789f6 100644 --- a/src/samplesource.h +++ b/src/samplesource.h @@ -26,6 +26,7 @@ #include "util.h" #include #include +#include class Annotation { @@ -34,11 +35,12 @@ class Annotation range_t frequencyRange; QString label; QString comment; + QColor boxColor; Annotation(range_t sampleRange, range_tfrequencyRange, QString label, - QString comment) + QString comment, QColor boxColor) : sampleRange(sampleRange), frequencyRange(frequencyRange), label(label), - comment(comment) {} + comment(comment), boxColor(boxColor) {} }; template diff --git a/src/spectrogramcontrols.cpp b/src/spectrogramcontrols.cpp index 0e0f9aa..b2fa380 100644 --- a/src/spectrogramcontrols.cpp +++ b/src/spectrogramcontrols.cpp @@ -46,13 +46,13 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent layout->addRow(new QLabel(tr("Spectrogram"))); fftSizeSlider = new QSlider(Qt::Horizontal, widget); - fftSizeSlider->setRange(4, 13); + fftSizeSlider->setRange(4, 16); fftSizeSlider->setPageStep(1); layout->addRow(new QLabel(tr("FFT size:")), fftSizeSlider); zoomLevelSlider = new QSlider(Qt::Horizontal, widget); - zoomLevelSlider->setRange(0, 10); + zoomLevelSlider->setRange(-6, 10); zoomLevelSlider->setPageStep(1); layout->addRow(new QLabel(tr("Zoom:")), zoomLevelSlider); @@ -99,8 +99,12 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent annosCheckBox = new QCheckBox(widget); layout->addRow(new QLabel(tr("Display Annotations:")), annosCheckBox); + annoLabelCheckBox = new QCheckBox(widget); + layout->addRow(new QLabel(tr("Annotation Labels:")), annoLabelCheckBox); commentsCheckBox = new QCheckBox(widget); - layout->addRow(new QLabel(tr("Display annotation comments tooltips:")), commentsCheckBox); + layout->addRow(new QLabel(tr("Annotation comments:")), commentsCheckBox); + annoColorCheckBox = new QCheckBox(widget); + layout->addRow(new QLabel(tr("Annotation Colors:")), annoColorCheckBox); widget->setLayout(layout); setWidget(widget); @@ -136,7 +140,8 @@ void SpectrogramControls::setDefaults() cursorSymbolsSpinBox->setValue(1); annosCheckBox->setCheckState(Qt::Checked); - commentsCheckBox->setCheckState(Qt::Checked); + annoLabelCheckBox->setCheckState(Qt::Checked); + annoColorCheckBox->setCheckState(Qt::Checked); // Try to set the sample rate from the last-used value QSettings settings; @@ -151,7 +156,13 @@ void SpectrogramControls::setDefaults() void SpectrogramControls::fftOrZoomChanged(void) { int fftSize = pow(2, fftSizeSlider->value()); - int zoomLevel = std::min(fftSize, (int)pow(2, zoomLevelSlider->value())); + int zoomLevel = zoomLevelSlider->value(); + if (zoomLevel >= 0) + // zooming in by power-of-two steps + zoomLevel = std::min(fftSize, (int)pow(2, zoomLevel)); + else + // zooming out (skipping FFTs) by power-of-two steps + zoomLevel = -1*std::min(fftSize, (int)pow(2, -1*zoomLevel)); emit fftOrZoomChanged(fftSize, zoomLevel); } diff --git a/src/spectrogramcontrols.h b/src/spectrogramcontrols.h index 69e7d60..e0c69ca 100644 --- a/src/spectrogramcontrols.h +++ b/src/spectrogramcontrols.h @@ -75,5 +75,7 @@ private slots: QLabel *symbolPeriodLabel; QCheckBox *scalesCheckBox; QCheckBox *annosCheckBox; + QCheckBox *annoLabelCheckBox; QCheckBox *commentsCheckBox; + QCheckBox *annoColorCheckBox; }; diff --git a/src/spectrogramplot.cpp b/src/spectrogramplot.cpp index 3ab57ec..9a2dd4f 100644 --- a/src/spectrogramplot.cpp +++ b/src/spectrogramplot.cpp @@ -35,11 +35,14 @@ SpectrogramPlot::SpectrogramPlot(std::shared_ptr s QPixmap* SpectrogramPlot::getPixmapTile(size_t tile) { - QPixmap *obj = pixmapCache.object(TileCacheKey(fftSize, zoomLevel, tile)); + QPixmap *obj = pixmapCache.object(TileCacheKey(fftSize, zoomLevel, nfftSkip, tile)); if (obj != 0) return obj; @@ -261,13 +269,13 @@ QPixmap* SpectrogramPlot::getPixmapTile(size_t tile) } } obj->convertFromImage(image); - pixmapCache.insert(TileCacheKey(fftSize, zoomLevel, tile), obj); + pixmapCache.insert(TileCacheKey(fftSize, zoomLevel, nfftSkip, tile), obj); return obj; } float* SpectrogramPlot::getFFTTile(size_t tile) { - std::array* obj = fftCache.object(TileCacheKey(fftSize, zoomLevel, tile)); + std::array* obj = fftCache.object(TileCacheKey(fftSize, zoomLevel, nfftSkip, tile)); if (obj != nullptr) return obj->data(); @@ -279,7 +287,7 @@ float* SpectrogramPlot::getFFTTile(size_t tile) sample += getStride(); ptr += fftSize; } - fftCache.insert(TileCacheKey(fftSize, zoomLevel, tile), destStorage); + fftCache.insert(TileCacheKey(fftSize, zoomLevel, nfftSkip, tile), destStorage); return destStorage->data(); } @@ -316,7 +324,7 @@ void SpectrogramPlot::getLine(float *dest, size_t sample) int SpectrogramPlot::getStride() { - return fftSize / zoomLevel; + return fftSize * nfftSkip / zoomLevel; } float SpectrogramPlot::getTunerPhaseInc() @@ -397,6 +405,11 @@ void SpectrogramPlot::setZoomLevel(int zoom) zoomLevel = zoom; } +void SpectrogramPlot::setSkip(int skip) +{ + nfftSkip = skip; +} + void SpectrogramPlot::setSampleRate(double rate) { sampleRate = rate; @@ -417,6 +430,16 @@ bool SpectrogramPlot::isAnnotationsEnabled(void) return sigmfAnnotationsEnabled; } +void SpectrogramPlot::enableAnnoLabels(bool enabled) +{ + sigmfAnnotationLabels = enabled; +} + +void SpectrogramPlot::enableAnnoColors(bool enabled) +{ + sigmfAnnotationColors = enabled; +} + bool SpectrogramPlot::tunerEnabled() { return (tunerTransform->subscriberCount() > 0); diff --git a/src/spectrogramplot.h b/src/spectrogramplot.h index 2e52cb4..cf9d1b0 100644 --- a/src/spectrogramplot.h +++ b/src/spectrogramplot.h @@ -52,7 +52,9 @@ class SpectrogramPlot : public Plot bool tunerEnabled(); void enableScales(bool enabled); void enableAnnotations(bool enabled); + void enableAnnoLabels(bool enabled); bool isAnnotationsEnabled(); + void enableAnnoColors(bool enabled); QString *mouseAnnotationComment(const QMouseEvent *event); public slots: @@ -60,6 +62,7 @@ public slots: void setPowerMax(int power); void setPowerMin(int power); void setZoomLevel(int zoom); + void setSkip(int skip); void tunerMoved(); private: @@ -76,11 +79,14 @@ public slots: int fftSize; int zoomLevel; + int nfftSkip; float powerMax; float powerMin; double sampleRate; bool frequencyScaleEnabled; bool sigmfAnnotationsEnabled; + bool sigmfAnnotationLabels; + bool sigmfAnnotationColors; Tuner tuner; std::shared_ptr tunerTransform; @@ -100,20 +106,23 @@ class TileCacheKey { public: - TileCacheKey(int fftSize, int zoomLevel, size_t sample) { + TileCacheKey(int fftSize, int zoomLevel, int nfftSkip, size_t sample) { this->fftSize = fftSize; this->zoomLevel = zoomLevel; + this->nfftSkip = nfftSkip; this->sample = sample; } bool operator==(const TileCacheKey &k2) const { return (this->fftSize == k2.fftSize) && (this->zoomLevel == k2.zoomLevel) && + (this->nfftSkip == k2.nfftSkip) && (this->sample == k2.sample); } int fftSize; int zoomLevel; + int nfftSkip; size_t sample; };