From da3557feda500c0abc98a4d89c0b853d3192a626 Mon Sep 17 00:00:00 2001 From: Hans Van Ingelgom Date: Thu, 13 Apr 2017 22:45:36 +0200 Subject: [PATCH 1/4] Add time resolution vs frequency resolution tradeoff --- mainwindow.cpp | 1 + plotview.cpp | 11 ++++++-- plotview.h | 5 ++-- spectrogramcontrols.cpp | 4 +++ spectrogramcontrols.h | 1 + spectrogramplot.cpp | 57 ++++++++++++++++++++++++++++++++++++++--- spectrogramplot.h | 2 ++ 7 files changed, 74 insertions(+), 7 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 88800f4..15cb3c6 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -46,6 +46,7 @@ MainWindow::MainWindow() connect(dock, SIGNAL(fftOrZoomChanged(int, int)), plots, SLOT(setFFTAndZoom(int, int))); connect(dock->powerMaxSlider, SIGNAL(valueChanged(int)), plots, SLOT(setPowerMax(int))); connect(dock->powerMinSlider, SIGNAL(valueChanged(int)), plots, SLOT(setPowerMin(int))); + connect(dock->timeResolutionSlider, SIGNAL(valueChanged(int)), plots, SLOT(setTimeResolution(int))); connect(dock->cursorsCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableCursors); connect(dock->scalesCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableScales); connect(dock->cursorSymbolsSpinBox, static_cast(&QSpinBox::valueChanged), plots, &PlotView::setCursorSegments); diff --git a/plotview.cpp b/plotview.cpp index c8aa80c..4a45642 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -382,7 +382,7 @@ void PlotView::setFFTAndZoom(int size, int zoom) void PlotView::setPowerMin(int power) { - powerMin = power; +// HVI_REVIEW: Not needed? powerMin = power; if (spectrogramPlot != nullptr) spectrogramPlot->setPowerMin(power); updateView(); @@ -390,12 +390,19 @@ void PlotView::setPowerMin(int power) void PlotView::setPowerMax(int power) { - powerMax = power; +// HVI_REVIEW: Not needed? powerMax = power; if (spectrogramPlot != nullptr) spectrogramPlot->setPowerMax(power); updateView(); } +void PlotView::setTimeResolution(int resolution) +{ + if (spectrogramPlot != nullptr) + spectrogramPlot->setTimeResolution(resolution); + updateView(true); +} + void PlotView::paintEvent(QPaintEvent *event) { if (mainSampleSource == nullptr) return; diff --git a/plotview.h b/plotview.h index 5cce114..5c64f0a 100644 --- a/plotview.h +++ b/plotview.h @@ -52,6 +52,7 @@ public slots: void setFFTAndZoom(int fftSize, int zoomLevel); void setPowerMin(int power); void setPowerMax(int power); + void setTimeResolution(int resolution); protected: void contextMenuEvent(QContextMenuEvent * event) override; @@ -70,8 +71,8 @@ public slots: int fftSize = 1024; int zoomLevel = 0; - int powerMin; - int powerMax; +// int powerMin; +// int powerMax; bool cursorsEnabled; off_t sampleRate = 0; bool timeScaleEnabled; diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 9db76eb..f2a4f61 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -63,6 +63,10 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent powerMinSlider->setRange(-140, 10); layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); + timeResolutionSlider = new QSlider(Qt::Horizontal, widget); + timeResolutionSlider->setRange(0, 99); + layout->addRow(new QLabel(tr("Time resolution:")), timeResolutionSlider); + scalesCheckBox = new QCheckBox(widget); scalesCheckBox->setCheckState(Qt::Checked); layout->addRow(new QLabel(tr("Scales:")), scalesCheckBox); diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index abe06d6..3829625 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -66,6 +66,7 @@ private slots: QSlider *zoomLevelSlider; QSlider *powerMaxSlider; QSlider *powerMinSlider; + QSlider *timeResolutionSlider; QCheckBox *cursorsCheckBox; QSpinBox *cursorSymbolsSpinBox; QLabel *rateLabel; diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index 31487b6..bfda1fc 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -33,12 +33,13 @@ SpectrogramPlot::SpectrogramPlot(std::shared_ptr>> src) : Plot(src), inputSource(src), fftSize(512), tuner(fftSize, this) { - setFFTSize(fftSize); zoomLevel = 1; powerMax = 0.0f; powerMin = -50.0f; + timeResolution = 0.0; sampleRate = 0; frequencyScaleEnabled = false; + setFFTSize(fftSize); for (int i = 0; i < 256; i++) { float p = (float)i / 256; @@ -271,15 +272,57 @@ std::shared_ptr SpectrogramPlot::output() return tunerTransform; } +static float bessel(float x, int kmax = 5) +{ + int k; + float kfactorial = 1.0; + float half_x_to_k_pow = 1.0; + float term; + float result = 1.0; + for(k = 1; k < kmax; k++) + { + half_x_to_k_pow *= x/2.0; + kfactorial *= k; + term = half_x_to_k_pow / kfactorial; + result += term * term; + } + return result; +} + void SpectrogramPlot::setFFTSize(int size) { float sizeScale = float(size) / float(fftSize); + float beta = 20; + float denominator = bessel(beta); + fftSize = size; fft.reset(new FFT(fftSize)); window.reset(new float[fftSize]); - for (int i = 0; i < fftSize; i++) { - window[i] = 0.5f * (1.0f - cos(Tau * i / (fftSize - 1))); + int zeroCount = (fftSize * timeResolution) / 100; + if ((zeroCount >= 0) && (zeroCount <= fftSize)) + { + int windowSize = fftSize - zeroCount; + for (int i = 0; i < windowSize; i++) { + float term = float(2*i - windowSize + 1) / windowSize; +// window[i + zeroCount/2] = bessel(beta * sqrt(1.0 - term * term)) / denominator; + window[i] = bessel(beta * sqrt(1.0 - term * term)) / denominator; +// window[i] = 0.5f * (1.0f - cos(Tau * i / (fftSize - 1))); + } +/* for(int i = 0; i < (zeroCount / 2); i++) { + window[i] = 0; + } + for(int i = zeroCount / 2 + windowSize; i < fftSize; i++) { + window[i] = 0; + } +*/ + for (int i = windowSize; i < fftSize; i++) { + window[i] = 0; + } + } else { + for(int i = 0; i < fftSize; i++) { + window[i] = 0; + } } setHeight(fftSize); @@ -301,6 +344,14 @@ void SpectrogramPlot::setPowerMin(int power) { powerMin = power; pixmapCache.clear(); + // HVI_REVIEW: Why no tunerMoved like in setPowerMax()? +} + +void SpectrogramPlot::setTimeResolution(int resolution) +{ + timeResolution = resolution; + setFFTSize(fftSize); + invalidateEvent(); } void SpectrogramPlot::setZoomLevel(int zoom) diff --git a/spectrogramplot.h b/spectrogramplot.h index 7c496ef..e1ec813 100644 --- a/spectrogramplot.h +++ b/spectrogramplot.h @@ -54,6 +54,7 @@ public slots: void setPowerMax(int power); void setPowerMin(int power); void setZoomLevel(int zoom); + void setTimeResolution(int res); void tunerMoved(); private: @@ -71,6 +72,7 @@ public slots: int zoomLevel; float powerMax; float powerMin; + int timeResolution; off_t sampleRate; bool frequencyScaleEnabled; From db14db8ee64e631b3ead423f9766689afa2b2517 Mon Sep 17 00:00:00 2001 From: Hans Van Ingelgom Date: Thu, 13 Apr 2017 23:13:08 +0200 Subject: [PATCH 2/4] Cleanup of time resolution --- plotview.cpp | 2 +- spectrogramplot.cpp | 37 +++++++++++++------------------------ 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/plotview.cpp b/plotview.cpp index 4a45642..2b2e116 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -400,7 +400,7 @@ void PlotView::setTimeResolution(int resolution) { if (spectrogramPlot != nullptr) spectrogramPlot->setTimeResolution(resolution); - updateView(true); +// updateView(true); } void PlotView::paintEvent(QPaintEvent *event) diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index bfda1fc..9432d0c 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -274,19 +274,18 @@ std::shared_ptr SpectrogramPlot::output() static float bessel(float x, int kmax = 5) { - int k; - float kfactorial = 1.0; - float half_x_to_k_pow = 1.0; - float term; - float result = 1.0; - for(k = 1; k < kmax; k++) - { - half_x_to_k_pow *= x/2.0; - kfactorial *= k; - term = half_x_to_k_pow / kfactorial; - result += term * term; - } - return result; + int k; + float kfactorial = 1.0; + float half_x_to_k_pow = 1.0; + float term; + float result = 1.0; + for(k = 1; k < kmax; k++) { + half_x_to_k_pow *= x/2.0; + kfactorial *= k; + term = half_x_to_k_pow / kfactorial; + result += term * term; + } + return result; } void SpectrogramPlot::setFFTSize(int size) @@ -300,22 +299,12 @@ void SpectrogramPlot::setFFTSize(int size) window.reset(new float[fftSize]); int zeroCount = (fftSize * timeResolution) / 100; - if ((zeroCount >= 0) && (zeroCount <= fftSize)) - { + if ((zeroCount >= 0) && (zeroCount <= fftSize)) { int windowSize = fftSize - zeroCount; for (int i = 0; i < windowSize; i++) { float term = float(2*i - windowSize + 1) / windowSize; -// window[i + zeroCount/2] = bessel(beta * sqrt(1.0 - term * term)) / denominator; window[i] = bessel(beta * sqrt(1.0 - term * term)) / denominator; -// window[i] = 0.5f * (1.0f - cos(Tau * i / (fftSize - 1))); - } -/* for(int i = 0; i < (zeroCount / 2); i++) { - window[i] = 0; - } - for(int i = zeroCount / 2 + windowSize; i < fftSize; i++) { - window[i] = 0; } -*/ for (int i = windowSize; i < fftSize; i++) { window[i] = 0; } From c36381bb533fb3db5ad3dfb8fc36837138ce79c5 Mon Sep 17 00:00:00 2001 From: Hans Van Ingelgom Date: Sat, 15 Apr 2017 09:49:27 +0200 Subject: [PATCH 3/4] added beta parameter slider for Kaiser window --- mainwindow.cpp | 1 + plotview.cpp | 7 ++++++- plotview.h | 1 + spectrogramcontrols.cpp | 4 ++++ spectrogramcontrols.h | 1 + spectrogramplot.cpp | 8 +++++++- spectrogramplot.h | 2 ++ 7 files changed, 22 insertions(+), 2 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 15cb3c6..131e33b 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -47,6 +47,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->timeResolutionSlider, SIGNAL(valueChanged(int)), plots, SLOT(setTimeResolution(int))); + connect(dock->betaSlider, SIGNAL(valueChanged(int)), plots, SLOT(setBeta(int))); connect(dock->cursorsCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableCursors); connect(dock->scalesCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableScales); connect(dock->cursorSymbolsSpinBox, static_cast(&QSpinBox::valueChanged), plots, &PlotView::setCursorSegments); diff --git a/plotview.cpp b/plotview.cpp index 2b2e116..108c800 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -400,7 +400,12 @@ void PlotView::setTimeResolution(int resolution) { if (spectrogramPlot != nullptr) spectrogramPlot->setTimeResolution(resolution); -// updateView(true); +} + +void PlotView::setBeta(int beta) +{ + if (spectrogramPlot != nullptr) + spectrogramPlot->setBeta(beta); } void PlotView::paintEvent(QPaintEvent *event) diff --git a/plotview.h b/plotview.h index 5c64f0a..28220de 100644 --- a/plotview.h +++ b/plotview.h @@ -53,6 +53,7 @@ public slots: void setPowerMin(int power); void setPowerMax(int power); void setTimeResolution(int resolution); + void setBeta(int beta); protected: void contextMenuEvent(QContextMenuEvent * event) override; diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index f2a4f61..890045f 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -67,6 +67,10 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent timeResolutionSlider->setRange(0, 99); layout->addRow(new QLabel(tr("Time resolution:")), timeResolutionSlider); + betaSlider = new QSlider(Qt::Horizontal, widget); + betaSlider->setRange(0, 15); + layout->addRow(new QLabel(tr("Beta:")), betaSlider); + scalesCheckBox = new QCheckBox(widget); scalesCheckBox->setCheckState(Qt::Checked); layout->addRow(new QLabel(tr("Scales:")), scalesCheckBox); diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index 3829625..1372bfe 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -67,6 +67,7 @@ private slots: QSlider *powerMaxSlider; QSlider *powerMinSlider; QSlider *timeResolutionSlider; + QSlider *betaSlider; QCheckBox *cursorsCheckBox; QSpinBox *cursorSymbolsSpinBox; QLabel *rateLabel; diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index 9432d0c..a780350 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -38,6 +38,7 @@ SpectrogramPlot::SpectrogramPlot(std::shared_ptrbeta = beta; + setFFTSize(fftSize); + invalidateEvent(); +} void SpectrogramPlot::setZoomLevel(int zoom) { zoomLevel = zoom; diff --git a/spectrogramplot.h b/spectrogramplot.h index e1ec813..cace493 100644 --- a/spectrogramplot.h +++ b/spectrogramplot.h @@ -55,6 +55,7 @@ public slots: void setPowerMin(int power); void setZoomLevel(int zoom); void setTimeResolution(int res); + void setBeta(float beta); void tunerMoved(); private: @@ -73,6 +74,7 @@ public slots: float powerMax; float powerMin; int timeResolution; + float beta; off_t sampleRate; bool frequencyScaleEnabled; From 3e5c9e0c30d3260e4d596cc4486ae788ddbace91 Mon Sep 17 00:00:00 2001 From: Hans Van Ingelgom Date: Sat, 15 Apr 2017 13:47:20 +0200 Subject: [PATCH 4/4] Moved sliders, using liquid-dsp kaiser implementation --- plotview.cpp | 4 ++-- plotview.h | 4 ++-- spectrogramcontrols.cpp | 16 ++++++++-------- spectrogramplot.cpp | 28 +++++++--------------------- 4 files changed, 19 insertions(+), 33 deletions(-) diff --git a/plotview.cpp b/plotview.cpp index 108c800..2fc1fe1 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -382,7 +382,7 @@ void PlotView::setFFTAndZoom(int size, int zoom) void PlotView::setPowerMin(int power) { -// HVI_REVIEW: Not needed? powerMin = power; + powerMin = power; if (spectrogramPlot != nullptr) spectrogramPlot->setPowerMin(power); updateView(); @@ -390,7 +390,7 @@ void PlotView::setPowerMin(int power) void PlotView::setPowerMax(int power) { -// HVI_REVIEW: Not needed? powerMax = power; + powerMax = power; if (spectrogramPlot != nullptr) spectrogramPlot->setPowerMax(power); updateView(); diff --git a/plotview.h b/plotview.h index 28220de..98cc901 100644 --- a/plotview.h +++ b/plotview.h @@ -72,8 +72,8 @@ public slots: int fftSize = 1024; int zoomLevel = 0; -// int powerMin; -// int powerMax; + int powerMin; + int powerMax; bool cursorsEnabled; off_t sampleRate = 0; bool timeScaleEnabled; diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 890045f..f0396cf 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -55,14 +55,6 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent layout->addRow(new QLabel(tr("Zoom:")), zoomLevelSlider); - powerMaxSlider = new QSlider(Qt::Horizontal, widget); - powerMaxSlider->setRange(-140, 10); - layout->addRow(new QLabel(tr("Power max:")), powerMaxSlider); - - powerMinSlider = new QSlider(Qt::Horizontal, widget); - powerMinSlider->setRange(-140, 10); - layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); - timeResolutionSlider = new QSlider(Qt::Horizontal, widget); timeResolutionSlider->setRange(0, 99); layout->addRow(new QLabel(tr("Time resolution:")), timeResolutionSlider); @@ -71,6 +63,14 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent betaSlider->setRange(0, 15); layout->addRow(new QLabel(tr("Beta:")), betaSlider); + powerMaxSlider = new QSlider(Qt::Horizontal, widget); + powerMaxSlider->setRange(-140, 10); + layout->addRow(new QLabel(tr("Power max:")), powerMaxSlider); + + powerMinSlider = new QSlider(Qt::Horizontal, widget); + powerMinSlider->setRange(-140, 10); + layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); + scalesCheckBox = new QCheckBox(widget); scalesCheckBox->setCheckState(Qt::Checked); layout->addRow(new QLabel(tr("Scales:")), scalesCheckBox); diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index a780350..f22d296 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -273,26 +273,9 @@ std::shared_ptr SpectrogramPlot::output() return tunerTransform; } -static float bessel(float x, int kmax = 5) -{ - int k; - float kfactorial = 1.0; - float half_x_to_k_pow = 1.0; - float term; - float result = 1.0; - for(k = 1; k < kmax; k++) { - half_x_to_k_pow *= x/2.0; - kfactorial *= k; - term = half_x_to_k_pow / kfactorial; - result += term * term; - } - return result; -} - void SpectrogramPlot::setFFTSize(int size) { float sizeScale = float(size) / float(fftSize); - float denominator = bessel(beta); fftSize = size; fft.reset(new FFT(fftSize)); @@ -301,11 +284,15 @@ void SpectrogramPlot::setFFTSize(int size) int zeroCount = (fftSize * timeResolution) / 100; if ((zeroCount >= 0) && (zeroCount <= fftSize)) { int windowSize = fftSize - zeroCount; + int leadingZeroCount = zeroCount/2; + + for (int i = 0; i < leadingZeroCount; i++) { + window[i] = 0; + } for (int i = 0; i < windowSize; i++) { - float term = float(2*i - windowSize + 1) / windowSize; - window[i] = bessel(beta * sqrt(1.0 - term * term)) / denominator; + window[i + leadingZeroCount] = kaiser(i, windowSize, beta, 0.0); } - for (int i = windowSize; i < fftSize; i++) { + for (int i = windowSize + leadingZeroCount; i < fftSize; i++) { window[i] = 0; } } else { @@ -333,7 +320,6 @@ void SpectrogramPlot::setPowerMin(int power) { powerMin = power; pixmapCache.clear(); - // HVI_REVIEW: Why no tunerMoved like in setPowerMax()? } void SpectrogramPlot::setTimeResolution(int resolution)