From e2f4eb8b7484a2ebc1cc4c9082753c4953510f3a Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 21 Oct 2015 23:42:51 +0100 Subject: [PATCH 01/99] WIP --- CMakeLists.txt | 1 + mainwindow.cpp | 19 +++++++++ mainwindow.h | 4 ++ spectrogram.h | 2 +- spectrogramcontrols.cpp | 2 +- waveformview.cpp | 87 +++++++++++++++++++++++++++++++++++++++++ waveformview.h | 44 +++++++++++++++++++++ 7 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 waveformview.cpp create mode 100644 waveformview.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 74d60dc..cdbd1cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ list(APPEND inspectrum_sources inputsource.cpp spectrogram.cpp spectrogramcontrols.cpp + waveformview.cpp ) INCLUDE(FindPkgConfig) diff --git a/mainwindow.cpp b/mainwindow.cpp index c66b29d..a5b2d33 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -32,6 +32,9 @@ MainWindow::MainWindow() dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); addDockWidget(Qt::LeftDockWidgetArea, dock); + wave = new WaveformView(); + addDockWidget(Qt::BottomDockWidgetArea, wave); + connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); connect(dock->sampleRate, SIGNAL(textChanged(QString)), this, SLOT(setSampleRate(QString))); connect(dock, SIGNAL(fftSizeChanged(int)), this, SLOT(setFFTSize(int))); @@ -78,6 +81,7 @@ void MainWindow::setFFTSize(int size) off_t sample = getCenterSample(); spectrogram.setFFTSize(size); scrollArea.verticalScrollBar()->setValue(getScrollPos(sample)); + wave->viewChanged(getTopSample(), getBottomSample()); } void MainWindow::setZoomLevel(int zoom) @@ -85,6 +89,13 @@ void MainWindow::setZoomLevel(int zoom) off_t sample = getCenterSample(); spectrogram.setZoomLevel(zoom); scrollArea.verticalScrollBar()->setValue(getScrollPos(sample)); + wave->viewChanged(getTopSample(), getBottomSample()); +} + +off_t MainWindow::getTopSample() +{ + int height = scrollArea.height(); + return scrollArea.verticalScrollBar()->value() * spectrogram.getStride(); } off_t MainWindow::getCenterSample() @@ -93,6 +104,12 @@ off_t MainWindow::getCenterSample() return (scrollArea.verticalScrollBar()->value() + height / 2) * spectrogram.getStride(); } +off_t MainWindow::getBottomSample() +{ + int height = scrollArea.height(); + return (scrollArea.verticalScrollBar()->value() + height) * spectrogram.getStride(); +} + int MainWindow::getScrollPos(off_t sample) { int height = scrollArea.height(); @@ -104,4 +121,6 @@ void MainWindow::openFile(QString fileName) QString title="%1: %2"; this->setWindowTitle(title.arg(QApplication::applicationName(),fileName.section('/',-1,-1))); spectrogram.openFile(fileName); + wave->inputSourceChanged(spectrogram.inputSource); + wave->viewChanged(getTopSample(), getBottomSample()); } diff --git a/mainwindow.h b/mainwindow.h index bdc53a4..d2cecfb 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -23,6 +23,7 @@ #include #include "spectrogram.h" #include "spectrogramcontrols.h" +#include "waveformview.h" class MainWindow : public QMainWindow { @@ -45,7 +46,10 @@ private: QScrollArea scrollArea; Spectrogram spectrogram; SpectrogramControls *dock; + WaveformView *wave; + off_t getTopSample(); off_t getCenterSample(); + off_t getBottomSample(); int getScrollPos(off_t sample); }; diff --git a/spectrogram.h b/spectrogram.h index 91d2713..bc8bc4e 100644 --- a/spectrogram.h +++ b/spectrogram.h @@ -40,6 +40,7 @@ public: int getHeight(); int getStride(); + InputSource *inputSource = nullptr; public slots: void openFile(QString fileName); void setSampleRate(int rate); @@ -56,7 +57,6 @@ private: const int linesPerGraduation = 50; const int tileSize = 65536; // This must be a multiple of the maximum FFT size - InputSource *inputSource = nullptr; FFT *fft = nullptr; std::unique_ptr window; fftwf_complex *lineBuffer = nullptr; diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 877ffc8..132d66d 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -42,7 +42,7 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent layout->addRow(new QLabel(tr("FFT size:")), fftSizeSlider); zoomLevelSlider = new QSlider(Qt::Horizontal, widget); - zoomLevelSlider->setRange(0, 5); + zoomLevelSlider->setRange(0, 10); zoomLevelSlider->setValue(0); layout->addRow(new QLabel(tr("Zoom:")), zoomLevelSlider); diff --git a/waveformview.cpp b/waveformview.cpp new file mode 100644 index 0000000..3be2e04 --- /dev/null +++ b/waveformview.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2015, 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 "waveformview.h" +#include +#include + +WaveformView::WaveformView() +{ + for (int i = 0; i < 128; i++) { + colormap[i] = qRgb(i/2, i*1.5, i*1.5); + } + for (int i = 128; i < 256; i++) { + colormap[i] = qRgb(64 + (i-128)*1.5, 192+(i-128)/2, 192+(i-128)/2); + } +} + +void WaveformView::inputSourceChanged(InputSource *input) +{ + inputSource = input; + update(); +} + +void WaveformView::viewChanged(off_t firstSample, off_t lastSample) +{ + this->firstSample = firstSample; + this->lastSample = lastSample; + qDebug() << "viewChanged(" << firstSample << ", " << lastSample << ")"; + update(); +} + +void WaveformView::paintEvent(QPaintEvent *event) +{ + if (inputSource == nullptr) return; + if (lastSample - firstSample <= 0) return; + + QRect rect = QRect(0, 0, width(), height()); + QPainter painter(this); + painter.fillRect(rect, Qt::black); + + off_t length = lastSample - firstSample; + fftwf_complex *samples = new fftwf_complex[length]; + inputSource->getSamples(samples, firstSample, length); + for (int iq = 0; iq < 2; iq++) { + switch (iq) { + case 0: + painter.setPen(Qt::red); + break; + case 1: + painter.setPen(Qt::blue); + break; + } + int xprev = 0; + int yprev = 0; + for (off_t i = 0; i < length; i++) { + int x = (float)i / length * rect.width(); + int y = (samples[i][iq] * 50 * 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, yprev, x, y); + xprev = x; + yprev = y; + } + } + + delete[] samples; +} \ No newline at end of file diff --git a/waveformview.h b/waveformview.h new file mode 100644 index 0000000..b91a215 --- /dev/null +++ b/waveformview.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015, 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 "inputsource.h" + +class WaveformView : public QDockWidget { + Q_OBJECT + +public: + WaveformView(); + +public slots: + void inputSourceChanged(InputSource *input); + void viewChanged(off_t firstSample, off_t lastSample); + +protected: + void paintEvent(QPaintEvent *event); + +private: + InputSource *inputSource = nullptr; + off_t firstSample = 0; + off_t lastSample = 0; + QRgb colormap[255]; +}; \ No newline at end of file From 7c7ff6edd666a58b7af7c71192416308b9ebe7f5 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 8 Nov 2015 20:38:26 +0000 Subject: [PATCH 02/99] Add generic SampleSource interface --- samplesource.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 samplesource.h diff --git a/samplesource.h b/samplesource.h new file mode 100644 index 0000000..1682337 --- /dev/null +++ b/samplesource.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015, 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 + +class SampleSource +{ + +public: + virtual ~SampleSource() {}; + + virtual bool getSamples(std::complex *dest, off_t start, off_t length); + virtual off_t count() = 0; +}; From c83892d432620af27b8c7e3faec27fae532e5eb2 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 8 Nov 2015 20:43:13 +0000 Subject: [PATCH 03/99] Convert InputSource to SampleSource interface --- inputsource.cpp | 8 ++++---- inputsource.h | 13 ++++++------- samplesource.h | 2 +- spectrogram.cpp | 12 ++++++------ spectrogram.h | 1 + waveformview.cpp | 4 ++-- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/inputsource.cpp b/inputsource.cpp index ca03fd3..2cb1c31 100644 --- a/inputsource.cpp +++ b/inputsource.cpp @@ -36,9 +36,9 @@ InputSource::InputSource(const char *filename) { if (fstat(fileno(m_file), &sb) != 0) throw std::runtime_error("Error fstating file"); m_file_size = sb.st_size; - sampleCount = m_file_size / sizeof(fftwf_complex); + sampleCount = m_file_size / sizeof(std::complex); - m_data = (fftwf_complex*)mmap(NULL, m_file_size, PROT_READ, MAP_SHARED, fileno(m_file), 0); + m_data = (std::complex*)mmap(NULL, m_file_size, PROT_READ, MAP_SHARED, fileno(m_file), 0); if (m_data == 0) throw std::runtime_error("Error mmapping file"); } @@ -48,7 +48,7 @@ InputSource::~InputSource() { fclose(m_file); } -bool InputSource::getSamples(fftwf_complex *dest, off_t start, int length) +bool InputSource::getSamples(std::complex *dest, off_t start, off_t length) { if(start < 0 || length < 0) return false; @@ -56,6 +56,6 @@ bool InputSource::getSamples(fftwf_complex *dest, off_t start, int length) if (start + length >= sampleCount) return false; - memcpy(dest, &m_data[start], length * sizeof(fftwf_complex)); + memcpy(dest, &m_data[start], length * sizeof(std::complex)); return true; } diff --git a/inputsource.h b/inputsource.h index ad310f2..d3e3bd6 100644 --- a/inputsource.h +++ b/inputsource.h @@ -19,22 +19,21 @@ #pragma once -#include "fft.h" -#include -#include +#include +#include "samplesource.h" -class InputSource +class InputSource : public SampleSource { private: FILE *m_file; off_t m_file_size; off_t sampleCount; - fftwf_complex *m_data; + std::complex *m_data; public: InputSource(const char *filename); ~InputSource(); - bool getSamples(fftwf_complex *dest, off_t start, int length); - off_t getSampleCount() { return sampleCount; }; + bool getSamples(std::complex *dest, off_t start, off_t length); + off_t count() { return sampleCount; }; }; diff --git a/samplesource.h b/samplesource.h index 1682337..523a43f 100644 --- a/samplesource.h +++ b/samplesource.h @@ -27,6 +27,6 @@ class SampleSource public: virtual ~SampleSource() {}; - virtual bool getSamples(std::complex *dest, off_t start, off_t length); + virtual bool getSamples(std::complex *dest, off_t start, off_t length) = 0; virtual off_t count() = 0; }; diff --git a/spectrogram.cpp b/spectrogram.cpp index c70f201..f756473 100644 --- a/spectrogram.cpp +++ b/spectrogram.cpp @@ -143,19 +143,19 @@ float* Spectrogram::getFFTTile(off_t tile) void Spectrogram::getLine(float *dest, off_t sample) { if (inputSource && fft) { - fftwf_complex buffer[fftSize]; + std::complex buffer[fftSize]; inputSource->getSamples(buffer, sample, fftSize); for (int i = 0; i < fftSize; i++) { - buffer[i][0] *= window[i]; - buffer[i][1] *= window[i]; + buffer[i].real(buffer[i].real() * window[i]); + buffer[i].imag(buffer[i].imag() * window[i]); } fft->process(buffer, buffer); for (int i = 0; i < fftSize; i++) { int k = (i + fftSize / 2) % fftSize; - float re = buffer[k][0]; - float im = buffer[k][1]; + float re = buffer[k].real(); + float im = buffer[k].imag(); float mag = sqrt(re * re + im * im) / fftSize; float magdb = 10 * log2(mag) / log2(10); *dest = magdb; @@ -227,7 +227,7 @@ int Spectrogram::getHeight() if (!inputSource) return 0; - return inputSource->getSampleCount() / getStride(); + return inputSource->count() / getStride(); } int Spectrogram::getStride() diff --git a/spectrogram.h b/spectrogram.h index bc8bc4e..44f278b 100644 --- a/spectrogram.h +++ b/spectrogram.h @@ -24,6 +24,7 @@ #include "fft.h" #include "inputsource.h" +#include #include static const double Tau = M_PI * 2.0; diff --git a/waveformview.cpp b/waveformview.cpp index 3be2e04..a677867 100644 --- a/waveformview.cpp +++ b/waveformview.cpp @@ -55,7 +55,7 @@ void WaveformView::paintEvent(QPaintEvent *event) painter.fillRect(rect, Qt::black); off_t length = lastSample - firstSample; - fftwf_complex *samples = new fftwf_complex[length]; + std::complex *samples = new std::complex[length]; inputSource->getSamples(samples, firstSample, length); for (int iq = 0; iq < 2; iq++) { switch (iq) { @@ -70,7 +70,7 @@ void WaveformView::paintEvent(QPaintEvent *event) int yprev = 0; for (off_t i = 0; i < length; i++) { int x = (float)i / length * rect.width(); - int y = (samples[i][iq] * 50 * rect.height()/2) + rect.height()/2; + int y = (samples[i].real() * 50 * rect.height()/2) + rect.height()/2; if (x < 0) x = 0; if (y < 0) y = 0; From 20d4b0c052ba7395e9a55104abd62c1754f151e7 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Tue, 10 Nov 2015 19:51:27 +0000 Subject: [PATCH 04/99] Add generic sample buffer class --- CMakeLists.txt | 1 + samplebuffer.cpp | 28 ++++++++++++++++++++++++++++ samplebuffer.h | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 samplebuffer.cpp create mode 100644 samplebuffer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cdbd1cc..7af5693 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ list(APPEND inspectrum_sources fft.cpp mainwindow.cpp inputsource.cpp + samplebuffer.cpp spectrogram.cpp spectrogramcontrols.cpp waveformview.cpp diff --git a/samplebuffer.cpp b/samplebuffer.cpp new file mode 100644 index 0000000..29607b3 --- /dev/null +++ b/samplebuffer.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015, 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 "samplebuffer.h" + +bool SampleBuffer::getSamples(std::complex *dest, off_t start, off_t length) +{ + std::complex buf[length]; + src->getSamples(buf, start, length); + work(buf, dest, length); + return true; +} \ No newline at end of file diff --git a/samplebuffer.h b/samplebuffer.h new file mode 100644 index 0000000..f2bc9cd --- /dev/null +++ b/samplebuffer.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015, 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 "samplesource.h" + +class SampleBuffer : public SampleSource +{ +private: + std::shared_ptr src; + +public: + SampleBuffer(SampleSource *src) : src(src) {}; + virtual bool getSamples(std::complex *dest, off_t start, off_t length); + virtual void work(void *input, void *output, int count) = 0; + virtual off_t count() { return src->count(); }; +}; From d47f1c471cda1f016a21dcaa5de9352431b9f828 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 3 Dec 2015 14:34:07 +0000 Subject: [PATCH 05/99] waveform: Use SampleSource --- waveformview.cpp | 8 ++++---- waveformview.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/waveformview.cpp b/waveformview.cpp index a677867..79942ce 100644 --- a/waveformview.cpp +++ b/waveformview.cpp @@ -31,9 +31,9 @@ WaveformView::WaveformView() } } -void WaveformView::inputSourceChanged(InputSource *input) +void WaveformView::inputSourceChanged(SampleSource *src) { - inputSource = input; + sampleSource = src; update(); } @@ -47,7 +47,7 @@ void WaveformView::viewChanged(off_t firstSample, off_t lastSample) void WaveformView::paintEvent(QPaintEvent *event) { - if (inputSource == nullptr) return; + if (sampleSource == nullptr) return; if (lastSample - firstSample <= 0) return; QRect rect = QRect(0, 0, width(), height()); @@ -56,7 +56,7 @@ void WaveformView::paintEvent(QPaintEvent *event) off_t length = lastSample - firstSample; std::complex *samples = new std::complex[length]; - inputSource->getSamples(samples, firstSample, length); + sampleSource->getSamples(samples, firstSample, length); for (int iq = 0; iq < 2; iq++) { switch (iq) { case 0: diff --git a/waveformview.h b/waveformview.h index b91a215..34e39c8 100644 --- a/waveformview.h +++ b/waveformview.h @@ -30,14 +30,14 @@ public: WaveformView(); public slots: - void inputSourceChanged(InputSource *input); + void inputSourceChanged(SampleSource *input); void viewChanged(off_t firstSample, off_t lastSample); protected: void paintEvent(QPaintEvent *event); private: - InputSource *inputSource = nullptr; + SampleSource *sampleSource = nullptr; off_t firstSample = 0; off_t lastSample = 0; QRgb colormap[255]; From c1b819fc377d646af59c45e1f6ed9ae6f71ca446 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 29 Nov 2015 17:54:05 +0000 Subject: [PATCH 06/99] Require GNURadio (and boost) --- CMakeLists.txt | 13 ++++++++++++- README.md | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7af5693..5fbc009 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,10 +38,15 @@ list(APPEND inspectrum_sources ) INCLUDE(FindPkgConfig) + find_package(Qt5Widgets REQUIRED) +find_package(Boost COMPONENTS system program_options REQUIRED) +set(GR_REQUIRED_COMPONENTS RUNTIME BLOCKS FILTER) +find_package(Gnuradio REQUIRED) pkg_check_modules(FFTW REQUIRED fftw3f) include_directories( + ${GNURADIO_RUNTIME_INCLUDE_DIRS} ${QT_INCLUDES} ${FFTW_INCLUDEDIR} ${FFTW_INCLUDE_DIRS} @@ -54,7 +59,13 @@ link_directories( add_executable(inspectrum ${inspectrum_sources}) qt5_use_modules(inspectrum Widgets) -target_link_libraries(inspectrum ${QT_LIBRARIES} ${FFTW_LIBRARIES} ${extraLibs}) +target_link_libraries(inspectrum + ${Boost_LIBRARIES} + ${GNURADIO_ALL_LIBRARIES} + ${QT_LIBRARIES} + ${FFTW_LIBRARIES} + ${extraLibs} +) set(INSTALL_DEFAULT_BINDIR "bin" CACHE STRING "Appended to CMAKE_INSTALL_PREFIX") install(TARGETS inspectrum RUNTIME DESTINATION ${INSTALL_DEFAULT_BINDIR}) diff --git a/README.md b/README.md index 3af7f91..e398fe9 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ inspectrum is a tool for analysing captured signals, primarily from software-def ## Try it ### Prerequisites + * boost >=1.35 + * gnuradio 3.7.x * qt5 * fftw 3.x * cmake From 655a3f9b9cfc1206385c7c58c5236afff6f1193e Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 4 Dec 2015 01:14:37 +0000 Subject: [PATCH 07/99] Add memory source/sink --- CMakeLists.txt | 2 ++ memory_sink.h | 41 +++++++++++++++++++++++ memory_sink_impl.cc | 75 ++++++++++++++++++++++++++++++++++++++++++ memory_sink_impl.h | 50 ++++++++++++++++++++++++++++ memory_source.h | 41 +++++++++++++++++++++++ memory_source_impl.cc | 76 +++++++++++++++++++++++++++++++++++++++++++ memory_source_impl.h | 50 ++++++++++++++++++++++++++++ 7 files changed, 335 insertions(+) create mode 100644 memory_sink.h create mode 100644 memory_sink_impl.cc create mode 100644 memory_sink_impl.h create mode 100644 memory_source.h create mode 100644 memory_source_impl.cc create mode 100644 memory_source_impl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fbc009..49266c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,8 @@ list(APPEND inspectrum_sources fft.cpp mainwindow.cpp inputsource.cpp + memory_sink_impl.cc + memory_source_impl.cc samplebuffer.cpp spectrogram.cpp spectrogramcontrols.cpp diff --git a/memory_sink.h b/memory_sink.h new file mode 100644 index 0000000..634444f --- /dev/null +++ b/memory_sink.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015, 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 . + */ + +#ifndef INCLUDED_GR_MEMORY_SINK_H +#define INCLUDED_GR_MEMORY_SINK_H + +#include +#include + +namespace gr { + namespace blocks { + class memory_sink : virtual public sync_block + { + public: + typedef boost::shared_ptr sptr; + + static sptr make(size_t itemsize); + + virtual void set_sink(void *sink, size_t length) = 0; + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_GR_MEMORY_SINK_H */ \ No newline at end of file diff --git a/memory_sink_impl.cc b/memory_sink_impl.cc new file mode 100644 index 0000000..7d57c3e --- /dev/null +++ b/memory_sink_impl.cc @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015, 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 "memory_sink_impl.h" +#include +#include + +namespace gr { + namespace blocks { + + memory_sink::sptr + memory_sink::make(size_t itemsize) + { + return gnuradio::get_initial_sptr + (new memory_sink_impl(itemsize)); + } + + memory_sink_impl::memory_sink_impl(size_t itemsize) + : sync_block("memory_sink", + io_signature::make(1, 1, itemsize), + io_signature::make(0, 0, 0)), + d_itemsize(itemsize) + { + } + + memory_sink_impl::~memory_sink_impl() + { + } + + void + memory_sink_impl::set_sink(void *sink, size_t length) + { + d_sink = sink; + d_length = length; + d_ptr = 0; + } + + int + memory_sink_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + char *inbuf = (char*)input_items[0]; + long nwritten = 0; + + if(!d_sink) + return noutput_items; + + nwritten = std::min((long)(d_length - d_ptr), (long)noutput_items); + if (nwritten >= 0) { + memcpy((char*)d_sink + d_ptr * d_itemsize, inbuf, nwritten * d_itemsize); + } + d_ptr += nwritten; + + return (nwritten == 0) ? -1 : nwritten; + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/memory_sink_impl.h b/memory_sink_impl.h new file mode 100644 index 0000000..4ceb41f --- /dev/null +++ b/memory_sink_impl.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015, 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 . + */ + +#ifndef INCLUDED_GR_MEMORY_SINK_IMPL_H +#define INCLUDED_GR_MEMORY_SINK_IMPL_H + +#include "memory_sink.h" + +namespace gr { + namespace blocks { + + class memory_sink_impl : public memory_sink + { + private: + size_t d_itemsize; + void *d_sink; + size_t d_length; + size_t d_ptr = 0; + + public: + memory_sink_impl(size_t itemsize); + ~memory_sink_impl(); + + void set_sink(void *sink, size_t length); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_GR_MEMORY_SINK_IMPL_H */ \ No newline at end of file diff --git a/memory_source.h b/memory_source.h new file mode 100644 index 0000000..f9c75e7 --- /dev/null +++ b/memory_source.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015, 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 . + */ + +#ifndef INCLUDED_GR_MEMORY_SOURCE_H +#define INCLUDED_GR_MEMORY_SOURCE_H + +#include +#include + +namespace gr { + namespace blocks { + class memory_source : virtual public sync_block + { + public: + typedef boost::shared_ptr sptr; + + static sptr make(size_t itemsize); + + virtual void set_source(void *source, size_t length) = 0; + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_GR_MEMORY_SOURCE_H */ \ No newline at end of file diff --git a/memory_source_impl.cc b/memory_source_impl.cc new file mode 100644 index 0000000..5a324d0 --- /dev/null +++ b/memory_source_impl.cc @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015, 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 "memory_source_impl.h" +#include +#include + +namespace gr { + namespace blocks { + + memory_source::sptr + memory_source::make(size_t itemsize) + { + return gnuradio::get_initial_sptr + (new memory_source_impl(itemsize)); + } + + memory_source_impl::memory_source_impl(size_t itemsize) + : sync_block("memory_source", + io_signature::make(0, 0, 0), + io_signature::make(1, 1, itemsize)), + d_itemsize(itemsize) + { + } + + memory_source_impl::~memory_source_impl() + { + } + + void + memory_source_impl::set_source(void *source, size_t length) + { + d_source = source; + d_length = length; + d_ptr = 0; + } + + int + memory_source_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + char *outbuf = (char*)output_items[0]; + long nwritten = 0; + + if(!d_source) + return noutput_items; + + nwritten = std::min((long)(d_length - d_ptr), (long)noutput_items); + + if (nwritten >= 0) { + memcpy(outbuf, (char*)d_source + d_ptr * d_itemsize, nwritten * d_itemsize); + } + d_ptr += nwritten; + + return (nwritten == 0) ? -1 : nwritten; + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/memory_source_impl.h b/memory_source_impl.h new file mode 100644 index 0000000..020946a --- /dev/null +++ b/memory_source_impl.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015, 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. + *ha + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef INCLUDED_GR_MEMORY_SOURCE_IMPL_H +#define INCLUDED_GR_MEMORY_SOURCE_IMPL_H + +#include "memory_source.h" + +namespace gr { + namespace blocks { + + class memory_source_impl : public memory_source + { + private: + size_t d_itemsize; + void *d_source; + size_t d_length; + size_t d_ptr = 0; + + public: + memory_source_impl(size_t itemsize); + ~memory_source_impl(); + + void set_source(void *source, size_t length); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_GR_MEMORY_SOURCE_IMPL_H */ \ No newline at end of file From f3917ec0329bb35b83fac3df55781f608265dbdc Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 4 Dec 2015 01:15:02 +0000 Subject: [PATCH 08/99] Add GNURadio sample buffer --- CMakeLists.txt | 1 + grsamplebuffer.cpp | 27 +++++++++++++++++++++++++++ grsamplebuffer.h | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 grsamplebuffer.cpp create mode 100644 grsamplebuffer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 49266c3..cbc8e73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ list(APPEND inspectrum_sources main.cpp fft.cpp mainwindow.cpp + grsamplebuffer.cpp inputsource.cpp memory_sink_impl.cc memory_source_impl.cc diff --git a/grsamplebuffer.cpp b/grsamplebuffer.cpp new file mode 100644 index 0000000..d0a86f1 --- /dev/null +++ b/grsamplebuffer.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015, 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 "grsamplebuffer.h" + + void GRSampleBuffer::work(void *input, void *output, int length) + { + mem_source->set_source(input, length); + mem_sink->set_sink(output, length); + tb->run(); + } \ No newline at end of file diff --git a/grsamplebuffer.h b/grsamplebuffer.h new file mode 100644 index 0000000..b8eca52 --- /dev/null +++ b/grsamplebuffer.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015, 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 "memory_sink.h" +#include "memory_source.h" + +#include "samplebuffer.h" + +class GRSampleBuffer : public SampleBuffer +{ +private: + gr::top_block_sptr tb; + gr::blocks::memory_source::sptr mem_source; + gr::blocks::memory_sink::sptr mem_sink; + +public: + GRSampleBuffer(SampleSource *src, gr::top_block_sptr tb, gr::blocks::memory_source::sptr mem_source, gr::blocks::memory_sink::sptr mem_sink) + : SampleBuffer(src), tb(tb), mem_source(mem_source), mem_sink(mem_sink) {}; + virtual void work(void *input, void *output, int count); +}; From b6a71e65d75099026bf6d6d89f6edd0493b2e002 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 4 Dec 2015 01:15:59 +0000 Subject: [PATCH 09/99] WIP waveformview use grsamplebuffer --- waveformview.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/waveformview.cpp b/waveformview.cpp index 79942ce..bd9f6c1 100644 --- a/waveformview.cpp +++ b/waveformview.cpp @@ -20,6 +20,11 @@ #include "waveformview.h" #include #include +#include +#include +#include "grsamplebuffer.h" +#include "memory_sink.h" +#include "memory_source.h" WaveformView::WaveformView() { @@ -33,7 +38,13 @@ WaveformView::WaveformView() void WaveformView::inputSourceChanged(SampleSource *src) { - sampleSource = src; + gr::top_block_sptr tb = gr::make_top_block("multiply"); + auto mem_source = gr::blocks::memory_source::make(8); + auto mem_sink = gr::blocks::memory_sink::make(8); + auto multiply = gr::blocks::multiply_const_cc::make(10); + tb->connect(mem_source, 0, multiply, 0); + tb->connect(multiply, 0, mem_sink, 0); + sampleSource = new GRSampleBuffer(src, tb, mem_source, mem_sink); update(); } @@ -69,8 +80,9 @@ void WaveformView::paintEvent(QPaintEvent *event) int xprev = 0; int yprev = 0; for (off_t i = 0; i < length; i++) { + float sample = (iq == 0) ? samples[i].real() : samples[i].imag(); int x = (float)i / length * rect.width(); - int y = (samples[i].real() * 50 * rect.height()/2) + rect.height()/2; + int y = (sample * rect.height()/2) + rect.height()/2; if (x < 0) x = 0; if (y < 0) y = 0; From 3d407648ddb3155c224a749e4ca3f8d515c45548 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 25 Dec 2015 23:12:38 +0000 Subject: [PATCH 10/99] Use signals/slots to update waveform view --- mainwindow.cpp | 12 +++++++++--- mainwindow.h | 4 ++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index a5b2d33..f68fd40 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -34,6 +34,7 @@ MainWindow::MainWindow() wave = new WaveformView(); addDockWidget(Qt::BottomDockWidgetArea, wave); + connect(this, SIGNAL(viewChanged(off_t, off_t)), wave, SLOT(viewChanged(off_t, off_t))); connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); connect(dock->sampleRate, SIGNAL(textChanged(QString)), this, SLOT(setSampleRate(QString))); @@ -81,7 +82,7 @@ void MainWindow::setFFTSize(int size) off_t sample = getCenterSample(); spectrogram.setFFTSize(size); scrollArea.verticalScrollBar()->setValue(getScrollPos(sample)); - wave->viewChanged(getTopSample(), getBottomSample()); + emitViewChanged(); } void MainWindow::setZoomLevel(int zoom) @@ -89,7 +90,12 @@ void MainWindow::setZoomLevel(int zoom) off_t sample = getCenterSample(); spectrogram.setZoomLevel(zoom); scrollArea.verticalScrollBar()->setValue(getScrollPos(sample)); - wave->viewChanged(getTopSample(), getBottomSample()); + emitViewChanged(); +} + +void MainWindow::emitViewChanged() +{ + emit viewChanged(getTopSample(), getBottomSample()); } off_t MainWindow::getTopSample() @@ -122,5 +128,5 @@ void MainWindow::openFile(QString fileName) this->setWindowTitle(title.arg(QApplication::applicationName(),fileName.section('/',-1,-1))); spectrogram.openFile(fileName); wave->inputSourceChanged(spectrogram.inputSource); - wave->viewChanged(getTopSample(), getBottomSample()); + emitViewChanged(); } diff --git a/mainwindow.h b/mainwindow.h index d2cecfb..49a21d7 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -39,6 +39,9 @@ public slots: void setFFTSize(int size); void setZoomLevel(int zoom); +signals: + void viewChanged(off_t firstSample, off_t lastSample); + protected: bool eventFilter(QObject *obj, QEvent *event); @@ -48,6 +51,7 @@ private: SpectrogramControls *dock; WaveformView *wave; + void emitViewChanged(); off_t getTopSample(); off_t getCenterSample(); off_t getBottomSample(); From 7fb65638562ec13aa769747c78f6a1c5e3e056fd Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 25 Dec 2015 23:18:06 +0000 Subject: [PATCH 11/99] Update waveform view on scroll --- mainwindow.cpp | 7 +++++++ mainwindow.h | 1 + 2 files changed, 8 insertions(+) diff --git a/mainwindow.cpp b/mainwindow.cpp index f68fd40..0da06d4 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -42,6 +42,8 @@ MainWindow::MainWindow() connect(dock->zoomLevelSlider, SIGNAL(valueChanged(int)), this, SLOT(setZoomLevel(int))); connect(dock->powerMaxSlider, SIGNAL(valueChanged(int)), &spectrogram, SLOT(setPowerMax(int))); connect(dock->powerMinSlider, SIGNAL(valueChanged(int)), &spectrogram, SLOT(setPowerMin(int))); + + connect(scrollArea.verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(spectrogramSliderMoved(int))); } bool MainWindow::eventFilter(QObject * /*obj*/, QEvent *event) @@ -93,6 +95,11 @@ void MainWindow::setZoomLevel(int zoom) emitViewChanged(); } +void MainWindow::spectrogramSliderMoved(int value) +{ + emitViewChanged(); +} + void MainWindow::emitViewChanged() { emit viewChanged(getTopSample(), getBottomSample()); diff --git a/mainwindow.h b/mainwindow.h index 49a21d7..d9ab8f9 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -38,6 +38,7 @@ public slots: void setSampleRate(QString rate); void setFFTSize(int size); void setZoomLevel(int zoom); + void spectrogramSliderMoved(int value); signals: void viewChanged(off_t firstSample, off_t lastSample); From c96f542823d9df860a35f29fea7941973049396f Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 29 Dec 2015 12:59:54 +0000 Subject: [PATCH 12/99] waveform: Factor out plot function --- waveformview.cpp | 52 ++++++++++++++++++++++++------------------------ waveformview.h | 2 ++ 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/waveformview.cpp b/waveformview.cpp index bd9f6c1..7ae8d18 100644 --- a/waveformview.cpp +++ b/waveformview.cpp @@ -41,7 +41,7 @@ void WaveformView::inputSourceChanged(SampleSource *src) gr::top_block_sptr tb = gr::make_top_block("multiply"); auto mem_source = gr::blocks::memory_source::make(8); auto mem_sink = gr::blocks::memory_sink::make(8); - auto multiply = gr::blocks::multiply_const_cc::make(10); + auto multiply = gr::blocks::multiply_const_cc::make(20); tb->connect(mem_source, 0, multiply, 0); tb->connect(multiply, 0, mem_sink, 0); sampleSource = new GRSampleBuffer(src, tb, mem_source, mem_sink); @@ -68,32 +68,32 @@ void WaveformView::paintEvent(QPaintEvent *event) off_t length = lastSample - firstSample; std::complex *samples = new std::complex[length]; sampleSource->getSamples(samples, firstSample, length); - for (int iq = 0; iq < 2; iq++) { - switch (iq) { - case 0: - painter.setPen(Qt::red); - break; - case 1: - painter.setPen(Qt::blue); - break; - } - int xprev = 0; - int yprev = 0; - for (off_t i = 0; i < length; i++) { - float sample = (iq == 0) ? samples[i].real() : samples[i].imag(); - int x = (float)i / length * rect.width(); - int y = (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, yprev, x, y); - xprev = x; - yprev = y; - } - } + painter.setPen(Qt::red); + plot(&painter, reinterpret_cast(samples), length, 2); + painter.setPen(Qt::blue); + plot(&painter, reinterpret_cast(samples)+1, length, 2); delete[] samples; +} + +void WaveformView::plot(QPainter *painter, float *samples, off_t count, int step = 1) +{ + QRect rect = QRect(0, 0, width(), height()); + 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 = (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, yprev, x, y); + xprev = x; + yprev = y; + } } \ No newline at end of file diff --git a/waveformview.h b/waveformview.h index 34e39c8..cb16f54 100644 --- a/waveformview.h +++ b/waveformview.h @@ -41,4 +41,6 @@ private: off_t firstSample = 0; off_t lastSample = 0; QRgb colormap[255]; + + void plot(QPainter *painter, float *samples, off_t count, int step); }; \ No newline at end of file From ceac8341031400d0e2361841e984e5cf026b3b6c Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Tue, 29 Dec 2015 20:42:32 +0000 Subject: [PATCH 13/99] Refactor getSamples to return a unique_ptr --- inputsource.cpp | 11 ++++++----- inputsource.h | 2 +- samplebuffer.cpp | 10 +++++----- samplebuffer.h | 2 +- samplesource.h | 3 ++- spectrogram.cpp | 5 ++--- waveformview.cpp | 9 +++------ 7 files changed, 20 insertions(+), 22 deletions(-) diff --git a/inputsource.cpp b/inputsource.cpp index 2cb1c31..9761612 100644 --- a/inputsource.cpp +++ b/inputsource.cpp @@ -48,14 +48,15 @@ InputSource::~InputSource() { fclose(m_file); } -bool InputSource::getSamples(std::complex *dest, off_t start, off_t length) +std::unique_ptr[]> InputSource::getSamples(off_t start, off_t length) { if(start < 0 || length < 0) - return false; + return nullptr; if (start + length >= sampleCount) - return false; + return nullptr; - memcpy(dest, &m_data[start], length * sizeof(std::complex)); - return true; + std::unique_ptr[]> dest(new std::complex[length]); + memcpy(dest.get(), &m_data[start], length * sizeof(std::complex)); + return dest; } diff --git a/inputsource.h b/inputsource.h index d3e3bd6..d2ea3b1 100644 --- a/inputsource.h +++ b/inputsource.h @@ -34,6 +34,6 @@ public: InputSource(const char *filename); ~InputSource(); - bool getSamples(std::complex *dest, off_t start, off_t length); + std::unique_ptr[]> getSamples(off_t start, off_t length); off_t count() { return sampleCount; }; }; diff --git a/samplebuffer.cpp b/samplebuffer.cpp index 29607b3..47f92e5 100644 --- a/samplebuffer.cpp +++ b/samplebuffer.cpp @@ -19,10 +19,10 @@ #include "samplebuffer.h" -bool SampleBuffer::getSamples(std::complex *dest, off_t start, off_t length) +std::unique_ptr[]> SampleBuffer::getSamples(off_t start, off_t length) { - std::complex buf[length]; - src->getSamples(buf, start, length); - work(buf, dest, length); - return true; + auto samples = src->getSamples(start, length); + std::unique_ptr[]> dest(new std::complex[length]); + work(samples.get(), dest.get(), length); + return dest; } \ No newline at end of file diff --git a/samplebuffer.h b/samplebuffer.h index f2bc9cd..76a8160 100644 --- a/samplebuffer.h +++ b/samplebuffer.h @@ -30,7 +30,7 @@ private: public: SampleBuffer(SampleSource *src) : src(src) {}; - virtual bool getSamples(std::complex *dest, off_t start, off_t length); + virtual std::unique_ptr[]> getSamples(off_t start, off_t length); virtual void work(void *input, void *output, int count) = 0; virtual off_t count() { return src->count(); }; }; diff --git a/samplesource.h b/samplesource.h index 523a43f..5ff629a 100644 --- a/samplesource.h +++ b/samplesource.h @@ -20,6 +20,7 @@ #pragma once #include +#include class SampleSource { @@ -27,6 +28,6 @@ class SampleSource public: virtual ~SampleSource() {}; - virtual bool getSamples(std::complex *dest, off_t start, off_t length) = 0; + virtual std::unique_ptr[]> getSamples(off_t start, off_t length) = 0; virtual off_t count() = 0; }; diff --git a/spectrogram.cpp b/spectrogram.cpp index f756473..c23afe5 100644 --- a/spectrogram.cpp +++ b/spectrogram.cpp @@ -143,15 +143,14 @@ float* Spectrogram::getFFTTile(off_t tile) void Spectrogram::getLine(float *dest, off_t sample) { if (inputSource && fft) { - std::complex buffer[fftSize]; - inputSource->getSamples(buffer, sample, fftSize); + auto buffer = inputSource->getSamples(sample, fftSize); for (int i = 0; i < fftSize; i++) { buffer[i].real(buffer[i].real() * window[i]); buffer[i].imag(buffer[i].imag() * window[i]); } - fft->process(buffer, buffer); + fft->process(buffer.get(), buffer.get()); for (int i = 0; i < fftSize; i++) { int k = (i + fftSize / 2) % fftSize; float re = buffer[k].real(); diff --git a/waveformview.cpp b/waveformview.cpp index 7ae8d18..e65a726 100644 --- a/waveformview.cpp +++ b/waveformview.cpp @@ -66,15 +66,12 @@ void WaveformView::paintEvent(QPaintEvent *event) painter.fillRect(rect, Qt::black); off_t length = lastSample - firstSample; - std::complex *samples = new std::complex[length]; - sampleSource->getSamples(samples, firstSample, length); + auto samples = sampleSource->getSamples(firstSample, length); painter.setPen(Qt::red); - plot(&painter, reinterpret_cast(samples), length, 2); + plot(&painter, reinterpret_cast(samples.get()), length, 2); painter.setPen(Qt::blue); - plot(&painter, reinterpret_cast(samples)+1, length, 2); - - delete[] samples; + plot(&painter, reinterpret_cast(samples.get())+1, length, 2); } void WaveformView::plot(QPainter *painter, float *samples, off_t count, int step = 1) From b349145d9196e1b75a7e5a98a27b15b8599a0ae7 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Wed, 30 Dec 2015 20:03:34 +0000 Subject: [PATCH 14/99] Template SampleSource --- abstractsamplesource.h | 30 ++++++++++++++++++++++++++++++ grsamplebuffer.cpp | 8 ++++++-- grsamplebuffer.h | 7 ++++--- inputsource.h | 2 +- samplebuffer.cpp | 10 +++++++--- samplebuffer.h | 9 +++++---- samplesource.h | 6 ++++-- waveformview.cpp | 11 ++++++++--- waveformview.h | 4 ++-- 9 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 abstractsamplesource.h diff --git a/abstractsamplesource.h b/abstractsamplesource.h new file mode 100644 index 0000000..f8fa1c6 --- /dev/null +++ b/abstractsamplesource.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015, 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 + +class AbstractSampleSource +{ + +public: + virtual ~AbstractSampleSource() {}; +}; diff --git a/grsamplebuffer.cpp b/grsamplebuffer.cpp index d0a86f1..5c62025 100644 --- a/grsamplebuffer.cpp +++ b/grsamplebuffer.cpp @@ -19,9 +19,13 @@ #include "grsamplebuffer.h" - void GRSampleBuffer::work(void *input, void *output, int length) + template + void GRSampleBuffer::work(void *input, void *output, int length) { mem_source->set_source(input, length); mem_sink->set_sink(output, length); tb->run(); - } \ No newline at end of file + } + +template class GRSampleBuffer, std::complex>; +template class GRSampleBuffer, float>; diff --git a/grsamplebuffer.h b/grsamplebuffer.h index b8eca52..f013457 100644 --- a/grsamplebuffer.h +++ b/grsamplebuffer.h @@ -26,7 +26,8 @@ #include "samplebuffer.h" -class GRSampleBuffer : public SampleBuffer +template +class GRSampleBuffer : public SampleBuffer { private: gr::top_block_sptr tb; @@ -34,7 +35,7 @@ private: gr::blocks::memory_sink::sptr mem_sink; public: - GRSampleBuffer(SampleSource *src, gr::top_block_sptr tb, gr::blocks::memory_source::sptr mem_source, gr::blocks::memory_sink::sptr mem_sink) - : SampleBuffer(src), tb(tb), mem_source(mem_source), mem_sink(mem_sink) {}; + GRSampleBuffer(SampleSource *src, gr::top_block_sptr tb, gr::blocks::memory_source::sptr mem_source, gr::blocks::memory_sink::sptr mem_sink) + : SampleBuffer(src), tb(tb), mem_source(mem_source), mem_sink(mem_sink) {}; virtual void work(void *input, void *output, int count); }; diff --git a/inputsource.h b/inputsource.h index d2ea3b1..63be033 100644 --- a/inputsource.h +++ b/inputsource.h @@ -22,7 +22,7 @@ #include #include "samplesource.h" -class InputSource : public SampleSource +class InputSource : public SampleSource> { private: FILE *m_file; diff --git a/samplebuffer.cpp b/samplebuffer.cpp index 47f92e5..9cc518d 100644 --- a/samplebuffer.cpp +++ b/samplebuffer.cpp @@ -19,10 +19,14 @@ #include "samplebuffer.h" -std::unique_ptr[]> SampleBuffer::getSamples(off_t start, off_t length) +template +std::unique_ptr SampleBuffer::getSamples(off_t start, off_t length) { auto samples = src->getSamples(start, length); - std::unique_ptr[]> dest(new std::complex[length]); + std::unique_ptr dest(new Tout[length]); work(samples.get(), dest.get(), length); return dest; -} \ No newline at end of file +} + +template class SampleBuffer, std::complex>; +template class SampleBuffer, float>; diff --git a/samplebuffer.h b/samplebuffer.h index 76a8160..53c0081 100644 --- a/samplebuffer.h +++ b/samplebuffer.h @@ -23,14 +23,15 @@ #include #include "samplesource.h" -class SampleBuffer : public SampleSource +template +class SampleBuffer : public SampleSource { private: - std::shared_ptr src; + std::shared_ptr> src; public: - SampleBuffer(SampleSource *src) : src(src) {}; - virtual std::unique_ptr[]> getSamples(off_t start, off_t length); + SampleBuffer(SampleSource *src) : src(src) {}; + virtual std::unique_ptr getSamples(off_t start, off_t length); virtual void work(void *input, void *output, int count) = 0; virtual off_t count() { return src->count(); }; }; diff --git a/samplesource.h b/samplesource.h index 5ff629a..22d5bbd 100644 --- a/samplesource.h +++ b/samplesource.h @@ -21,13 +21,15 @@ #include #include +#include "abstractsamplesource.h" -class SampleSource +template +class SampleSource : public AbstractSampleSource { public: virtual ~SampleSource() {}; - virtual std::unique_ptr[]> getSamples(off_t start, off_t length) = 0; + virtual std::unique_ptr getSamples(off_t start, off_t length) = 0; virtual off_t count() = 0; }; diff --git a/waveformview.cpp b/waveformview.cpp index e65a726..d922eaa 100644 --- a/waveformview.cpp +++ b/waveformview.cpp @@ -36,7 +36,7 @@ WaveformView::WaveformView() } } -void WaveformView::inputSourceChanged(SampleSource *src) +void WaveformView::inputSourceChanged(AbstractSampleSource *src) { gr::top_block_sptr tb = gr::make_top_block("multiply"); auto mem_source = gr::blocks::memory_source::make(8); @@ -44,7 +44,12 @@ void WaveformView::inputSourceChanged(SampleSource *src) auto multiply = gr::blocks::multiply_const_cc::make(20); tb->connect(mem_source, 0, multiply, 0); tb->connect(multiply, 0, mem_sink, 0); - sampleSource = new GRSampleBuffer(src, tb, mem_source, mem_sink); + + auto derived = dynamic_cast>*>(src); + if (derived == nullptr) + throw new std::runtime_error("SampleSource doesn't provide correct type for GRSampleBuffer"); + + sampleSource = new GRSampleBuffer, std::complex>(derived, tb, mem_source, mem_sink); update(); } @@ -66,7 +71,7 @@ void WaveformView::paintEvent(QPaintEvent *event) painter.fillRect(rect, Qt::black); off_t length = lastSample - firstSample; - auto samples = sampleSource->getSamples(firstSample, length); + auto samples = dynamic_cast>*>(sampleSource)->getSamples(firstSample, length); painter.setPen(Qt::red); plot(&painter, reinterpret_cast(samples.get()), length, 2); diff --git a/waveformview.h b/waveformview.h index cb16f54..63563b9 100644 --- a/waveformview.h +++ b/waveformview.h @@ -30,14 +30,14 @@ public: WaveformView(); public slots: - void inputSourceChanged(SampleSource *input); + void inputSourceChanged(AbstractSampleSource *input); void viewChanged(off_t firstSample, off_t lastSample); protected: void paintEvent(QPaintEvent *event); private: - SampleSource *sampleSource = nullptr; + AbstractSampleSource *sampleSource = nullptr; off_t firstSample = 0; off_t lastSample = 0; QRgb colormap[255]; From fe98bb871d2f6e8a742ffac949729727c206837c Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Wed, 30 Dec 2015 20:40:57 +0000 Subject: [PATCH 15/99] waveform: Support single-channel waveforms --- waveformview.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/waveformview.cpp b/waveformview.cpp index d922eaa..f143333 100644 --- a/waveformview.cpp +++ b/waveformview.cpp @@ -71,12 +71,17 @@ void WaveformView::paintEvent(QPaintEvent *event) painter.fillRect(rect, Qt::black); off_t length = lastSample - firstSample; - auto samples = dynamic_cast>*>(sampleSource)->getSamples(firstSample, length); - - painter.setPen(Qt::red); - plot(&painter, reinterpret_cast(samples.get()), length, 2); - painter.setPen(Qt::blue); - plot(&painter, reinterpret_cast(samples.get())+1, length, 2); + if (auto src = dynamic_cast>*>(sampleSource)) { + auto samples = src->getSamples(firstSample, length); + painter.setPen(Qt::red); + plot(&painter, reinterpret_cast(samples.get()), length, 2); + painter.setPen(Qt::blue); + plot(&painter, reinterpret_cast(samples.get())+1, length, 2); + } else if (auto src = dynamic_cast*>(sampleSource)) { + auto samples = src->getSamples(firstSample, length); + painter.setPen(Qt::green); + plot(&painter, samples.get(), length, 1); + } } void WaveformView::plot(QPainter *painter, float *samples, off_t count, int step = 1) From bb59dded04f1a248441db09b88ab0089a61d2d0d Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Wed, 30 Dec 2015 22:40:38 +0000 Subject: [PATCH 16/99] waveform: Pass in QRect to specify plot area --- waveformview.cpp | 11 +++++------ waveformview.h | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/waveformview.cpp b/waveformview.cpp index f143333..e54bfa8 100644 --- a/waveformview.cpp +++ b/waveformview.cpp @@ -74,19 +74,18 @@ void WaveformView::paintEvent(QPaintEvent *event) if (auto src = dynamic_cast>*>(sampleSource)) { auto samples = src->getSamples(firstSample, length); painter.setPen(Qt::red); - plot(&painter, reinterpret_cast(samples.get()), length, 2); + plot(&painter, rect, reinterpret_cast(samples.get()), length, 2); painter.setPen(Qt::blue); - plot(&painter, reinterpret_cast(samples.get())+1, length, 2); + plot(&painter, rect, reinterpret_cast(samples.get())+1, length, 2); } else if (auto src = dynamic_cast*>(sampleSource)) { auto samples = src->getSamples(firstSample, length); painter.setPen(Qt::green); - plot(&painter, samples.get(), length, 1); + plot(&painter, rect, samples.get(), length, 1); } } -void WaveformView::plot(QPainter *painter, float *samples, off_t count, int step = 1) +void WaveformView::plot(QPainter *painter, QRect &rect, float *samples, off_t count, int step = 1) { - QRect rect = QRect(0, 0, width(), height()); int xprev = 0; int yprev = 0; for (off_t i = 0; i < count; i++) { @@ -99,7 +98,7 @@ void WaveformView::plot(QPainter *painter, float *samples, off_t count, int step if (x >= rect.width()-1) x = rect.width()-2; if (y >= rect.height()-1) y = rect.height()-2; - painter->drawLine(xprev, yprev, x, y); + painter->drawLine(xprev + rect.x(), yprev + rect.y(), x + rect.x(), y + rect.y()); xprev = x; yprev = y; } diff --git a/waveformview.h b/waveformview.h index 63563b9..9fddcbf 100644 --- a/waveformview.h +++ b/waveformview.h @@ -42,5 +42,5 @@ private: off_t lastSample = 0; QRgb colormap[255]; - void plot(QPainter *painter, float *samples, off_t count, int step); + void plot(QPainter *painter, QRect &rect, float *samples, off_t count, int step); }; \ No newline at end of file From 3a1c0882f647e03e346ed8be0a6e6f6fe116171e Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Wed, 30 Dec 2015 22:42:15 +0000 Subject: [PATCH 17/99] waveform: Support plotting multiple waveforms --- waveformview.cpp | 44 ++++++++++++++++++++++++++------------------ waveformview.h | 2 +- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/waveformview.cpp b/waveformview.cpp index e54bfa8..8f09b9f 100644 --- a/waveformview.cpp +++ b/waveformview.cpp @@ -38,18 +38,20 @@ WaveformView::WaveformView() void WaveformView::inputSourceChanged(AbstractSampleSource *src) { - gr::top_block_sptr tb = gr::make_top_block("multiply"); - auto mem_source = gr::blocks::memory_source::make(8); - auto mem_sink = gr::blocks::memory_sink::make(8); + sampleSources.clear(); + + gr::top_block_sptr iq_tb = gr::make_top_block("multiply"); + auto iq_mem_source = gr::blocks::memory_source::make(8); + auto iq_mem_sink = gr::blocks::memory_sink::make(8); auto multiply = gr::blocks::multiply_const_cc::make(20); - tb->connect(mem_source, 0, multiply, 0); - tb->connect(multiply, 0, mem_sink, 0); + iq_tb->connect(iq_mem_source, 0, multiply, 0); + iq_tb->connect(multiply, 0, iq_mem_sink, 0); auto derived = dynamic_cast>*>(src); if (derived == nullptr) throw new std::runtime_error("SampleSource doesn't provide correct type for GRSampleBuffer"); - sampleSource = new GRSampleBuffer, std::complex>(derived, tb, mem_source, mem_sink); + sampleSources.emplace_back(new GRSampleBuffer, std::complex>(derived, iq_tb, iq_mem_source, iq_mem_sink)); update(); } @@ -63,24 +65,30 @@ void WaveformView::viewChanged(off_t firstSample, off_t lastSample) void WaveformView::paintEvent(QPaintEvent *event) { - if (sampleSource == nullptr) return; if (lastSample - firstSample <= 0) return; QRect rect = QRect(0, 0, width(), height()); QPainter painter(this); painter.fillRect(rect, Qt::black); - off_t length = lastSample - firstSample; - if (auto src = dynamic_cast>*>(sampleSource)) { - auto samples = src->getSamples(firstSample, length); - painter.setPen(Qt::red); - plot(&painter, rect, reinterpret_cast(samples.get()), length, 2); - painter.setPen(Qt::blue); - plot(&painter, rect, reinterpret_cast(samples.get())+1, length, 2); - } else if (auto src = dynamic_cast*>(sampleSource)) { - auto samples = src->getSamples(firstSample, length); - painter.setPen(Qt::green); - plot(&painter, rect, samples.get(), length, 1); + // 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++; } } diff --git a/waveformview.h b/waveformview.h index 9fddcbf..ab80768 100644 --- a/waveformview.h +++ b/waveformview.h @@ -37,7 +37,7 @@ protected: void paintEvent(QPaintEvent *event); private: - AbstractSampleSource *sampleSource = nullptr; + std::vector> sampleSources; off_t firstSample = 0; off_t lastSample = 0; QRgb colormap[255]; From 14425a7885838ce7641d574643e1b257ab36c806 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Wed, 30 Dec 2015 22:42:34 +0000 Subject: [PATCH 18/99] waveform: Plot instantaneous frequency --- CMakeLists.txt | 2 +- waveformview.cpp | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cbc8e73..e32970c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ INCLUDE(FindPkgConfig) find_package(Qt5Widgets REQUIRED) find_package(Boost COMPONENTS system program_options REQUIRED) -set(GR_REQUIRED_COMPONENTS RUNTIME BLOCKS FILTER) +set(GR_REQUIRED_COMPONENTS RUNTIME ANALOG BLOCKS FILTER) find_package(Gnuradio REQUIRED) pkg_check_modules(FFTW REQUIRED fftw3f) diff --git a/waveformview.cpp b/waveformview.cpp index 8f09b9f..3643000 100644 --- a/waveformview.cpp +++ b/waveformview.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "grsamplebuffer.h" #include "memory_sink.h" @@ -47,11 +48,19 @@ void WaveformView::inputSourceChanged(AbstractSampleSource *src) iq_tb->connect(iq_mem_source, 0, multiply, 0); iq_tb->connect(multiply, 0, iq_mem_sink, 0); + gr::top_block_sptr quad_demod_tb = gr::make_top_block("quad_demod"); + auto quad_demod_mem_source = gr::blocks::memory_source::make(8); + auto quad_demod_mem_sink = gr::blocks::memory_sink::make(4); + auto quad_demod = gr::analog::quadrature_demod_cf::make(5); + quad_demod_tb->connect(quad_demod_mem_source, 0, quad_demod, 0); + quad_demod_tb->connect(quad_demod, 0, quad_demod_mem_sink, 0); + auto derived = dynamic_cast>*>(src); if (derived == nullptr) throw new std::runtime_error("SampleSource doesn't provide correct type for GRSampleBuffer"); sampleSources.emplace_back(new GRSampleBuffer, std::complex>(derived, iq_tb, iq_mem_source, iq_mem_sink)); + sampleSources.emplace_back(new GRSampleBuffer, float>(derived, quad_demod_tb, quad_demod_mem_source, quad_demod_mem_sink)); update(); } From 27bf33a6c72babd6e8e142399e1c625f6b6d6838 Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Sun, 20 Sep 2015 16:11:21 +0200 Subject: [PATCH 19/99] feature: Call external program on snippet --- mainwindow.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index c66b29d..586641f 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -18,6 +18,7 @@ */ #include +#include #include "mainwindow.h" @@ -40,6 +41,9 @@ MainWindow::MainWindow() connect(dock->powerMinSlider, SIGNAL(valueChanged(int)), &spectrogram, SLOT(setPowerMin(int))); } +QRubberBand *rubberBand=NULL; +QPoint mystart; + bool MainWindow::eventFilter(QObject * /*obj*/, QEvent *event) { if (event->type() == QEvent::Wheel) { @@ -58,7 +62,51 @@ bool MainWindow::eventFilter(QObject * /*obj*/, QEvent *event) } return true; } - } + }else if (event->type() == QEvent::MouseButtonPress) { + QMouseEvent *mouseEvent = (QMouseEvent*)event; + if (mouseEvent->buttons() == Qt::LeftButton){ + mystart = (mouseEvent->pos()); + if(!rubberBand) + rubberBand = new QRubberBand(QRubberBand::Rectangle, scrollArea.viewport()); + rubberBand->setGeometry(QRect(mystart, mystart)); + rubberBand->show(); + return true; + } + }else if (event->type() == QEvent::MouseMove) { + QMouseEvent *mouseEvent = (QMouseEvent*)event; + if (mouseEvent->buttons() == Qt::LeftButton){ + rubberBand->setGeometry(QRect(mystart, mouseEvent->pos()).normalized()); //Area Bounding + return true; + } + }else if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = (QMouseEvent*)event; + QRect rb= rubberBand->geometry(); + + int topSample=spectrogram.lineToSample(scrollArea.verticalScrollBar()->value()+ rb.top()); + int bottomSample=spectrogram.lineToSample(scrollArea.verticalScrollBar()->value()+ rb.bottom()); + + int leftFreq=(int)(scrollArea.horizontalScrollBar()->value() + rb.left())*spectrogram.getSampleRate()/spectrogram.getFFTSize(); + int rightFreq=(int)(scrollArea.horizontalScrollBar()->value() + rb.right())*spectrogram.getSampleRate()/spectrogram.getFFTSize(); + + QStringList command; + command << "cut_sample" << spectrogram.getFileName() << QString::number(spectrogram.getSampleRate()) + << QString::number(spectrogram.lineToSample(scrollArea.verticalScrollBar()->value()+ rb.top())) + << QString::number(spectrogram.lineToSample(scrollArea.verticalScrollBar()->value()+ rb.bottom())) + + << QString::number((long int)(scrollArea.horizontalScrollBar()->value() + rb.left())*spectrogram.getSampleRate()/spectrogram.getFFTSize() +spectrogram.getCenterFreq() -spectrogram.getSampleRate()/2) + << QString::number((long int)(scrollArea.horizontalScrollBar()->value() + rb.right())*spectrogram.getSampleRate()/spectrogram.getFFTSize() +spectrogram.getCenterFreq() -spectrogram.getSampleRate()/2) + << QString::number(spectrogram.getCenterFreq()) + ; + + if(system(command.join(" ").toLatin1().data())){ + QMessageBox msgBox; + msgBox.setText("cut_sample call failed."); + msgBox.exec(); + }; + rubberBand->hide(); + rubberBand->clearMask(); + return true; + }; return false; } From 75769550c295d356f6a3152eccb93540ddcfb131 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 7 Jan 2016 21:25:35 +0000 Subject: [PATCH 20/99] Style fixup astyle --kr --- CMakeLists.txt | 32 ++--- fft.h | 7 +- inputsource.cpp | 6 +- inputsource.h | 4 +- main.cpp | 24 ++-- mainwindow.cpp | 74 +++++------ mainwindow.h | 12 +- spectrogram.cpp | 280 ++++++++++++++++++++-------------------- spectrogram.h | 102 ++++++++------- spectrogramcontrols.cpp | 66 +++++----- spectrogramcontrols.h | 31 ++--- 11 files changed, 327 insertions(+), 311 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74d60dc..025e3f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,12 +10,12 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) if (WIN32) - find_library (MMAN mman) - if(NOT(MMAN)) - message(FATAL_ERROR "please install mman-win32") - else(NOT(MMAN)) - set (extraLibs ${extraLibs} ${MMAN}) - endif(NOT(MMAN)) + find_library (MMAN mman) + if(NOT(MMAN)) + message(FATAL_ERROR "please install mman-win32") + else(NOT(MMAN)) + set (extraLibs ${extraLibs} ${MMAN}) + endif(NOT(MMAN)) ENDIF (WIN32) if (NOT CMAKE_CXX_FLAGS) @@ -27,12 +27,12 @@ endif (NOT CMAKE_CXX_FLAGS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") list(APPEND inspectrum_sources - main.cpp - fft.cpp - mainwindow.cpp - inputsource.cpp - spectrogram.cpp - spectrogramcontrols.cpp + main.cpp + fft.cpp + mainwindow.cpp + inputsource.cpp + spectrogram.cpp + spectrogramcontrols.cpp ) INCLUDE(FindPkgConfig) @@ -40,13 +40,13 @@ find_package(Qt5Widgets REQUIRED) pkg_check_modules(FFTW REQUIRED fftw3f) include_directories( - ${QT_INCLUDES} - ${FFTW_INCLUDEDIR} - ${FFTW_INCLUDE_DIRS} + ${QT_INCLUDES} + ${FFTW_INCLUDEDIR} + ${FFTW_INCLUDE_DIRS} ) link_directories( - ${FFTW_LIBRARY_DIRS} + ${FFTW_LIBRARY_DIRS} ) add_executable(inspectrum ${inspectrum_sources}) diff --git a/fft.h b/fft.h index d30c456..adec9a7 100644 --- a/fft.h +++ b/fft.h @@ -21,12 +21,15 @@ #include -class FFT { +class FFT +{ public: FFT(int size); ~FFT(); void process(void *dest, void *source); - int getSize() { return fftSize; } + int getSize() { + return fftSize; + } private: int fftSize; diff --git a/inputsource.cpp b/inputsource.cpp index ca03fd3..197ed98 100644 --- a/inputsource.cpp +++ b/inputsource.cpp @@ -27,7 +27,8 @@ #include -InputSource::InputSource(const char *filename) { +InputSource::InputSource(const char *filename) +{ m_file = fopen(filename, "rb"); if (m_file == nullptr) throw std::runtime_error("Error opening file"); @@ -43,7 +44,8 @@ InputSource::InputSource(const char *filename) { throw std::runtime_error("Error mmapping file"); } -InputSource::~InputSource() { +InputSource::~InputSource() +{ munmap(m_data, m_file_size); fclose(m_file); } diff --git a/inputsource.h b/inputsource.h index ad310f2..ae8b0e0 100644 --- a/inputsource.h +++ b/inputsource.h @@ -36,5 +36,7 @@ public: ~InputSource(); bool getSamples(fftwf_complex *dest, off_t start, int length); - off_t getSampleCount() { return sampleCount; }; + off_t getSampleCount() { + return sampleCount; + }; }; diff --git a/main.cpp b/main.cpp index c922599..01ee997 100644 --- a/main.cpp +++ b/main.cpp @@ -35,27 +35,27 @@ int main(int argc, char *argv[]) // Add options QCommandLineOption rateOption(QStringList() << "r" << "rate", - QCoreApplication::translate("main", "Set sample rate."), - QCoreApplication::translate("main", "Hz")); + QCoreApplication::translate("main", "Set sample rate."), + QCoreApplication::translate("main", "Hz")); parser.addOption(rateOption); // Process the actual command line parser.process(a); - if (parser.isSet(rateOption)){ - bool ok; - // Use toDouble just for scientific notation support - int rate = parser.value(rateOption).toDouble(&ok); - if(!ok){ - fputs("ERROR: could not parse rate\n", stderr); - return 1; - } - mainWin.changeSampleRate(rate); + if (parser.isSet(rateOption)) { + bool ok; + // Use toDouble just for scientific notation support + int rate = parser.value(rateOption).toDouble(&ok); + if(!ok) { + fputs("ERROR: could not parse rate\n", stderr); + return 1; + } + mainWin.changeSampleRate(rate); } const QStringList args = parser.positionalArguments(); if (args.size()>=1) - mainWin.openFile(args.at(0)); + mainWin.openFile(args.at(0)); mainWin.show(); return a.exec(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 586641f..59ab03e 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -62,51 +62,51 @@ bool MainWindow::eventFilter(QObject * /*obj*/, QEvent *event) } return true; } - }else if (event->type() == QEvent::MouseButtonPress) { + } else if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *mouseEvent = (QMouseEvent*)event; - if (mouseEvent->buttons() == Qt::LeftButton){ - mystart = (mouseEvent->pos()); - if(!rubberBand) - rubberBand = new QRubberBand(QRubberBand::Rectangle, scrollArea.viewport()); - rubberBand->setGeometry(QRect(mystart, mystart)); - rubberBand->show(); - return true; - } - }else if (event->type() == QEvent::MouseMove) { + if (mouseEvent->buttons() == Qt::LeftButton) { + mystart = (mouseEvent->pos()); + if(!rubberBand) + rubberBand = new QRubberBand(QRubberBand::Rectangle, scrollArea.viewport()); + rubberBand->setGeometry(QRect(mystart, mystart)); + rubberBand->show(); + return true; + } + } else if (event->type() == QEvent::MouseMove) { QMouseEvent *mouseEvent = (QMouseEvent*)event; - if (mouseEvent->buttons() == Qt::LeftButton){ - rubberBand->setGeometry(QRect(mystart, mouseEvent->pos()).normalized()); //Area Bounding - return true; - } - }else if (event->type() == QEvent::MouseButtonRelease) { + if (mouseEvent->buttons() == Qt::LeftButton) { + rubberBand->setGeometry(QRect(mystart, mouseEvent->pos()).normalized()); //Area Bounding + return true; + } + } else if (event->type() == QEvent::MouseButtonRelease) { QMouseEvent *mouseEvent = (QMouseEvent*)event; - QRect rb= rubberBand->geometry(); + QRect rb = rubberBand->geometry(); - int topSample=spectrogram.lineToSample(scrollArea.verticalScrollBar()->value()+ rb.top()); - int bottomSample=spectrogram.lineToSample(scrollArea.verticalScrollBar()->value()+ rb.bottom()); + int topSample = spectrogram.lineToSample(scrollArea.verticalScrollBar()->value() + rb.top()); + int bottomSample = spectrogram.lineToSample(scrollArea.verticalScrollBar()->value() + rb.bottom()); - int leftFreq=(int)(scrollArea.horizontalScrollBar()->value() + rb.left())*spectrogram.getSampleRate()/spectrogram.getFFTSize(); - int rightFreq=(int)(scrollArea.horizontalScrollBar()->value() + rb.right())*spectrogram.getSampleRate()/spectrogram.getFFTSize(); + int leftFreq = (int)(scrollArea.horizontalScrollBar()->value() + rb.left())*spectrogram.getSampleRate()/spectrogram.getFFTSize(); + int rightFreq = (int)(scrollArea.horizontalScrollBar()->value() + rb.right())*spectrogram.getSampleRate()/spectrogram.getFFTSize(); - QStringList command; - command << "cut_sample" << spectrogram.getFileName() << QString::number(spectrogram.getSampleRate()) - << QString::number(spectrogram.lineToSample(scrollArea.verticalScrollBar()->value()+ rb.top())) - << QString::number(spectrogram.lineToSample(scrollArea.verticalScrollBar()->value()+ rb.bottom())) + QStringList command; + command << "cut_sample" << spectrogram.getFileName() << QString::number(spectrogram.getSampleRate()) + << QString::number(spectrogram.lineToSample(scrollArea.verticalScrollBar()->value() + rb.top())) + << QString::number(spectrogram.lineToSample(scrollArea.verticalScrollBar()->value() + rb.bottom())) - << QString::number((long int)(scrollArea.horizontalScrollBar()->value() + rb.left())*spectrogram.getSampleRate()/spectrogram.getFFTSize() +spectrogram.getCenterFreq() -spectrogram.getSampleRate()/2) - << QString::number((long int)(scrollArea.horizontalScrollBar()->value() + rb.right())*spectrogram.getSampleRate()/spectrogram.getFFTSize() +spectrogram.getCenterFreq() -spectrogram.getSampleRate()/2) - << QString::number(spectrogram.getCenterFreq()) - ; + << QString::number((long int)(scrollArea.horizontalScrollBar()->value() + rb.left())*spectrogram.getSampleRate()/spectrogram.getFFTSize() + spectrogram.getCenterFreq() - spectrogram.getSampleRate()/2) + << QString::number((long int)(scrollArea.horizontalScrollBar()->value() + rb.right())*spectrogram.getSampleRate()/spectrogram.getFFTSize() + spectrogram.getCenterFreq() - spectrogram.getSampleRate()/2) + << QString::number(spectrogram.getCenterFreq()) + ; - if(system(command.join(" ").toLatin1().data())){ - QMessageBox msgBox; - msgBox.setText("cut_sample call failed."); - msgBox.exec(); - }; - rubberBand->hide(); - rubberBand->clearMask(); - return true; - }; + if(system(command.join(" ").toLatin1().data())) { + QMessageBox msgBox; + msgBox.setText("cut_sample call failed."); + msgBox.exec(); + }; + rubberBand->hide(); + rubberBand->clearMask(); + return true; + }; return false; } diff --git a/mainwindow.h b/mainwindow.h index bdc53a4..3ab4e0c 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -34,18 +34,18 @@ public: public slots: void openFile(QString fileName); - void setSampleRate(QString rate); - void setFFTSize(int size); - void setZoomLevel(int zoom); + void setSampleRate(QString rate); + void setFFTSize(int size); + void setZoomLevel(int zoom); protected: - bool eventFilter(QObject *obj, QEvent *event); + bool eventFilter(QObject *obj, QEvent *event); private: QScrollArea scrollArea; Spectrogram spectrogram; SpectrogramControls *dock; - off_t getCenterSample(); - int getScrollPos(off_t sample); + off_t getCenterSample(); + int getScrollPos(off_t sample); }; diff --git a/spectrogram.cpp b/spectrogram.cpp index c70f201..0ef3f08 100644 --- a/spectrogram.cpp +++ b/spectrogram.cpp @@ -30,228 +30,234 @@ Spectrogram::Spectrogram() { - sampleRate = 8000000; - setFFTSize(1024); - zoomLevel = 0; - powerMax = 0.0f; - powerMin = -50.0f; + sampleRate = 8000000; + setFFTSize(1024); + zoomLevel = 0; + powerMax = 0.0f; + powerMin = -50.0f; - for (int i = 0; i < 256; i++) { - float p = (float)i / 256; - colormap[i] = QColor::fromHsvF(p * 0.83f, 1.0, 1.0 - p).rgba(); - } + for (int i = 0; i < 256; i++) { + float p = (float)i / 256; + colormap[i] = QColor::fromHsvF(p * 0.83f, 1.0, 1.0 - p).rgba(); + } } Spectrogram::~Spectrogram() { - delete fft; - delete inputSource; + delete fft; + delete inputSource; } -QSize Spectrogram::sizeHint() const { - return QSize(1024, 2048); +QSize Spectrogram::sizeHint() const +{ + return QSize(1024, 2048); } void Spectrogram::openFile(QString fileName) { - if (fileName != nullptr) { - try { - InputSource *newFile = new InputSource(fileName.toUtf8().constData()); - delete inputSource; - pixmapCache.clear(); - fftCache.clear(); - inputSource = newFile; - resize(fftSize, getHeight()); - } catch (std::runtime_error e) { - // TODO: display error - } - } + if (fileName != nullptr) { + try { + InputSource *newFile = new InputSource(fileName.toUtf8().constData()); + delete inputSource; + pixmapCache.clear(); + fftCache.clear(); + inputSource = newFile; + resize(fftSize, getHeight()); + } catch (std::runtime_error e) { + // TODO: display error + } + } } -template const T& clamp (const T& value, const T& min, const T& max) { +template const T& clamp (const T& value, const T& min, const T& max) +{ return std::min(max, std::max(min, value)); } void Spectrogram::paintEvent(QPaintEvent *event) { - QRect rect = event->rect(); - QPainter painter(this); - painter.fillRect(rect, Qt::black); + QRect rect = event->rect(); + QPainter painter(this); + painter.fillRect(rect, Qt::black); - if (inputSource != nullptr) { - int height = rect.height(); - off_t y = rect.y(); + if (inputSource != nullptr) { + int height = rect.height(); + off_t y = rect.y(); - QImage image(fftSize, height, QImage::Format_RGB32); + QImage image(fftSize, height, QImage::Format_RGB32); - while (height > 0) { - int tileOffset = y % linesPerTile(); // To handle drawing a partial first tile - int drawHeight = std::min(linesPerTile() - tileOffset, height); // Draw rest of first tile, full tile, or partial final tile - off_t tileId = lineToSample(y - tileOffset); - QPixmap *tile = getPixmapTile(tileId); - painter.drawPixmap(QRect(0, y, fftSize, drawHeight), *tile, QRect(0, tileOffset, fftSize, drawHeight)); - y += drawHeight; - height -= drawHeight; - } + while (height > 0) { + int tileOffset = y % linesPerTile(); // To handle drawing a partial first tile + int drawHeight = std::min(linesPerTile() - tileOffset, height); // Draw rest of first tile, full tile, or partial final tile + off_t tileId = lineToSample(y - tileOffset); + QPixmap *tile = getPixmapTile(tileId); + painter.drawPixmap(QRect(0, y, fftSize, drawHeight), *tile, QRect(0, tileOffset, fftSize, drawHeight)); + y += drawHeight; + height -= drawHeight; + } - paintTimeAxis(&painter, rect); - } + paintTimeAxis(&painter, rect); + } } QPixmap* Spectrogram::getPixmapTile(off_t tile) { - QPixmap *obj = pixmapCache.object(TileCacheKey(fftSize, zoomLevel, tile)); - if (obj != 0) - return obj; + QPixmap *obj = pixmapCache.object(TileCacheKey(fftSize, zoomLevel, tile)); + if (obj != 0) + return obj; - float *fftTile = getFFTTile(tile); - obj = new QPixmap(fftSize, linesPerTile()); - QImage image(fftSize, linesPerTile(), QImage::Format_RGB32); - for (int y = 0; y < linesPerTile(); y++) { - float *line = &fftTile[y * fftSize]; - for (int x = 0; x < fftSize; x++) { - float powerRange = std::abs(int(powerMin - powerMax)); - float normPower = (line[x] - powerMax) * -1.0f / powerRange; - normPower = clamp(normPower, 0.0f, 1.0f); + float *fftTile = getFFTTile(tile); + obj = new QPixmap(fftSize, linesPerTile()); + QImage image(fftSize, linesPerTile(), QImage::Format_RGB32); + for (int y = 0; y < linesPerTile(); y++) { + float *line = &fftTile[y * fftSize]; + for (int x = 0; x < fftSize; x++) { + float powerRange = std::abs(int(powerMin - powerMax)); + float normPower = (line[x] - powerMax) * -1.0f / powerRange; + normPower = clamp(normPower, 0.0f, 1.0f); - image.setPixel(x, y, colormap[(uint8_t)(normPower * (256 - 1))]); - } - } - obj->convertFromImage(image); - pixmapCache.insert(TileCacheKey(fftSize, zoomLevel, tile), obj); - return obj; + image.setPixel(x, y, colormap[(uint8_t)(normPower * (256 - 1))]); + } + } + obj->convertFromImage(image); + pixmapCache.insert(TileCacheKey(fftSize, zoomLevel, tile), obj); + return obj; } float* Spectrogram::getFFTTile(off_t tile) { - float *obj = fftCache.object(TileCacheKey(fftSize, zoomLevel, tile)); - if (obj != 0) - return obj; + float *obj = fftCache.object(TileCacheKey(fftSize, zoomLevel, tile)); + if (obj != 0) + return obj; - float *dest = new float[tileSize]; - float *ptr = dest; - off_t sample = tile; - while ((ptr - dest) < tileSize) { - getLine(ptr, sample); - sample += getStride(); - ptr += fftSize; - } - fftCache.insert(TileCacheKey(fftSize, zoomLevel, tile), dest); - return dest; + float *dest = new float[tileSize]; + float *ptr = dest; + off_t sample = tile; + while ((ptr - dest) < tileSize) { + getLine(ptr, sample); + sample += getStride(); + ptr += fftSize; + } + fftCache.insert(TileCacheKey(fftSize, zoomLevel, tile), dest); + return dest; } void Spectrogram::getLine(float *dest, off_t sample) { - if (inputSource && fft) { - fftwf_complex buffer[fftSize]; - inputSource->getSamples(buffer, sample, fftSize); + if (inputSource && fft) { + fftwf_complex buffer[fftSize]; + inputSource->getSamples(buffer, sample, fftSize); - for (int i = 0; i < fftSize; i++) { - buffer[i][0] *= window[i]; - buffer[i][1] *= window[i]; - } + for (int i = 0; i < fftSize; i++) { + buffer[i][0] *= window[i]; + buffer[i][1] *= window[i]; + } - fft->process(buffer, buffer); - for (int i = 0; i < fftSize; i++) { - int k = (i + fftSize / 2) % fftSize; - float re = buffer[k][0]; - float im = buffer[k][1]; - float mag = sqrt(re * re + im * im) / fftSize; - float magdb = 10 * log2(mag) / log2(10); - *dest = magdb; - dest++; - } - } + fft->process(buffer, buffer); + for (int i = 0; i < fftSize; i++) { + int k = (i + fftSize / 2) % fftSize; + float re = buffer[k][0]; + float im = buffer[k][1]; + float mag = sqrt(re * re + im * im) / fftSize; + float magdb = 10 * log2(mag) / log2(10); + *dest = magdb; + dest++; + } + } } void Spectrogram::paintTimeAxis(QPainter *painter, QRect rect) { - // Round up for firstLine and round each to nearest linesPerGraduation - int firstLine = ((rect.y() + linesPerGraduation - 1) / linesPerGraduation) * linesPerGraduation; - int lastLine = ((rect.y() + rect.height()) / linesPerGraduation) * linesPerGraduation; + // Round up for firstLine and round each to nearest linesPerGraduation + int firstLine = ((rect.y() + linesPerGraduation - 1) / linesPerGraduation) * linesPerGraduation; + int lastLine = ((rect.y() + rect.height()) / linesPerGraduation) * linesPerGraduation; - painter->save(); - QPen pen(Qt::white, 1, Qt::SolidLine); - painter->setPen(pen); - QFontMetrics fm(painter->font()); - int textOffset = fm.ascent() / 2 - 1; - for (int line = firstLine; line <= lastLine; line += linesPerGraduation) { - painter->drawLine(0, line, 10, line); - painter->drawText(12, line + textOffset, sampleToTime(lineToSample(line))); - } - painter->restore(); + painter->save(); + QPen pen(Qt::white, 1, Qt::SolidLine); + painter->setPen(pen); + QFontMetrics fm(painter->font()); + int textOffset = fm.ascent() / 2 - 1; + for (int line = firstLine; line <= lastLine; line += linesPerGraduation) { + painter->drawLine(0, line, 10, line); + painter->drawText(12, line + textOffset, sampleToTime(lineToSample(line))); + } + painter->restore(); } void Spectrogram::setSampleRate(int rate) { - sampleRate = rate; - update(); + sampleRate = rate; + update(); } void Spectrogram::setFFTSize(int size) { - fftSize = size; - delete fft; - fft = new FFT(fftSize); + fftSize = size; + delete fft; + fft = 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))); - } + window.reset(new float[fftSize]); + for (int i = 0; i < fftSize; i++) { + window[i] = 0.5f * (1.0f - cos(Tau * i / (fftSize - 1))); + } - resize(fftSize, getHeight()); + resize(fftSize, getHeight()); } void Spectrogram::setPowerMax(int power) { - powerMax = power; - pixmapCache.clear(); - update(); + powerMax = power; + pixmapCache.clear(); + update(); } void Spectrogram::setPowerMin(int power) { - powerMin = power; - pixmapCache.clear(); - update(); + powerMin = power; + pixmapCache.clear(); + update(); } void Spectrogram::setZoomLevel(int zoom) { - zoomLevel = clamp(zoom, 0, (int)log2(fftSize)); - resize(fftSize, getHeight()); + zoomLevel = clamp(zoom, 0, (int)log2(fftSize)); + resize(fftSize, getHeight()); } int Spectrogram::getHeight() { - if (!inputSource) - return 0; + if (!inputSource) + return 0; - return inputSource->getSampleCount() / getStride(); + return inputSource->getSampleCount() / getStride(); } int Spectrogram::getStride() { - return fftSize / pow(2, zoomLevel); + return fftSize / pow(2, zoomLevel); } -off_t Spectrogram::lineToSample(off_t line) { - return line * getStride(); +off_t Spectrogram::lineToSample(off_t line) +{ + return line * getStride(); } -int Spectrogram::sampleToLine(off_t sample) { - return sample / getStride(); +int Spectrogram::sampleToLine(off_t sample) +{ + return sample / getStride(); } QString Spectrogram::sampleToTime(off_t sample) { - return QString::number((float)sample / sampleRate).append("s"); + return QString::number((float)sample / sampleRate).append("s"); } -int Spectrogram::linesPerTile() { - return tileSize / fftSize; +int Spectrogram::linesPerTile() +{ + return tileSize / fftSize; } -uint qHash(const TileCacheKey &key, uint seed) { - return key.fftSize ^ key.zoomLevel ^ key.sample ^ seed; +uint qHash(const TileCacheKey &key, uint seed) +{ + return key.fftSize ^ key.zoomLevel ^ key.sample ^ seed; } diff --git a/spectrogram.h b/spectrogram.h index 91d2713..bb0003e 100644 --- a/spectrogram.h +++ b/spectrogram.h @@ -30,72 +30,74 @@ static const double Tau = M_PI * 2.0; class TileCacheKey; -class Spectrogram : public QWidget { - Q_OBJECT +class Spectrogram : public QWidget +{ + Q_OBJECT public: - Spectrogram(); - ~Spectrogram(); - QSize sizeHint() const; - int getHeight(); - int getStride(); + Spectrogram(); + ~Spectrogram(); + QSize sizeHint() const; + int getHeight(); + int getStride(); public slots: - void openFile(QString fileName); - void setSampleRate(int rate); - void setFFTSize(int size); - void setPowerMax(int power); - void setPowerMin(int power); - void setZoomLevel(int zoom); + void openFile(QString fileName); + void setSampleRate(int rate); + void setFFTSize(int size); + void setPowerMax(int power); + void setPowerMin(int power); + void setZoomLevel(int zoom); protected: - void paintEvent(QPaintEvent *event); + void paintEvent(QPaintEvent *event); private: - const int linesPerGraduation = 50; - const int tileSize = 65536; // This must be a multiple of the maximum FFT size + const int linesPerGraduation = 50; + const int tileSize = 65536; // This must be a multiple of the maximum FFT size - InputSource *inputSource = nullptr; - FFT *fft = nullptr; - std::unique_ptr window; - fftwf_complex *lineBuffer = nullptr; - QCache pixmapCache; - QCache fftCache; - uint colormap[256]; + InputSource *inputSource = nullptr; + FFT *fft = nullptr; + std::unique_ptr window; + fftwf_complex *lineBuffer = nullptr; + QCache pixmapCache; + QCache fftCache; + uint colormap[256]; - int sampleRate; - int fftSize; - int zoomLevel; - float powerMax; - float powerMin; + int sampleRate; + int fftSize; + int zoomLevel; + float powerMax; + float powerMin; - QPixmap* getPixmapTile(off_t tile); - float* getFFTTile(off_t tile); - void getLine(float *dest, off_t sample); - void paintTimeAxis(QPainter *painter, QRect rect); - off_t lineToSample(off_t line); - int sampleToLine(off_t sample); - QString sampleToTime(off_t sample); - int linesPerTile(); + QPixmap* getPixmapTile(off_t tile); + float* getFFTTile(off_t tile); + void getLine(float *dest, off_t sample); + void paintTimeAxis(QPainter *painter, QRect rect); + off_t lineToSample(off_t line); + int sampleToLine(off_t sample); + QString sampleToTime(off_t sample); + int linesPerTile(); }; -class TileCacheKey { +class TileCacheKey +{ public: - TileCacheKey(int fftSize, int zoomLevel, off_t sample) { - this->fftSize = fftSize; - this->zoomLevel = zoomLevel; - this->sample = sample; - } + TileCacheKey(int fftSize, int zoomLevel, off_t sample) { + this->fftSize = fftSize; + this->zoomLevel = zoomLevel; + this->sample = sample; + } - bool operator==(const TileCacheKey &k2) const { - return (this->fftSize == k2.fftSize) && - (this->zoomLevel == k2.zoomLevel) && - (this->sample == k2.sample); - } + bool operator==(const TileCacheKey &k2) const { + return (this->fftSize == k2.fftSize) && + (this->zoomLevel == k2.zoomLevel) && + (this->sample == k2.sample); + } - int fftSize; - int zoomLevel; - off_t sample; + int fftSize; + int zoomLevel; + off_t sample; }; diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 877ffc8..9d75f3c 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -24,54 +24,54 @@ #include SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent) - : QDockWidget::QDockWidget(title, parent) + : QDockWidget::QDockWidget(title, parent) { - widget = new QWidget(this); - layout = new QFormLayout(widget); + widget = new QWidget(this); + layout = new QFormLayout(widget); - fileOpenButton = new QPushButton("Open file...", widget); - layout->addRow(fileOpenButton); + fileOpenButton = new QPushButton("Open file...", widget); + layout->addRow(fileOpenButton); - sampleRate = new QLineEdit("8000000"); - sampleRate->setValidator(new QIntValidator(this)); - layout->addRow(new QLabel(tr("Sample rate:")), sampleRate); + sampleRate = new QLineEdit("8000000"); + sampleRate->setValidator(new QIntValidator(this)); + layout->addRow(new QLabel(tr("Sample rate:")), sampleRate); - fftSizeSlider = new QSlider(Qt::Horizontal, widget); - fftSizeSlider->setRange(7, 13); - fftSizeSlider->setValue(10); - layout->addRow(new QLabel(tr("FFT size:")), fftSizeSlider); + fftSizeSlider = new QSlider(Qt::Horizontal, widget); + fftSizeSlider->setRange(7, 13); + fftSizeSlider->setValue(10); + layout->addRow(new QLabel(tr("FFT size:")), fftSizeSlider); - zoomLevelSlider = new QSlider(Qt::Horizontal, widget); - zoomLevelSlider->setRange(0, 5); - zoomLevelSlider->setValue(0); - layout->addRow(new QLabel(tr("Zoom:")), zoomLevelSlider); + zoomLevelSlider = new QSlider(Qt::Horizontal, widget); + zoomLevelSlider->setRange(0, 5); + zoomLevelSlider->setValue(0); + layout->addRow(new QLabel(tr("Zoom:")), zoomLevelSlider); - powerMaxSlider = new QSlider(Qt::Horizontal, widget); - powerMaxSlider->setRange(-100, 20); - powerMaxSlider->setValue(0); - layout->addRow(new QLabel(tr("Power max:")), powerMaxSlider); + powerMaxSlider = new QSlider(Qt::Horizontal, widget); + powerMaxSlider->setRange(-100, 20); + powerMaxSlider->setValue(0); + layout->addRow(new QLabel(tr("Power max:")), powerMaxSlider); - powerMinSlider = new QSlider(Qt::Horizontal, widget); - powerMinSlider->setRange(-100, 20); - powerMinSlider->setValue(-50); - layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); + powerMinSlider = new QSlider(Qt::Horizontal, widget); + powerMinSlider->setRange(-100, 20); + powerMinSlider->setValue(-50); + layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); - widget->setLayout(layout); - setWidget(widget); + widget->setLayout(layout); + setWidget(widget); - connect(fftSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(fftSizeSliderChanged(int))); - connect(fileOpenButton, SIGNAL(clicked()), this, SLOT(fileOpenButtonClicked())); + connect(fftSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(fftSizeSliderChanged(int))); + connect(fileOpenButton, SIGNAL(clicked()), this, SLOT(fileOpenButtonClicked())); } void SpectrogramControls::fftSizeSliderChanged(int size) { - emit fftSizeChanged((int)pow(2, size)); + emit fftSizeChanged((int)pow(2, size)); } void SpectrogramControls::fileOpenButtonClicked() { - QString fileName = QFileDialog::getOpenFileName( - this, tr("Open File"), "", tr("Sample file (*.cfile *.bin);;All files (*)") - ); - emit openFile(fileName); + QString fileName = QFileDialog::getOpenFileName( + this, tr("Open File"), "", tr("Sample file (*.cfile *.bin);;All files (*)") + ); + emit openFile(fileName); } \ No newline at end of file diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index 1e03f4c..1979378 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -25,28 +25,29 @@ #include #include -class SpectrogramControls : public QDockWidget { - Q_OBJECT +class SpectrogramControls : public QDockWidget +{ + Q_OBJECT public: - SpectrogramControls(const QString & title, QWidget * parent); + SpectrogramControls(const QString & title, QWidget * parent); signals: - void fftSizeChanged(int size); - void openFile(QString fileName); + void fftSizeChanged(int size); + void openFile(QString fileName); private slots: - void fftSizeSliderChanged(int size); - void fileOpenButtonClicked(); + void fftSizeSliderChanged(int size); + void fileOpenButtonClicked(); private: - QWidget *widget; - QFormLayout *layout; + QWidget *widget; + QFormLayout *layout; public: - QPushButton *fileOpenButton; - QLineEdit *sampleRate; - QSlider *fftSizeSlider; - QSlider *zoomLevelSlider; - QSlider *powerMaxSlider; - QSlider *powerMinSlider; + QPushButton *fileOpenButton; + QLineEdit *sampleRate; + QSlider *fftSizeSlider; + QSlider *zoomLevelSlider; + QSlider *powerMaxSlider; + QSlider *powerMinSlider; }; \ No newline at end of file From 3b28c00affe8f33f4789be484a32ce9f98db0bda Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 10 Jan 2016 23:23:56 +0000 Subject: [PATCH 21/99] Move clamp from spectrogram to util --- CMakeLists.txt | 1 + mainwindow.cpp | 1 + spectrogram.cpp | 6 +----- util.cpp | 28 ++++++++++++++++++++++++++++ util.h | 22 ++++++++++++++++++++++ 5 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 util.cpp create mode 100644 util.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 025e3f2..6fcf781 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ list(APPEND inspectrum_sources inputsource.cpp spectrogram.cpp spectrogramcontrols.cpp + util.cpp ) INCLUDE(FindPkgConfig) diff --git a/mainwindow.cpp b/mainwindow.cpp index 59ab03e..a970c4f 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -21,6 +21,7 @@ #include #include "mainwindow.h" +#include "util.h" MainWindow::MainWindow() { diff --git a/spectrogram.cpp b/spectrogram.cpp index 0ef3f08..1b4a820 100644 --- a/spectrogram.cpp +++ b/spectrogram.cpp @@ -26,6 +26,7 @@ #include #include +#include "util.h" Spectrogram::Spectrogram() @@ -69,11 +70,6 @@ void Spectrogram::openFile(QString fileName) } } -template const T& clamp (const T& value, const T& min, const T& max) -{ - return std::min(max, std::max(min, value)); -} - void Spectrogram::paintEvent(QPaintEvent *event) { QRect rect = event->rect(); diff --git a/util.cpp b/util.cpp new file mode 100644 index 0000000..98eab81 --- /dev/null +++ b/util.cpp @@ -0,0 +1,28 @@ +/* + * 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 +#include "util.h" + +template const T& clamp (const T& value, const T& min, const T& max) +{ + return std::min(max, std::max(min, value)); +} +template const float& clamp(const float&, const float&, const float&); +template const int& clamp(const int&, const int&, const int&); diff --git a/util.h b/util.h new file mode 100644 index 0000000..eaf4d77 --- /dev/null +++ b/util.h @@ -0,0 +1,22 @@ +/* + * 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 + +template const T& clamp (const T& value, const T& min, const T& max); From b0c29831accaadf638141a5011d89fe7d19307c4 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 11 Jan 2016 00:50:30 +0000 Subject: [PATCH 22/99] selection: Store spectrogram selection --- mainwindow.cpp | 28 ++++++++-------------------- mainwindow.h | 2 ++ spectrogram.h | 2 +- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index a970c4f..796ff6d 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -83,29 +83,17 @@ bool MainWindow::eventFilter(QObject * /*obj*/, QEvent *event) QMouseEvent *mouseEvent = (QMouseEvent*)event; QRect rb = rubberBand->geometry(); - int topSample = spectrogram.lineToSample(scrollArea.verticalScrollBar()->value() + rb.top()); - int bottomSample = spectrogram.lineToSample(scrollArea.verticalScrollBar()->value() + rb.bottom()); + off_t topSample = spectrogram.lineToSample(scrollArea.verticalScrollBar()->value() + rb.top()); + off_t bottomSample = spectrogram.lineToSample(scrollArea.verticalScrollBar()->value() + rb.bottom()); - int leftFreq = (int)(scrollArea.horizontalScrollBar()->value() + rb.left())*spectrogram.getSampleRate()/spectrogram.getFFTSize(); - int rightFreq = (int)(scrollArea.horizontalScrollBar()->value() + rb.right())*spectrogram.getSampleRate()/spectrogram.getFFTSize(); + int offset = scrollArea.horizontalScrollBar()->value(); + int width = spectrogram.width(); + float left = (float)clamp(offset + rb.left(), 0, width) / width - 0.5; + float right = (float)clamp(offset + rb.right(), 0, width) / width - 0.5; - QStringList command; - command << "cut_sample" << spectrogram.getFileName() << QString::number(spectrogram.getSampleRate()) - << QString::number(spectrogram.lineToSample(scrollArea.verticalScrollBar()->value() + rb.top())) - << QString::number(spectrogram.lineToSample(scrollArea.verticalScrollBar()->value() + rb.bottom())) + selectionTime = {topSample, bottomSample}; + selectionFreq = {left, right}; - << QString::number((long int)(scrollArea.horizontalScrollBar()->value() + rb.left())*spectrogram.getSampleRate()/spectrogram.getFFTSize() + spectrogram.getCenterFreq() - spectrogram.getSampleRate()/2) - << QString::number((long int)(scrollArea.horizontalScrollBar()->value() + rb.right())*spectrogram.getSampleRate()/spectrogram.getFFTSize() + spectrogram.getCenterFreq() - spectrogram.getSampleRate()/2) - << QString::number(spectrogram.getCenterFreq()) - ; - - if(system(command.join(" ").toLatin1().data())) { - QMessageBox msgBox; - msgBox.setText("cut_sample call failed."); - msgBox.exec(); - }; - rubberBand->hide(); - rubberBand->clearMask(); return true; }; return false; diff --git a/mainwindow.h b/mainwindow.h index 3ab4e0c..a4fefff 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -45,6 +45,8 @@ private: QScrollArea scrollArea; Spectrogram spectrogram; SpectrogramControls *dock; + std::pair selectionTime; + std::pair selectionFreq; off_t getCenterSample(); int getScrollPos(off_t sample); diff --git a/spectrogram.h b/spectrogram.h index bb0003e..bc7c8f6 100644 --- a/spectrogram.h +++ b/spectrogram.h @@ -40,6 +40,7 @@ public: QSize sizeHint() const; int getHeight(); int getStride(); + off_t lineToSample(off_t line); public slots: void openFile(QString fileName); @@ -75,7 +76,6 @@ private: float* getFFTTile(off_t tile); void getLine(float *dest, off_t sample); void paintTimeAxis(QPainter *painter, QRect rect); - off_t lineToSample(off_t line); int sampleToLine(off_t sample); QString sampleToTime(off_t sample); int linesPerTile(); From c2489985279fda4572d0dc3018ae73d2a15e6235 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 11 Jan 2016 00:54:09 +0000 Subject: [PATCH 23/99] selection: Clear selection if too small --- mainwindow.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 796ff6d..d8abdd8 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -91,9 +91,13 @@ bool MainWindow::eventFilter(QObject * /*obj*/, QEvent *event) float left = (float)clamp(offset + rb.left(), 0, width) / width - 0.5; float right = (float)clamp(offset + rb.right(), 0, width) / width - 0.5; - selectionTime = {topSample, bottomSample}; - selectionFreq = {left, right}; - + if (rb.width() > 10 && rb.height() > 10) { + selectionTime = {topSample, bottomSample}; + selectionFreq = {left, right}; + } else { + rubberBand->hide(); + rubberBand->clearMask(); + } return true; }; return false; From 35cdd56064824887be4253770268a66fc881eab0 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 11 Jan 2016 01:02:50 +0000 Subject: [PATCH 24/99] selection: Emit signals --- mainwindow.cpp | 2 ++ mainwindow.h | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/mainwindow.cpp b/mainwindow.cpp index d8abdd8..3b8896b 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -94,9 +94,11 @@ bool MainWindow::eventFilter(QObject * /*obj*/, QEvent *event) if (rb.width() > 10 && rb.height() > 10) { selectionTime = {topSample, bottomSample}; selectionFreq = {left, right}; + emit selectionChanged(selectionTime, selectionFreq); } else { rubberBand->hide(); rubberBand->clearMask(); + emit selectionCleared(); } return true; }; diff --git a/mainwindow.h b/mainwindow.h index a4fefff..b271add 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -38,6 +38,10 @@ public slots: void setFFTSize(int size); void setZoomLevel(int zoom); +signals: + void selectionChanged(std::pair selectionTime, std::pair selectionFreq); + void selectionCleared(); + protected: bool eventFilter(QObject *obj, QEvent *event); From 3aec698450c9fe8f7e4abc86851551ca30c25444 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 11 Jan 2016 02:04:39 +0000 Subject: [PATCH 25/99] WIP Style --- fft.h | 7 +- grsamplebuffer.cpp | 18 +-- grsamplebuffer.h | 12 +- inputsource.cpp | 6 +- inputsource.h | 4 +- main.cpp | 24 ++-- mainwindow.h | 12 +- memory_sink.h | 22 ++-- memory_sink_impl.h | 38 +++--- memory_source.h | 22 ++-- memory_source_impl.h | 38 +++--- samplebuffer.cpp | 8 +- samplebuffer.h | 12 +- spectrogram.cpp | 278 ++++++++++++++++++++-------------------- spectrogram.h | 102 +++++++-------- spectrogramcontrols.cpp | 66 +++++----- spectrogramcontrols.h | 31 ++--- waveformview.h | 23 ++-- 18 files changed, 375 insertions(+), 348 deletions(-) diff --git a/fft.h b/fft.h index d30c456..adec9a7 100644 --- a/fft.h +++ b/fft.h @@ -21,12 +21,15 @@ #include -class FFT { +class FFT +{ public: FFT(int size); ~FFT(); void process(void *dest, void *source); - int getSize() { return fftSize; } + int getSize() { + return fftSize; + } private: int fftSize; diff --git a/grsamplebuffer.cpp b/grsamplebuffer.cpp index 5c62025..5bddce1 100644 --- a/grsamplebuffer.cpp +++ b/grsamplebuffer.cpp @@ -17,15 +17,15 @@ * along with this program. If not, see . */ - #include "grsamplebuffer.h" - - template - void GRSampleBuffer::work(void *input, void *output, int length) - { - mem_source->set_source(input, length); - mem_sink->set_sink(output, length); - tb->run(); - } +#include "grsamplebuffer.h" + +template +void GRSampleBuffer::work(void *input, void *output, int length) +{ + mem_source->set_source(input, length); + mem_sink->set_sink(output, length); + tb->run(); +} template class GRSampleBuffer, std::complex>; template class GRSampleBuffer, float>; diff --git a/grsamplebuffer.h b/grsamplebuffer.h index f013457..585202f 100644 --- a/grsamplebuffer.h +++ b/grsamplebuffer.h @@ -30,12 +30,12 @@ template class GRSampleBuffer : public SampleBuffer { private: - gr::top_block_sptr tb; - gr::blocks::memory_source::sptr mem_source; - gr::blocks::memory_sink::sptr mem_sink; + gr::top_block_sptr tb; + gr::blocks::memory_source::sptr mem_source; + gr::blocks::memory_sink::sptr mem_sink; public: - GRSampleBuffer(SampleSource *src, gr::top_block_sptr tb, gr::blocks::memory_source::sptr mem_source, gr::blocks::memory_sink::sptr mem_sink) - : SampleBuffer(src), tb(tb), mem_source(mem_source), mem_sink(mem_sink) {}; - virtual void work(void *input, void *output, int count); + GRSampleBuffer(SampleSource *src, gr::top_block_sptr tb, gr::blocks::memory_source::sptr mem_source, gr::blocks::memory_sink::sptr mem_sink) + : SampleBuffer(src), tb(tb), mem_source(mem_source), mem_sink(mem_sink) {}; + virtual void work(void *input, void *output, int count); }; diff --git a/inputsource.cpp b/inputsource.cpp index 9761612..1fea732 100644 --- a/inputsource.cpp +++ b/inputsource.cpp @@ -27,7 +27,8 @@ #include -InputSource::InputSource(const char *filename) { +InputSource::InputSource(const char *filename) +{ m_file = fopen(filename, "rb"); if (m_file == nullptr) throw std::runtime_error("Error opening file"); @@ -43,7 +44,8 @@ InputSource::InputSource(const char *filename) { throw std::runtime_error("Error mmapping file"); } -InputSource::~InputSource() { +InputSource::~InputSource() +{ munmap(m_data, m_file_size); fclose(m_file); } diff --git a/inputsource.h b/inputsource.h index 63be033..ea31e1e 100644 --- a/inputsource.h +++ b/inputsource.h @@ -35,5 +35,7 @@ public: ~InputSource(); std::unique_ptr[]> getSamples(off_t start, off_t length); - off_t count() { return sampleCount; }; + off_t count() { + return sampleCount; + }; }; diff --git a/main.cpp b/main.cpp index c922599..01ee997 100644 --- a/main.cpp +++ b/main.cpp @@ -35,27 +35,27 @@ int main(int argc, char *argv[]) // Add options QCommandLineOption rateOption(QStringList() << "r" << "rate", - QCoreApplication::translate("main", "Set sample rate."), - QCoreApplication::translate("main", "Hz")); + QCoreApplication::translate("main", "Set sample rate."), + QCoreApplication::translate("main", "Hz")); parser.addOption(rateOption); // Process the actual command line parser.process(a); - if (parser.isSet(rateOption)){ - bool ok; - // Use toDouble just for scientific notation support - int rate = parser.value(rateOption).toDouble(&ok); - if(!ok){ - fputs("ERROR: could not parse rate\n", stderr); - return 1; - } - mainWin.changeSampleRate(rate); + if (parser.isSet(rateOption)) { + bool ok; + // Use toDouble just for scientific notation support + int rate = parser.value(rateOption).toDouble(&ok); + if(!ok) { + fputs("ERROR: could not parse rate\n", stderr); + return 1; + } + mainWin.changeSampleRate(rate); } const QStringList args = parser.positionalArguments(); if (args.size()>=1) - mainWin.openFile(args.at(0)); + mainWin.openFile(args.at(0)); mainWin.show(); return a.exec(); diff --git a/mainwindow.h b/mainwindow.h index d9ab8f9..400c10b 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -35,16 +35,16 @@ public: public slots: void openFile(QString fileName); - void setSampleRate(QString rate); - void setFFTSize(int size); - void setZoomLevel(int zoom); + void setSampleRate(QString rate); + void setFFTSize(int size); + void setZoomLevel(int zoom); void spectrogramSliderMoved(int value); signals: void viewChanged(off_t firstSample, off_t lastSample); protected: - bool eventFilter(QObject *obj, QEvent *event); + bool eventFilter(QObject *obj, QEvent *event); private: QScrollArea scrollArea; @@ -54,7 +54,7 @@ private: void emitViewChanged(); off_t getTopSample(); - off_t getCenterSample(); + off_t getCenterSample(); off_t getBottomSample(); - int getScrollPos(off_t sample); + int getScrollPos(off_t sample); }; diff --git a/memory_sink.h b/memory_sink.h index 634444f..afcd2cb 100644 --- a/memory_sink.h +++ b/memory_sink.h @@ -23,19 +23,21 @@ #include #include -namespace gr { - namespace blocks { - class memory_sink : virtual public sync_block - { - public: - typedef boost::shared_ptr sptr; +namespace gr +{ +namespace blocks +{ +class memory_sink : virtual public sync_block +{ +public: + typedef boost::shared_ptr sptr; - static sptr make(size_t itemsize); + static sptr make(size_t itemsize); - virtual void set_sink(void *sink, size_t length) = 0; - }; + virtual void set_sink(void *sink, size_t length) = 0; +}; - } /* namespace blocks */ +} /* namespace blocks */ } /* namespace gr */ #endif /* INCLUDED_GR_MEMORY_SINK_H */ \ No newline at end of file diff --git a/memory_sink_impl.h b/memory_sink_impl.h index 4ceb41f..c2fdac5 100644 --- a/memory_sink_impl.h +++ b/memory_sink_impl.h @@ -22,29 +22,31 @@ #include "memory_sink.h" -namespace gr { - namespace blocks { +namespace gr +{ +namespace blocks +{ - class memory_sink_impl : public memory_sink - { - private: - size_t d_itemsize; - void *d_sink; - size_t d_length; - size_t d_ptr = 0; +class memory_sink_impl : public memory_sink +{ +private: + size_t d_itemsize; + void *d_sink; + size_t d_length; + size_t d_ptr = 0; - public: - memory_sink_impl(size_t itemsize); - ~memory_sink_impl(); +public: + memory_sink_impl(size_t itemsize); + ~memory_sink_impl(); - void set_sink(void *sink, size_t length); + void set_sink(void *sink, size_t length); - int work(int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - }; + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; - } /* namespace blocks */ +} /* namespace blocks */ } /* namespace gr */ #endif /* INCLUDED_GR_MEMORY_SINK_IMPL_H */ \ No newline at end of file diff --git a/memory_source.h b/memory_source.h index f9c75e7..10c3c11 100644 --- a/memory_source.h +++ b/memory_source.h @@ -23,19 +23,21 @@ #include #include -namespace gr { - namespace blocks { - class memory_source : virtual public sync_block - { - public: - typedef boost::shared_ptr sptr; +namespace gr +{ +namespace blocks +{ +class memory_source : virtual public sync_block +{ +public: + typedef boost::shared_ptr sptr; - static sptr make(size_t itemsize); + static sptr make(size_t itemsize); - virtual void set_source(void *source, size_t length) = 0; - }; + virtual void set_source(void *source, size_t length) = 0; +}; - } /* namespace blocks */ +} /* namespace blocks */ } /* namespace gr */ #endif /* INCLUDED_GR_MEMORY_SOURCE_H */ \ No newline at end of file diff --git a/memory_source_impl.h b/memory_source_impl.h index 020946a..7c57a0e 100644 --- a/memory_source_impl.h +++ b/memory_source_impl.h @@ -22,29 +22,31 @@ #include "memory_source.h" -namespace gr { - namespace blocks { +namespace gr +{ +namespace blocks +{ - class memory_source_impl : public memory_source - { - private: - size_t d_itemsize; - void *d_source; - size_t d_length; - size_t d_ptr = 0; +class memory_source_impl : public memory_source +{ +private: + size_t d_itemsize; + void *d_source; + size_t d_length; + size_t d_ptr = 0; - public: - memory_source_impl(size_t itemsize); - ~memory_source_impl(); +public: + memory_source_impl(size_t itemsize); + ~memory_source_impl(); - void set_source(void *source, size_t length); + void set_source(void *source, size_t length); - int work(int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - }; + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; - } /* namespace blocks */ +} /* namespace blocks */ } /* namespace gr */ #endif /* INCLUDED_GR_MEMORY_SOURCE_IMPL_H */ \ No newline at end of file diff --git a/samplebuffer.cpp b/samplebuffer.cpp index 9cc518d..a34a2f8 100644 --- a/samplebuffer.cpp +++ b/samplebuffer.cpp @@ -22,10 +22,10 @@ template std::unique_ptr SampleBuffer::getSamples(off_t start, off_t length) { - auto samples = src->getSamples(start, length); - std::unique_ptr dest(new Tout[length]); - work(samples.get(), dest.get(), length); - return dest; + auto samples = src->getSamples(start, length); + std::unique_ptr dest(new Tout[length]); + work(samples.get(), dest.get(), length); + return dest; } template class SampleBuffer, std::complex>; diff --git a/samplebuffer.h b/samplebuffer.h index 53c0081..b4b065f 100644 --- a/samplebuffer.h +++ b/samplebuffer.h @@ -27,11 +27,13 @@ template class SampleBuffer : public SampleSource { private: - std::shared_ptr> src; + std::shared_ptr> src; public: - SampleBuffer(SampleSource *src) : src(src) {}; - virtual std::unique_ptr getSamples(off_t start, off_t length); - virtual void work(void *input, void *output, int count) = 0; - virtual off_t count() { return src->count(); }; + SampleBuffer(SampleSource *src) : src(src) {}; + virtual std::unique_ptr getSamples(off_t start, off_t length); + virtual void work(void *input, void *output, int count) = 0; + virtual off_t count() { + return src->count(); + }; }; diff --git a/spectrogram.cpp b/spectrogram.cpp index c23afe5..41e0d19 100644 --- a/spectrogram.cpp +++ b/spectrogram.cpp @@ -30,227 +30,233 @@ Spectrogram::Spectrogram() { - sampleRate = 8000000; - setFFTSize(1024); - zoomLevel = 0; - powerMax = 0.0f; - powerMin = -50.0f; + sampleRate = 8000000; + setFFTSize(1024); + zoomLevel = 0; + powerMax = 0.0f; + powerMin = -50.0f; - for (int i = 0; i < 256; i++) { - float p = (float)i / 256; - colormap[i] = QColor::fromHsvF(p * 0.83f, 1.0, 1.0 - p).rgba(); - } + for (int i = 0; i < 256; i++) { + float p = (float)i / 256; + colormap[i] = QColor::fromHsvF(p * 0.83f, 1.0, 1.0 - p).rgba(); + } } Spectrogram::~Spectrogram() { - delete fft; - delete inputSource; + delete fft; + delete inputSource; } -QSize Spectrogram::sizeHint() const { - return QSize(1024, 2048); +QSize Spectrogram::sizeHint() const +{ + return QSize(1024, 2048); } void Spectrogram::openFile(QString fileName) { - if (fileName != nullptr) { - try { - InputSource *newFile = new InputSource(fileName.toUtf8().constData()); - delete inputSource; - pixmapCache.clear(); - fftCache.clear(); - inputSource = newFile; - resize(fftSize, getHeight()); - } catch (std::runtime_error e) { - // TODO: display error - } - } + if (fileName != nullptr) { + try { + InputSource *newFile = new InputSource(fileName.toUtf8().constData()); + delete inputSource; + pixmapCache.clear(); + fftCache.clear(); + inputSource = newFile; + resize(fftSize, getHeight()); + } catch (std::runtime_error e) { + // TODO: display error + } + } } -template const T& clamp (const T& value, const T& min, const T& max) { +template const T& clamp (const T& value, const T& min, const T& max) +{ return std::min(max, std::max(min, value)); } void Spectrogram::paintEvent(QPaintEvent *event) { - QRect rect = event->rect(); - QPainter painter(this); - painter.fillRect(rect, Qt::black); + QRect rect = event->rect(); + QPainter painter(this); + painter.fillRect(rect, Qt::black); - if (inputSource != nullptr) { - int height = rect.height(); - off_t y = rect.y(); + if (inputSource != nullptr) { + int height = rect.height(); + off_t y = rect.y(); - QImage image(fftSize, height, QImage::Format_RGB32); + QImage image(fftSize, height, QImage::Format_RGB32); - while (height > 0) { - int tileOffset = y % linesPerTile(); // To handle drawing a partial first tile - int drawHeight = std::min(linesPerTile() - tileOffset, height); // Draw rest of first tile, full tile, or partial final tile - off_t tileId = lineToSample(y - tileOffset); - QPixmap *tile = getPixmapTile(tileId); - painter.drawPixmap(QRect(0, y, fftSize, drawHeight), *tile, QRect(0, tileOffset, fftSize, drawHeight)); - y += drawHeight; - height -= drawHeight; - } + while (height > 0) { + int tileOffset = y % linesPerTile(); // To handle drawing a partial first tile + int drawHeight = std::min(linesPerTile() - tileOffset, height); // Draw rest of first tile, full tile, or partial final tile + off_t tileId = lineToSample(y - tileOffset); + QPixmap *tile = getPixmapTile(tileId); + painter.drawPixmap(QRect(0, y, fftSize, drawHeight), *tile, QRect(0, tileOffset, fftSize, drawHeight)); + y += drawHeight; + height -= drawHeight; + } - paintTimeAxis(&painter, rect); - } + paintTimeAxis(&painter, rect); + } } QPixmap* Spectrogram::getPixmapTile(off_t tile) { - QPixmap *obj = pixmapCache.object(TileCacheKey(fftSize, zoomLevel, tile)); - if (obj != 0) - return obj; + QPixmap *obj = pixmapCache.object(TileCacheKey(fftSize, zoomLevel, tile)); + if (obj != 0) + return obj; - float *fftTile = getFFTTile(tile); - obj = new QPixmap(fftSize, linesPerTile()); - QImage image(fftSize, linesPerTile(), QImage::Format_RGB32); - for (int y = 0; y < linesPerTile(); y++) { - float *line = &fftTile[y * fftSize]; - for (int x = 0; x < fftSize; x++) { - float powerRange = std::abs(int(powerMin - powerMax)); - float normPower = (line[x] - powerMax) * -1.0f / powerRange; - normPower = clamp(normPower, 0.0f, 1.0f); + float *fftTile = getFFTTile(tile); + obj = new QPixmap(fftSize, linesPerTile()); + QImage image(fftSize, linesPerTile(), QImage::Format_RGB32); + for (int y = 0; y < linesPerTile(); y++) { + float *line = &fftTile[y * fftSize]; + for (int x = 0; x < fftSize; x++) { + float powerRange = std::abs(int(powerMin - powerMax)); + float normPower = (line[x] - powerMax) * -1.0f / powerRange; + normPower = clamp(normPower, 0.0f, 1.0f); - image.setPixel(x, y, colormap[(uint8_t)(normPower * (256 - 1))]); - } - } - obj->convertFromImage(image); - pixmapCache.insert(TileCacheKey(fftSize, zoomLevel, tile), obj); - return obj; + image.setPixel(x, y, colormap[(uint8_t)(normPower * (256 - 1))]); + } + } + obj->convertFromImage(image); + pixmapCache.insert(TileCacheKey(fftSize, zoomLevel, tile), obj); + return obj; } float* Spectrogram::getFFTTile(off_t tile) { - float *obj = fftCache.object(TileCacheKey(fftSize, zoomLevel, tile)); - if (obj != 0) - return obj; + float *obj = fftCache.object(TileCacheKey(fftSize, zoomLevel, tile)); + if (obj != 0) + return obj; - float *dest = new float[tileSize]; - float *ptr = dest; - off_t sample = tile; - while ((ptr - dest) < tileSize) { - getLine(ptr, sample); - sample += getStride(); - ptr += fftSize; - } - fftCache.insert(TileCacheKey(fftSize, zoomLevel, tile), dest); - return dest; + float *dest = new float[tileSize]; + float *ptr = dest; + off_t sample = tile; + while ((ptr - dest) < tileSize) { + getLine(ptr, sample); + sample += getStride(); + ptr += fftSize; + } + fftCache.insert(TileCacheKey(fftSize, zoomLevel, tile), dest); + return dest; } void Spectrogram::getLine(float *dest, off_t sample) { - if (inputSource && fft) { - auto buffer = inputSource->getSamples(sample, fftSize); + if (inputSource && fft) { + auto buffer = inputSource->getSamples(sample, fftSize); - for (int i = 0; i < fftSize; i++) { - buffer[i].real(buffer[i].real() * window[i]); - buffer[i].imag(buffer[i].imag() * window[i]); - } + for (int i = 0; i < fftSize; i++) { + buffer[i].real(buffer[i].real() * window[i]); + buffer[i].imag(buffer[i].imag() * window[i]); + } - fft->process(buffer.get(), buffer.get()); - for (int i = 0; i < fftSize; i++) { - int k = (i + fftSize / 2) % fftSize; - float re = buffer[k].real(); - float im = buffer[k].imag(); - float mag = sqrt(re * re + im * im) / fftSize; - float magdb = 10 * log2(mag) / log2(10); - *dest = magdb; - dest++; - } - } + fft->process(buffer.get(), buffer.get()); + for (int i = 0; i < fftSize; i++) { + int k = (i + fftSize / 2) % fftSize; + float re = buffer[k].real(); + float im = buffer[k].imag(); + float mag = sqrt(re * re + im * im) / fftSize; + float magdb = 10 * log2(mag) / log2(10); + *dest = magdb; + dest++; + } + } } void Spectrogram::paintTimeAxis(QPainter *painter, QRect rect) { - // Round up for firstLine and round each to nearest linesPerGraduation - int firstLine = ((rect.y() + linesPerGraduation - 1) / linesPerGraduation) * linesPerGraduation; - int lastLine = ((rect.y() + rect.height()) / linesPerGraduation) * linesPerGraduation; + // Round up for firstLine and round each to nearest linesPerGraduation + int firstLine = ((rect.y() + linesPerGraduation - 1) / linesPerGraduation) * linesPerGraduation; + int lastLine = ((rect.y() + rect.height()) / linesPerGraduation) * linesPerGraduation; - painter->save(); - QPen pen(Qt::white, 1, Qt::SolidLine); - painter->setPen(pen); - QFontMetrics fm(painter->font()); - int textOffset = fm.ascent() / 2 - 1; - for (int line = firstLine; line <= lastLine; line += linesPerGraduation) { - painter->drawLine(0, line, 10, line); - painter->drawText(12, line + textOffset, sampleToTime(lineToSample(line))); - } - painter->restore(); + painter->save(); + QPen pen(Qt::white, 1, Qt::SolidLine); + painter->setPen(pen); + QFontMetrics fm(painter->font()); + int textOffset = fm.ascent() / 2 - 1; + for (int line = firstLine; line <= lastLine; line += linesPerGraduation) { + painter->drawLine(0, line, 10, line); + painter->drawText(12, line + textOffset, sampleToTime(lineToSample(line))); + } + painter->restore(); } void Spectrogram::setSampleRate(int rate) { - sampleRate = rate; - update(); + sampleRate = rate; + update(); } void Spectrogram::setFFTSize(int size) { - fftSize = size; - delete fft; - fft = new FFT(fftSize); + fftSize = size; + delete fft; + fft = 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))); - } + window.reset(new float[fftSize]); + for (int i = 0; i < fftSize; i++) { + window[i] = 0.5f * (1.0f - cos(Tau * i / (fftSize - 1))); + } - resize(fftSize, getHeight()); + resize(fftSize, getHeight()); } void Spectrogram::setPowerMax(int power) { - powerMax = power; - pixmapCache.clear(); - update(); + powerMax = power; + pixmapCache.clear(); + update(); } void Spectrogram::setPowerMin(int power) { - powerMin = power; - pixmapCache.clear(); - update(); + powerMin = power; + pixmapCache.clear(); + update(); } void Spectrogram::setZoomLevel(int zoom) { - zoomLevel = clamp(zoom, 0, (int)log2(fftSize)); - resize(fftSize, getHeight()); + zoomLevel = clamp(zoom, 0, (int)log2(fftSize)); + resize(fftSize, getHeight()); } int Spectrogram::getHeight() { - if (!inputSource) - return 0; + if (!inputSource) + return 0; - return inputSource->count() / getStride(); + return inputSource->count() / getStride(); } int Spectrogram::getStride() { - return fftSize / pow(2, zoomLevel); + return fftSize / pow(2, zoomLevel); } -off_t Spectrogram::lineToSample(off_t line) { - return line * getStride(); +off_t Spectrogram::lineToSample(off_t line) +{ + return line * getStride(); } -int Spectrogram::sampleToLine(off_t sample) { - return sample / getStride(); +int Spectrogram::sampleToLine(off_t sample) +{ + return sample / getStride(); } QString Spectrogram::sampleToTime(off_t sample) { - return QString::number((float)sample / sampleRate).append("s"); + return QString::number((float)sample / sampleRate).append("s"); } -int Spectrogram::linesPerTile() { - return tileSize / fftSize; +int Spectrogram::linesPerTile() +{ + return tileSize / fftSize; } -uint qHash(const TileCacheKey &key, uint seed) { - return key.fftSize ^ key.zoomLevel ^ key.sample ^ seed; +uint qHash(const TileCacheKey &key, uint seed) +{ + return key.fftSize ^ key.zoomLevel ^ key.sample ^ seed; } diff --git a/spectrogram.h b/spectrogram.h index 44f278b..301413e 100644 --- a/spectrogram.h +++ b/spectrogram.h @@ -31,72 +31,74 @@ static const double Tau = M_PI * 2.0; class TileCacheKey; -class Spectrogram : public QWidget { - Q_OBJECT +class Spectrogram : public QWidget +{ + Q_OBJECT public: - Spectrogram(); - ~Spectrogram(); - QSize sizeHint() const; - int getHeight(); - int getStride(); + Spectrogram(); + ~Spectrogram(); + QSize sizeHint() const; + int getHeight(); + int getStride(); - InputSource *inputSource = nullptr; + InputSource *inputSource = nullptr; public slots: - void openFile(QString fileName); - void setSampleRate(int rate); - void setFFTSize(int size); - void setPowerMax(int power); - void setPowerMin(int power); - void setZoomLevel(int zoom); + void openFile(QString fileName); + void setSampleRate(int rate); + void setFFTSize(int size); + void setPowerMax(int power); + void setPowerMin(int power); + void setZoomLevel(int zoom); protected: - void paintEvent(QPaintEvent *event); + void paintEvent(QPaintEvent *event); private: - const int linesPerGraduation = 50; - const int tileSize = 65536; // This must be a multiple of the maximum FFT size + const int linesPerGraduation = 50; + const int tileSize = 65536; // This must be a multiple of the maximum FFT size - FFT *fft = nullptr; - std::unique_ptr window; - fftwf_complex *lineBuffer = nullptr; - QCache pixmapCache; - QCache fftCache; - uint colormap[256]; + FFT *fft = nullptr; + std::unique_ptr window; + fftwf_complex *lineBuffer = nullptr; + QCache pixmapCache; + QCache fftCache; + uint colormap[256]; - int sampleRate; - int fftSize; - int zoomLevel; - float powerMax; - float powerMin; + int sampleRate; + int fftSize; + int zoomLevel; + float powerMax; + float powerMin; - QPixmap* getPixmapTile(off_t tile); - float* getFFTTile(off_t tile); - void getLine(float *dest, off_t sample); - void paintTimeAxis(QPainter *painter, QRect rect); - off_t lineToSample(off_t line); - int sampleToLine(off_t sample); - QString sampleToTime(off_t sample); - int linesPerTile(); + QPixmap* getPixmapTile(off_t tile); + float* getFFTTile(off_t tile); + void getLine(float *dest, off_t sample); + void paintTimeAxis(QPainter *painter, QRect rect); + off_t lineToSample(off_t line); + int sampleToLine(off_t sample); + QString sampleToTime(off_t sample); + int linesPerTile(); }; -class TileCacheKey { +class TileCacheKey +{ public: - TileCacheKey(int fftSize, int zoomLevel, off_t sample) { - this->fftSize = fftSize; - this->zoomLevel = zoomLevel; - this->sample = sample; - } + TileCacheKey(int fftSize, int zoomLevel, off_t sample) { + this->fftSize = fftSize; + this->zoomLevel = zoomLevel; + this->sample = sample; + } - bool operator==(const TileCacheKey &k2) const { - return (this->fftSize == k2.fftSize) && - (this->zoomLevel == k2.zoomLevel) && - (this->sample == k2.sample); - } + bool operator==(const TileCacheKey &k2) const { + return (this->fftSize == k2.fftSize) && + (this->zoomLevel == k2.zoomLevel) && + (this->sample == k2.sample); + } - int fftSize; - int zoomLevel; - off_t sample; + int fftSize; + int zoomLevel; + off_t sample; }; diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 132d66d..bfd75fc 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -24,54 +24,54 @@ #include SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent) - : QDockWidget::QDockWidget(title, parent) + : QDockWidget::QDockWidget(title, parent) { - widget = new QWidget(this); - layout = new QFormLayout(widget); + widget = new QWidget(this); + layout = new QFormLayout(widget); - fileOpenButton = new QPushButton("Open file...", widget); - layout->addRow(fileOpenButton); + fileOpenButton = new QPushButton("Open file...", widget); + layout->addRow(fileOpenButton); - sampleRate = new QLineEdit("8000000"); - sampleRate->setValidator(new QIntValidator(this)); - layout->addRow(new QLabel(tr("Sample rate:")), sampleRate); + sampleRate = new QLineEdit("8000000"); + sampleRate->setValidator(new QIntValidator(this)); + layout->addRow(new QLabel(tr("Sample rate:")), sampleRate); - fftSizeSlider = new QSlider(Qt::Horizontal, widget); - fftSizeSlider->setRange(7, 13); - fftSizeSlider->setValue(10); - layout->addRow(new QLabel(tr("FFT size:")), fftSizeSlider); + fftSizeSlider = new QSlider(Qt::Horizontal, widget); + fftSizeSlider->setRange(7, 13); + fftSizeSlider->setValue(10); + layout->addRow(new QLabel(tr("FFT size:")), fftSizeSlider); - zoomLevelSlider = new QSlider(Qt::Horizontal, widget); - zoomLevelSlider->setRange(0, 10); - zoomLevelSlider->setValue(0); - layout->addRow(new QLabel(tr("Zoom:")), zoomLevelSlider); + zoomLevelSlider = new QSlider(Qt::Horizontal, widget); + zoomLevelSlider->setRange(0, 10); + zoomLevelSlider->setValue(0); + layout->addRow(new QLabel(tr("Zoom:")), zoomLevelSlider); - powerMaxSlider = new QSlider(Qt::Horizontal, widget); - powerMaxSlider->setRange(-100, 20); - powerMaxSlider->setValue(0); - layout->addRow(new QLabel(tr("Power max:")), powerMaxSlider); + powerMaxSlider = new QSlider(Qt::Horizontal, widget); + powerMaxSlider->setRange(-100, 20); + powerMaxSlider->setValue(0); + layout->addRow(new QLabel(tr("Power max:")), powerMaxSlider); - powerMinSlider = new QSlider(Qt::Horizontal, widget); - powerMinSlider->setRange(-100, 20); - powerMinSlider->setValue(-50); - layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); + powerMinSlider = new QSlider(Qt::Horizontal, widget); + powerMinSlider->setRange(-100, 20); + powerMinSlider->setValue(-50); + layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); - widget->setLayout(layout); - setWidget(widget); + widget->setLayout(layout); + setWidget(widget); - connect(fftSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(fftSizeSliderChanged(int))); - connect(fileOpenButton, SIGNAL(clicked()), this, SLOT(fileOpenButtonClicked())); + connect(fftSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(fftSizeSliderChanged(int))); + connect(fileOpenButton, SIGNAL(clicked()), this, SLOT(fileOpenButtonClicked())); } void SpectrogramControls::fftSizeSliderChanged(int size) { - emit fftSizeChanged((int)pow(2, size)); + emit fftSizeChanged((int)pow(2, size)); } void SpectrogramControls::fileOpenButtonClicked() { - QString fileName = QFileDialog::getOpenFileName( - this, tr("Open File"), "", tr("Sample file (*.cfile *.bin);;All files (*)") - ); - emit openFile(fileName); + QString fileName = QFileDialog::getOpenFileName( + this, tr("Open File"), "", tr("Sample file (*.cfile *.bin);;All files (*)") + ); + emit openFile(fileName); } \ No newline at end of file diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index 1e03f4c..1979378 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -25,28 +25,29 @@ #include #include -class SpectrogramControls : public QDockWidget { - Q_OBJECT +class SpectrogramControls : public QDockWidget +{ + Q_OBJECT public: - SpectrogramControls(const QString & title, QWidget * parent); + SpectrogramControls(const QString & title, QWidget * parent); signals: - void fftSizeChanged(int size); - void openFile(QString fileName); + void fftSizeChanged(int size); + void openFile(QString fileName); private slots: - void fftSizeSliderChanged(int size); - void fileOpenButtonClicked(); + void fftSizeSliderChanged(int size); + void fileOpenButtonClicked(); private: - QWidget *widget; - QFormLayout *layout; + QWidget *widget; + QFormLayout *layout; public: - QPushButton *fileOpenButton; - QLineEdit *sampleRate; - QSlider *fftSizeSlider; - QSlider *zoomLevelSlider; - QSlider *powerMaxSlider; - QSlider *powerMinSlider; + QPushButton *fileOpenButton; + QLineEdit *sampleRate; + QSlider *fftSizeSlider; + QSlider *zoomLevelSlider; + QSlider *powerMaxSlider; + QSlider *powerMinSlider; }; \ No newline at end of file diff --git a/waveformview.h b/waveformview.h index ab80768..0bc84fc 100644 --- a/waveformview.h +++ b/waveformview.h @@ -23,24 +23,25 @@ #include #include "inputsource.h" -class WaveformView : public QDockWidget { - Q_OBJECT +class WaveformView : public QDockWidget +{ + Q_OBJECT public: - WaveformView(); + WaveformView(); public slots: - void inputSourceChanged(AbstractSampleSource *input); - void viewChanged(off_t firstSample, off_t lastSample); + void inputSourceChanged(AbstractSampleSource *input); + void viewChanged(off_t firstSample, off_t lastSample); protected: - void paintEvent(QPaintEvent *event); + void paintEvent(QPaintEvent *event); private: - std::vector> sampleSources; - off_t firstSample = 0; - off_t lastSample = 0; - QRgb colormap[255]; + std::vector> sampleSources; + off_t firstSample = 0; + off_t lastSample = 0; + QRgb colormap[255]; - void plot(QPainter *painter, QRect &rect, float *samples, off_t count, int step); + void plot(QPainter *painter, QRect &rect, float *samples, off_t count, int step); }; \ No newline at end of file From 5728d46bbab4af0953cee07cace5e9b9b98b8a46 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 11 Jan 2016 03:48:21 +0000 Subject: [PATCH 26/99] waveform: Fix erroneous shared_ptr --- samplebuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samplebuffer.h b/samplebuffer.h index b4b065f..93fc135 100644 --- a/samplebuffer.h +++ b/samplebuffer.h @@ -27,7 +27,7 @@ template class SampleBuffer : public SampleSource { private: - std::shared_ptr> src; + SampleSource *src; public: SampleBuffer(SampleSource *src) : src(src) {}; From dbd45d05196b253eab2da22afe62d5f640b2b250 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 11 Jan 2016 03:52:43 +0000 Subject: [PATCH 27/99] waveform: Setup freq_xlating_fir_filter based on selection --- mainwindow.cpp | 3 +++ waveformview.cpp | 46 +++++++++++++++++++++++++++++++++++++++------- waveformview.h | 7 +++++++ 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 796f990..fe02602 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -37,6 +37,9 @@ MainWindow::MainWindow() wave = new WaveformView(); addDockWidget(Qt::BottomDockWidgetArea, wave); connect(this, SIGNAL(viewChanged(off_t, off_t)), wave, SLOT(viewChanged(off_t, off_t))); + connect(this, SIGNAL(selectionChanged(std::pair, std::pair)), + wave, SLOT(selectionChanged(std::pair, std::pair))); + connect(this, SIGNAL(selectionCleared()), wave, SLOT(selectionCleared())); connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); connect(dock->sampleRate, SIGNAL(textChanged(QString)), this, SLOT(setSampleRate(QString))); diff --git a/waveformview.cpp b/waveformview.cpp index 3643000..9c567e0 100644 --- a/waveformview.cpp +++ b/waveformview.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "grsamplebuffer.h" #include "memory_sink.h" #include "memory_source.h" @@ -37,7 +39,7 @@ WaveformView::WaveformView() } } -void WaveformView::inputSourceChanged(AbstractSampleSource *src) +void WaveformView::refreshSources() { sampleSources.clear(); @@ -45,8 +47,19 @@ void WaveformView::inputSourceChanged(AbstractSampleSource *src) auto iq_mem_source = gr::blocks::memory_source::make(8); auto iq_mem_sink = gr::blocks::memory_sink::make(8); auto multiply = gr::blocks::multiply_const_cc::make(20); - iq_tb->connect(iq_mem_source, 0, multiply, 0); - iq_tb->connect(multiply, 0, iq_mem_sink, 0); + if (selection) { + float centre = (selectionFreq.first + selectionFreq.second) / 2; + float cutoff = std::abs(selectionFreq.first - centre); + auto lp_taps = gr::filter::firdes::low_pass(1.0, 1.0, cutoff, cutoff / 2); + auto filter = gr::filter::freq_xlating_fir_filter_ccf::make(1, lp_taps, centre, 1.0); + + iq_tb->connect(iq_mem_source, 0, filter, 0); + iq_tb->connect(filter, 0, multiply, 0); + iq_tb->connect(multiply, 0, iq_mem_sink, 0); + } else { + iq_tb->connect(iq_mem_source, 0, multiply, 0); + iq_tb->connect(multiply, 0, iq_mem_sink, 0); + } gr::top_block_sptr quad_demod_tb = gr::make_top_block("quad_demod"); auto quad_demod_mem_source = gr::blocks::memory_source::make(8); @@ -55,23 +68,42 @@ void WaveformView::inputSourceChanged(AbstractSampleSource *src) 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)); + update(); +} + +void WaveformView::inputSourceChanged(AbstractSampleSource *src) +{ auto derived = dynamic_cast>*>(src); if (derived == nullptr) throw new std::runtime_error("SampleSource doesn't provide correct type for GRSampleBuffer"); - sampleSources.emplace_back(new GRSampleBuffer, std::complex>(derived, iq_tb, iq_mem_source, iq_mem_sink)); - sampleSources.emplace_back(new GRSampleBuffer, float>(derived, quad_demod_tb, quad_demod_mem_source, quad_demod_mem_sink)); - update(); + mainSampleSource = derived; + refreshSources(); } void WaveformView::viewChanged(off_t firstSample, off_t lastSample) { this->firstSample = firstSample; this->lastSample = lastSample; - qDebug() << "viewChanged(" << firstSample << ", " << lastSample << ")"; update(); } +void WaveformView::selectionChanged(std::pair selectionTime, std::pair selectionFreq) +{ + this->selectionTime = selectionTime; + this->selectionFreq = selectionFreq; + selection = true; + refreshSources(); +} + +void WaveformView::selectionCleared() +{ + selection = false; + refreshSources(); +} + void WaveformView::paintEvent(QPaintEvent *event) { if (lastSample - firstSample <= 0) return; diff --git a/waveformview.h b/waveformview.h index 0bc84fc..5ab6911 100644 --- a/waveformview.h +++ b/waveformview.h @@ -33,15 +33,22 @@ public: public slots: void inputSourceChanged(AbstractSampleSource *input); void viewChanged(off_t firstSample, off_t lastSample); + void selectionChanged(std::pair selectionTime, std::pair selectionFreq); + void selectionCleared(); protected: void paintEvent(QPaintEvent *event); private: + SampleSource> *mainSampleSource = nullptr; std::vector> sampleSources; off_t firstSample = 0; off_t lastSample = 0; + bool selection = false; + std::pair selectionTime; + std::pair selectionFreq; QRgb colormap[255]; + void refreshSources(); void plot(QPainter *painter, QRect &rect, float *samples, off_t count, int step); }; \ No newline at end of file From d1a580f0b9512ae660f66ea75fd45fd8b86e709f Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 24 Jan 2016 22:54:54 +0000 Subject: [PATCH 28/99] Rename WaveformView -> PlotView --- CMakeLists.txt | 2 +- mainwindow.cpp | 12 ++++++------ mainwindow.h | 4 ++-- waveformview.cpp => plotview.cpp | 20 ++++++++++---------- waveformview.h => plotview.h | 6 +++--- 5 files changed, 22 insertions(+), 22 deletions(-) rename waveformview.cpp => plotview.cpp (90%) rename waveformview.h => plotview.h (93%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a2928e..0836825 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,10 +34,10 @@ list(APPEND inspectrum_sources inputsource.cpp memory_sink_impl.cc memory_source_impl.cc + plotview.cpp samplebuffer.cpp spectrogram.cpp spectrogramcontrols.cpp - waveformview.cpp util.cpp ) diff --git a/mainwindow.cpp b/mainwindow.cpp index fe02602..3e2d809 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -34,12 +34,12 @@ MainWindow::MainWindow() dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); addDockWidget(Qt::LeftDockWidgetArea, dock); - wave = new WaveformView(); - addDockWidget(Qt::BottomDockWidgetArea, wave); - connect(this, SIGNAL(viewChanged(off_t, off_t)), wave, SLOT(viewChanged(off_t, off_t))); + plots = new PlotView(); + addDockWidget(Qt::BottomDockWidgetArea, plots); + connect(this, SIGNAL(viewChanged(off_t, off_t)), plots, SLOT(viewChanged(off_t, off_t))); connect(this, SIGNAL(selectionChanged(std::pair, std::pair)), - wave, SLOT(selectionChanged(std::pair, std::pair))); - connect(this, SIGNAL(selectionCleared()), wave, SLOT(selectionCleared())); + plots, SLOT(selectionChanged(std::pair, std::pair))); + connect(this, SIGNAL(selectionCleared()), plots, SLOT(selectionCleared())); connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); connect(dock->sampleRate, SIGNAL(textChanged(QString)), this, SLOT(setSampleRate(QString))); @@ -180,6 +180,6 @@ void MainWindow::openFile(QString fileName) QString title="%1: %2"; this->setWindowTitle(title.arg(QApplication::applicationName(),fileName.section('/',-1,-1))); spectrogram.openFile(fileName); - wave->inputSourceChanged(spectrogram.inputSource); + plots->inputSourceChanged(spectrogram.inputSource); emitViewChanged(); } diff --git a/mainwindow.h b/mainwindow.h index e224fe0..18fa2c4 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -23,7 +23,7 @@ #include #include "spectrogram.h" #include "spectrogramcontrols.h" -#include "waveformview.h" +#include "plotview.h" class MainWindow : public QMainWindow { @@ -52,7 +52,7 @@ private: QScrollArea scrollArea; Spectrogram spectrogram; SpectrogramControls *dock; - WaveformView *wave; + PlotView *plots; std::pair selectionTime; std::pair selectionFreq; diff --git a/waveformview.cpp b/plotview.cpp similarity index 90% rename from waveformview.cpp rename to plotview.cpp index 9c567e0..cc0d880 100644 --- a/waveformview.cpp +++ b/plotview.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015, Mike Walters + * Copyright (C) 2015-2016, Mike Walters * * This file is part of inspectrum. * @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "waveformview.h" +#include "plotview.h" #include #include #include @@ -29,7 +29,7 @@ #include "memory_sink.h" #include "memory_source.h" -WaveformView::WaveformView() +PlotView::PlotView() { for (int i = 0; i < 128; i++) { colormap[i] = qRgb(i/2, i*1.5, i*1.5); @@ -39,7 +39,7 @@ WaveformView::WaveformView() } } -void WaveformView::refreshSources() +void PlotView::refreshSources() { sampleSources.clear(); @@ -73,7 +73,7 @@ void WaveformView::refreshSources() update(); } -void WaveformView::inputSourceChanged(AbstractSampleSource *src) +void PlotView::inputSourceChanged(AbstractSampleSource *src) { auto derived = dynamic_cast>*>(src); if (derived == nullptr) @@ -83,14 +83,14 @@ void WaveformView::inputSourceChanged(AbstractSampleSource *src) refreshSources(); } -void WaveformView::viewChanged(off_t firstSample, off_t lastSample) +void PlotView::viewChanged(off_t firstSample, off_t lastSample) { this->firstSample = firstSample; this->lastSample = lastSample; update(); } -void WaveformView::selectionChanged(std::pair selectionTime, std::pair selectionFreq) +void PlotView::selectionChanged(std::pair selectionTime, std::pair selectionFreq) { this->selectionTime = selectionTime; this->selectionFreq = selectionFreq; @@ -98,13 +98,13 @@ void WaveformView::selectionChanged(std::pair selectionTime, std:: refreshSources(); } -void WaveformView::selectionCleared() +void PlotView::selectionCleared() { selection = false; refreshSources(); } -void WaveformView::paintEvent(QPaintEvent *event) +void PlotView::paintEvent(QPaintEvent *event) { if (lastSample - firstSample <= 0) return; @@ -133,7 +133,7 @@ void WaveformView::paintEvent(QPaintEvent *event) } } -void WaveformView::plot(QPainter *painter, QRect &rect, float *samples, off_t count, int step = 1) +void PlotView::plot(QPainter *painter, QRect &rect, float *samples, off_t count, int step = 1) { int xprev = 0; int yprev = 0; diff --git a/waveformview.h b/plotview.h similarity index 93% rename from waveformview.h rename to plotview.h index 5ab6911..64f5d2a 100644 --- a/waveformview.h +++ b/plotview.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015, Mike Walters + * Copyright (C) 2015-2016, Mike Walters * * This file is part of inspectrum. * @@ -23,12 +23,12 @@ #include #include "inputsource.h" -class WaveformView : public QDockWidget +class PlotView : public QDockWidget { Q_OBJECT public: - WaveformView(); + PlotView(); public slots: void inputSourceChanged(AbstractSampleSource *input); From 1c6029f60e06e44f39b3ab05acaaa60b5d10fabb Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 28 Feb 2016 17:02:38 +0000 Subject: [PATCH 29/99] plot: Fix inverted y-axis --- plotview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotview.cpp b/plotview.cpp index cc0d880..a20a2a0 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -140,7 +140,7 @@ void PlotView::plot(QPainter *painter, QRect &rect, float *samples, off_t count, for (off_t i = 0; i < count; i++) { float sample = samples[i*step]; int x = (float)i / count * rect.width(); - int y = (sample * rect.height()/2) + rect.height()/2; + int y = rect.height() - ((sample * rect.height()/2) + rect.height()/2); if (x < 0) x = 0; if (y < 0) y = 0; From 84e173fabd22205056bb1fc9fcc1a2340af64ec6 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 7 Feb 2016 21:52:16 +0000 Subject: [PATCH 30/99] util: Make util header-only --- CMakeLists.txt | 1 - util.cpp | 28 ---------------------------- util.h | 6 +++++- 3 files changed, 5 insertions(+), 30 deletions(-) delete mode 100644 util.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0836825..8620b3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,6 @@ list(APPEND inspectrum_sources samplebuffer.cpp spectrogram.cpp spectrogramcontrols.cpp - util.cpp ) INCLUDE(FindPkgConfig) diff --git a/util.cpp b/util.cpp deleted file mode 100644 index 98eab81..0000000 --- a/util.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 -#include "util.h" - -template const T& clamp (const T& value, const T& min, const T& max) -{ - return std::min(max, std::max(min, value)); -} -template const float& clamp(const float&, const float&, const float&); -template const int& clamp(const int&, const int&, const int&); diff --git a/util.h b/util.h index eaf4d77..208183c 100644 --- a/util.h +++ b/util.h @@ -18,5 +18,9 @@ */ #pragma once +#include -template const T& clamp (const T& value, const T& min, const T& max); +template const T& clamp (const T& value, const T& min, const T& max) +{ + return std::min(max, std::max(min, value)); +} From 4c3511c62f709280b7f2cf6cd79fe86bef80651b Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 7 Feb 2016 21:54:18 +0000 Subject: [PATCH 31/99] util: Borrow range_t from @sharebrained --- util.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/util.h b/util.h index 208183c..ee0d8a7 100644 --- a/util.h +++ b/util.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2016, Mike Walters + * Copyright (C) 2016, Jared Boone, ShareBrained Technology, Inc. * * This file is part of inspectrum. * @@ -24,3 +25,34 @@ template const T& clamp (const T& value, const T& min, const T& max) { return std::min(max, std::max(min, value)); } + +template +struct range_t { + const T minimum; + const T maximum; + + const T& clip(const T& value) const { + return clamp(value, minimum, maximum); + } + + void reset_if_outside(T& value, const T& reset_value) const { + if( (value < minimum ) || + (value > maximum ) ) { + value = reset_value; + } + } + + bool below_range(const T& value) const { + return value < minimum; + } + + bool contains(const T& value) const { + // TODO: Subtle gotcha here! Range test doesn't include maximum! + return (value >= minimum) && (value < maximum); + } + + bool out_of_range(const T& value) const { + // TODO: Subtle gotcha here! Range test in contains() doesn't include maximum! + return !contains(value); + } +}; From fb3228c087b10666b447e80233ade73f4271acd8 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 29 Feb 2016 00:25:44 +0000 Subject: [PATCH 32/99] util: Add length to range_t --- util.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util.h b/util.h index ee0d8a7..94ee8fa 100644 --- a/util.h +++ b/util.h @@ -31,6 +31,10 @@ struct range_t { const T minimum; const T maximum; + const T length() { + return maximum - minimum; + } + const T& clip(const T& value) const { return clamp(value, minimum, maximum); } From f43412231d120aec5133b6a7100b2785cb5c3347 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 29 Feb 2016 00:26:11 +0000 Subject: [PATCH 33/99] plot: Move trace plotting out of PlotView --- CMakeLists.txt | 1 + plot.cpp | 20 +++++++++++++ plot.h | 33 +++++++++++++++++++++ plotview.cpp | 68 ++++++++++++++++++------------------------- plotview.h | 4 +-- traceplot.cpp | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ traceplot.h | 41 ++++++++++++++++++++++++++ 7 files changed, 203 insertions(+), 42 deletions(-) create mode 100644 plot.cpp create mode 100644 plot.h create mode 100644 traceplot.cpp create mode 100644 traceplot.h 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); +}; From fd73720eab7b523677dbd5df988cf944a1e559e6 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 29 Feb 2016 22:39:18 +0000 Subject: [PATCH 34/99] Style --- fft.h | 7 +- inputsource.cpp | 6 +- inputsource.h | 4 +- main.cpp | 24 +-- mainwindow.h | 12 +- spectrogram.cpp | 384 +++++++++++++++++++++------------------- spectrogram.h | 132 +++++++------- spectrogramcontrols.cpp | 94 +++++----- spectrogramcontrols.h | 43 ++--- 9 files changed, 363 insertions(+), 343 deletions(-) diff --git a/fft.h b/fft.h index d30c456..adec9a7 100644 --- a/fft.h +++ b/fft.h @@ -21,12 +21,15 @@ #include -class FFT { +class FFT +{ public: FFT(int size); ~FFT(); void process(void *dest, void *source); - int getSize() { return fftSize; } + int getSize() { + return fftSize; + } private: int fftSize; diff --git a/inputsource.cpp b/inputsource.cpp index ca03fd3..197ed98 100644 --- a/inputsource.cpp +++ b/inputsource.cpp @@ -27,7 +27,8 @@ #include -InputSource::InputSource(const char *filename) { +InputSource::InputSource(const char *filename) +{ m_file = fopen(filename, "rb"); if (m_file == nullptr) throw std::runtime_error("Error opening file"); @@ -43,7 +44,8 @@ InputSource::InputSource(const char *filename) { throw std::runtime_error("Error mmapping file"); } -InputSource::~InputSource() { +InputSource::~InputSource() +{ munmap(m_data, m_file_size); fclose(m_file); } diff --git a/inputsource.h b/inputsource.h index ad310f2..ae8b0e0 100644 --- a/inputsource.h +++ b/inputsource.h @@ -36,5 +36,7 @@ public: ~InputSource(); bool getSamples(fftwf_complex *dest, off_t start, int length); - off_t getSampleCount() { return sampleCount; }; + off_t getSampleCount() { + return sampleCount; + }; }; diff --git a/main.cpp b/main.cpp index c922599..01ee997 100644 --- a/main.cpp +++ b/main.cpp @@ -35,27 +35,27 @@ int main(int argc, char *argv[]) // Add options QCommandLineOption rateOption(QStringList() << "r" << "rate", - QCoreApplication::translate("main", "Set sample rate."), - QCoreApplication::translate("main", "Hz")); + QCoreApplication::translate("main", "Set sample rate."), + QCoreApplication::translate("main", "Hz")); parser.addOption(rateOption); // Process the actual command line parser.process(a); - if (parser.isSet(rateOption)){ - bool ok; - // Use toDouble just for scientific notation support - int rate = parser.value(rateOption).toDouble(&ok); - if(!ok){ - fputs("ERROR: could not parse rate\n", stderr); - return 1; - } - mainWin.changeSampleRate(rate); + if (parser.isSet(rateOption)) { + bool ok; + // Use toDouble just for scientific notation support + int rate = parser.value(rateOption).toDouble(&ok); + if(!ok) { + fputs("ERROR: could not parse rate\n", stderr); + return 1; + } + mainWin.changeSampleRate(rate); } const QStringList args = parser.positionalArguments(); if (args.size()>=1) - mainWin.openFile(args.at(0)); + mainWin.openFile(args.at(0)); mainWin.show(); return a.exec(); diff --git a/mainwindow.h b/mainwindow.h index bdc53a4..3ab4e0c 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -34,18 +34,18 @@ public: public slots: void openFile(QString fileName); - void setSampleRate(QString rate); - void setFFTSize(int size); - void setZoomLevel(int zoom); + void setSampleRate(QString rate); + void setFFTSize(int size); + void setZoomLevel(int zoom); protected: - bool eventFilter(QObject *obj, QEvent *event); + bool eventFilter(QObject *obj, QEvent *event); private: QScrollArea scrollArea; Spectrogram spectrogram; SpectrogramControls *dock; - off_t getCenterSample(); - int getScrollPos(off_t sample); + off_t getCenterSample(); + int getScrollPos(off_t sample); }; diff --git a/spectrogram.cpp b/spectrogram.cpp index 195522d..ff18517 100644 --- a/spectrogram.cpp +++ b/spectrogram.cpp @@ -30,301 +30,311 @@ Spectrogram::Spectrogram() { - sampleRate = 8000000; - setFFTSize(1024); - zoomLevel = 0; - powerMax = 0.0f; - powerMin = -50.0f; - timeScaleIsEnabled = true; - deltaDragIsEnabled = true; + sampleRate = 8000000; + setFFTSize(1024); + zoomLevel = 0; + powerMax = 0.0f; + powerMin = -50.0f; + timeScaleIsEnabled = true; + deltaDragIsEnabled = true; - for (int i = 0; i < 256; i++) { - float p = (float)i / 256; - colormap[i] = QColor::fromHsvF(p * 0.83f, 1.0, 1.0 - p).rgba(); - } + for (int i = 0; i < 256; i++) { + float p = (float)i / 256; + colormap[i] = QColor::fromHsvF(p * 0.83f, 1.0, 1.0 - p).rgba(); + } - setMouseTracking(true); + setMouseTracking(true); } Spectrogram::~Spectrogram() { - delete fft; - delete inputSource; + delete fft; + delete inputSource; } -QSize Spectrogram::sizeHint() const { - return QSize(1024, 2048); +QSize Spectrogram::sizeHint() const +{ + return QSize(1024, 2048); } void Spectrogram::openFile(QString fileName) { - if (fileName != nullptr) { - try { - InputSource *newFile = new InputSource(fileName.toUtf8().constData()); - delete inputSource; - pixmapCache.clear(); - fftCache.clear(); - inputSource = newFile; - resize(fftSize, getHeight()); - } catch (std::runtime_error e) { - // TODO: display error - } - } + if (fileName != nullptr) { + try { + InputSource *newFile = new InputSource(fileName.toUtf8().constData()); + delete inputSource; + pixmapCache.clear(); + fftCache.clear(); + inputSource = newFile; + resize(fftSize, getHeight()); + } catch (std::runtime_error e) { + // TODO: display error + } + } } -template const T& clamp (const T& value, const T& min, const T& max) { +template const T& clamp (const T& value, const T& min, const T& max) +{ return std::min(max, std::max(min, value)); } -void Spectrogram::xyToFreqTime(int x, int y, float *freq, float *time) { - *freq = labs(x - (fftSize / 2)) * sampleRate / 2 / (float)fftSize; - *time = (float)lineToSample(y) / sampleRate; +void Spectrogram::xyToFreqTime(int x, int y, float *freq, float *time) +{ + *freq = labs(x - (fftSize / 2)) * sampleRate / 2 / (float)fftSize; + *time = (float)lineToSample(y) / sampleRate; } -void Spectrogram::mouseReleaseEvent(QMouseEvent *event) { - if (deltaDragIsEnabled) { - cursorStartX = -1; - update(); - } +void Spectrogram::mouseReleaseEvent(QMouseEvent *event) +{ + if (deltaDragIsEnabled) { + cursorStartX = -1; + update(); + } } -void Spectrogram::mouseMoveEvent(QMouseEvent *event) { - float freq, time; - xyToFreqTime(event->x(), event->y(), &freq, &time); - emit cursorFrequencyChanged(QString::number(freq) + " Hz"); - emit cursorTimeChanged(QString::number(time) + " s"); - if (cursorStartX != -1) { - float s_freq, s_time; - xyToFreqTime(cursorStartX, cursorStartY, &s_freq, &s_time); - emit deltaFrequencyChanged(QString::number(fabs(s_freq - freq)) + " Hz"); - emit deltaTimeChanged(QString::number(fabs(s_time - time)) + " s"); - cursorEndX = event->x(); - cursorEndY = event->y(); - update(); - } +void Spectrogram::mouseMoveEvent(QMouseEvent *event) +{ + float freq, time; + xyToFreqTime(event->x(), event->y(), &freq, &time); + emit cursorFrequencyChanged(QString::number(freq) + " Hz"); + emit cursorTimeChanged(QString::number(time) + " s"); + if (cursorStartX != -1) { + float s_freq, s_time; + xyToFreqTime(cursorStartX, cursorStartY, &s_freq, &s_time); + emit deltaFrequencyChanged(QString::number(fabs(s_freq - freq)) + " Hz"); + emit deltaTimeChanged(QString::number(fabs(s_time - time)) + " s"); + cursorEndX = event->x(); + cursorEndY = event->y(); + update(); + } } -void Spectrogram::mousePressEvent(QMouseEvent *event) { - if (cursorStartX == -1) { - cursorEndX = cursorStartX = event->x(); - cursorEndY = cursorStartY = event->y(); - } else { - cursorStartX = -1; - } - update(); +void Spectrogram::mousePressEvent(QMouseEvent *event) +{ + if (cursorStartX == -1) { + cursorEndX = cursorStartX = event->x(); + cursorEndY = cursorStartY = event->y(); + } else { + cursorStartX = -1; + } + update(); } void Spectrogram::paintEvent(QPaintEvent *event) { - QRect rect = event->rect(); - QPainter painter(this); - painter.fillRect(rect, Qt::black); + QRect rect = event->rect(); + QPainter painter(this); + painter.fillRect(rect, Qt::black); - if (inputSource != nullptr) { - int height = rect.height(); - off_t y = rect.y(); + if (inputSource != nullptr) { + int height = rect.height(); + off_t y = rect.y(); - QImage image(fftSize, height, QImage::Format_RGB32); + QImage image(fftSize, height, QImage::Format_RGB32); - while (height > 0) { - int tileOffset = y % linesPerTile(); // To handle drawing a partial first tile - int drawHeight = std::min(linesPerTile() - tileOffset, height); // Draw rest of first tile, full tile, or partial final tile - off_t tileId = lineToSample(y - tileOffset); - QPixmap *tile = getPixmapTile(tileId); - painter.drawPixmap(QRect(0, y, fftSize, drawHeight), *tile, QRect(0, tileOffset, fftSize, drawHeight)); - y += drawHeight; - height -= drawHeight; - } + while (height > 0) { + int tileOffset = y % linesPerTile(); // To handle drawing a partial first tile + int drawHeight = std::min(linesPerTile() - tileOffset, height); // Draw rest of first tile, full tile, or partial final tile + off_t tileId = lineToSample(y - tileOffset); + QPixmap *tile = getPixmapTile(tileId); + painter.drawPixmap(QRect(0, y, fftSize, drawHeight), *tile, QRect(0, tileOffset, fftSize, drawHeight)); + y += drawHeight; + height -= drawHeight; + } - paintTimeAxis(&painter, rect); - paintCursors(&painter, rect); - } + paintTimeAxis(&painter, rect); + paintCursors(&painter, rect); + } } QPixmap* Spectrogram::getPixmapTile(off_t tile) { - QPixmap *obj = pixmapCache.object(TileCacheKey(fftSize, zoomLevel, tile)); - if (obj != 0) - return obj; + QPixmap *obj = pixmapCache.object(TileCacheKey(fftSize, zoomLevel, tile)); + if (obj != 0) + return obj; - float *fftTile = getFFTTile(tile); - obj = new QPixmap(fftSize, linesPerTile()); - QImage image(fftSize, linesPerTile(), QImage::Format_RGB32); - for (int y = 0; y < linesPerTile(); y++) { - float *line = &fftTile[y * fftSize]; - for (int x = 0; x < fftSize; x++) { - float powerRange = std::abs(int(powerMin - powerMax)); - float normPower = (line[x] - powerMax) * -1.0f / powerRange; - normPower = clamp(normPower, 0.0f, 1.0f); + float *fftTile = getFFTTile(tile); + obj = new QPixmap(fftSize, linesPerTile()); + QImage image(fftSize, linesPerTile(), QImage::Format_RGB32); + for (int y = 0; y < linesPerTile(); y++) { + float *line = &fftTile[y * fftSize]; + for (int x = 0; x < fftSize; x++) { + float powerRange = std::abs(int(powerMin - powerMax)); + float normPower = (line[x] - powerMax) * -1.0f / powerRange; + normPower = clamp(normPower, 0.0f, 1.0f); - image.setPixel(x, y, colormap[(uint8_t)(normPower * (256 - 1))]); - } - } - obj->convertFromImage(image); - pixmapCache.insert(TileCacheKey(fftSize, zoomLevel, tile), obj); - return obj; + image.setPixel(x, y, colormap[(uint8_t)(normPower * (256 - 1))]); + } + } + obj->convertFromImage(image); + pixmapCache.insert(TileCacheKey(fftSize, zoomLevel, tile), obj); + return obj; } float* Spectrogram::getFFTTile(off_t tile) { - float *obj = fftCache.object(TileCacheKey(fftSize, zoomLevel, tile)); - if (obj != 0) - return obj; + float *obj = fftCache.object(TileCacheKey(fftSize, zoomLevel, tile)); + if (obj != 0) + return obj; - float *dest = new float[tileSize]; - float *ptr = dest; - off_t sample = tile; - while ((ptr - dest) < tileSize) { - getLine(ptr, sample); - sample += getStride(); - ptr += fftSize; - } - fftCache.insert(TileCacheKey(fftSize, zoomLevel, tile), dest); - return dest; + float *dest = new float[tileSize]; + float *ptr = dest; + off_t sample = tile; + while ((ptr - dest) < tileSize) { + getLine(ptr, sample); + sample += getStride(); + ptr += fftSize; + } + fftCache.insert(TileCacheKey(fftSize, zoomLevel, tile), dest); + return dest; } void Spectrogram::getLine(float *dest, off_t sample) { - if (inputSource && fft) { - fftwf_complex buffer[fftSize]; - inputSource->getSamples(buffer, sample, fftSize); + if (inputSource && fft) { + fftwf_complex buffer[fftSize]; + inputSource->getSamples(buffer, sample, fftSize); - for (int i = 0; i < fftSize; i++) { - buffer[i][0] *= window[i]; - buffer[i][1] *= window[i]; - } + for (int i = 0; i < fftSize; i++) { + buffer[i][0] *= window[i]; + buffer[i][1] *= window[i]; + } - fft->process(buffer, buffer); - for (int i = 0; i < fftSize; i++) { - int k = (i + fftSize / 2) % fftSize; - float re = buffer[k][0]; - float im = buffer[k][1]; - float mag = sqrt(re * re + im * im) / fftSize; - float magdb = 10 * log2(mag) / log2(10); - *dest = magdb; - dest++; - } - } + fft->process(buffer, buffer); + for (int i = 0; i < fftSize; i++) { + int k = (i + fftSize / 2) % fftSize; + float re = buffer[k][0]; + float im = buffer[k][1]; + float mag = sqrt(re * re + im * im) / fftSize; + float magdb = 10 * log2(mag) / log2(10); + *dest = magdb; + dest++; + } + } } void Spectrogram::paintCursors(QPainter *painter, QRect rect) { - if (cursorStartX != -1) { - painter->save(); - QPen pen(Qt::white, 1, Qt::DashLine); - painter->setPen(pen); - painter->drawLine(rect.left(), cursorStartY, rect.right(), cursorStartY); - painter->drawLine(cursorStartX, rect.top(), cursorStartX, rect.bottom()); - painter->drawLine(rect.left(), cursorEndY, rect.right(), cursorEndY); - painter->drawLine(cursorEndX, rect.top(), cursorEndX, rect.bottom()); - painter->restore(); + if (cursorStartX != -1) { + painter->save(); + QPen pen(Qt::white, 1, Qt::DashLine); + painter->setPen(pen); + painter->drawLine(rect.left(), cursorStartY, rect.right(), cursorStartY); + painter->drawLine(cursorStartX, rect.top(), cursorStartX, rect.bottom()); + painter->drawLine(rect.left(), cursorEndY, rect.right(), cursorEndY); + painter->drawLine(cursorEndX, rect.top(), cursorEndX, rect.bottom()); + painter->restore(); - } + } } void Spectrogram::paintTimeAxis(QPainter *painter, QRect rect) { - if (timeScaleIsEnabled){ - // Round up for firstLine and round each to nearest linesPerGraduation - int firstLine = ((rect.y() + linesPerGraduation - 1) / linesPerGraduation) * linesPerGraduation; - int lastLine = ((rect.y() + rect.height()) / linesPerGraduation) * linesPerGraduation; + if (timeScaleIsEnabled) { + // Round up for firstLine and round each to nearest linesPerGraduation + int firstLine = ((rect.y() + linesPerGraduation - 1) / linesPerGraduation) * linesPerGraduation; + int lastLine = ((rect.y() + rect.height()) / linesPerGraduation) * linesPerGraduation; - painter->save(); - QPen pen(Qt::white, 1, Qt::SolidLine); - painter->setPen(pen); - QFontMetrics fm(painter->font()); - int textOffset = fm.ascent() / 2 - 1; - for (int line = firstLine; line <= lastLine; line += linesPerGraduation) { - painter->drawLine(0, line, 10, line); - painter->drawText(12, line + textOffset, sampleToTime(lineToSample(line))); - } - painter->restore(); - } + painter->save(); + QPen pen(Qt::white, 1, Qt::SolidLine); + painter->setPen(pen); + QFontMetrics fm(painter->font()); + int textOffset = fm.ascent() / 2 - 1; + for (int line = firstLine; line <= lastLine; line += linesPerGraduation) { + painter->drawLine(0, line, 10, line); + painter->drawText(12, line + textOffset, sampleToTime(lineToSample(line))); + } + painter->restore(); + } } void Spectrogram::setSampleRate(int rate) { - sampleRate = rate; - update(); + sampleRate = rate; + update(); } void Spectrogram::setFFTSize(int size) { - fftSize = size; - delete fft; - fft = new FFT(fftSize); + fftSize = size; + delete fft; + fft = 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))); - } + window.reset(new float[fftSize]); + for (int i = 0; i < fftSize; i++) { + window[i] = 0.5f * (1.0f - cos(Tau * i / (fftSize - 1))); + } - resize(fftSize, getHeight()); + resize(fftSize, getHeight()); } void Spectrogram::setPowerMax(int power) { - powerMax = power; - pixmapCache.clear(); - update(); + powerMax = power; + pixmapCache.clear(); + update(); } void Spectrogram::setPowerMin(int power) { - powerMin = power; - pixmapCache.clear(); - update(); + powerMin = power; + pixmapCache.clear(); + update(); } void Spectrogram::setZoomLevel(int zoom) { - zoomLevel = clamp(zoom, 0, (int)log2(fftSize)); - resize(fftSize, getHeight()); + zoomLevel = clamp(zoom, 0, (int)log2(fftSize)); + resize(fftSize, getHeight()); } void Spectrogram::setTimeScaleEnable(int state) { - timeScaleIsEnabled = (state == Qt::Checked); - pixmapCache.clear(); - update(); + timeScaleIsEnabled = (state == Qt::Checked); + pixmapCache.clear(); + update(); } void Spectrogram::setDeltaDragEnable(int state) { - deltaDragIsEnabled = (state == Qt::Checked); + deltaDragIsEnabled = (state == Qt::Checked); } int Spectrogram::getHeight() { - if (!inputSource) - return 0; + if (!inputSource) + return 0; - return inputSource->getSampleCount() / getStride(); + return inputSource->getSampleCount() / getStride(); } int Spectrogram::getStride() { - return fftSize / pow(2, zoomLevel); + return fftSize / pow(2, zoomLevel); } -off_t Spectrogram::lineToSample(off_t line) { - return line * getStride(); +off_t Spectrogram::lineToSample(off_t line) +{ + return line * getStride(); } -int Spectrogram::sampleToLine(off_t sample) { - return sample / getStride(); +int Spectrogram::sampleToLine(off_t sample) +{ + return sample / getStride(); } QString Spectrogram::sampleToTime(off_t sample) { - return QString::number((float)sample / sampleRate).append("s"); + return QString::number((float)sample / sampleRate).append("s"); } -int Spectrogram::linesPerTile() { - return tileSize / fftSize; +int Spectrogram::linesPerTile() +{ + return tileSize / fftSize; } -uint qHash(const TileCacheKey &key, uint seed) { - return key.fftSize ^ key.zoomLevel ^ key.sample ^ seed; +uint qHash(const TileCacheKey &key, uint seed) +{ + return key.fftSize ^ key.zoomLevel ^ key.sample ^ seed; } diff --git a/spectrogram.h b/spectrogram.h index d96b708..e73f9f1 100644 --- a/spectrogram.h +++ b/spectrogram.h @@ -30,89 +30,91 @@ static const double Tau = M_PI * 2.0; class TileCacheKey; -class Spectrogram : public QWidget { - Q_OBJECT +class Spectrogram : public QWidget +{ + Q_OBJECT public: - Spectrogram(); - ~Spectrogram(); - QSize sizeHint() const; - int getHeight(); - int getStride(); + Spectrogram(); + ~Spectrogram(); + QSize sizeHint() const; + int getHeight(); + int getStride(); signals: - void cursorFrequencyChanged(QString); - void cursorTimeChanged(QString); - void deltaFrequencyChanged(QString); - void deltaTimeChanged(QString); + void cursorFrequencyChanged(QString); + void cursorTimeChanged(QString); + void deltaFrequencyChanged(QString); + void deltaTimeChanged(QString); public slots: - void openFile(QString fileName); - void setSampleRate(int rate); - void setFFTSize(int size); - void setPowerMax(int power); - void setPowerMin(int power); - void setZoomLevel(int zoom); - void setTimeScaleEnable(int state); - void setDeltaDragEnable(int state); + void openFile(QString fileName); + void setSampleRate(int rate); + void setFFTSize(int size); + void setPowerMax(int power); + void setPowerMin(int power); + void setZoomLevel(int zoom); + void setTimeScaleEnable(int state); + void setDeltaDragEnable(int state); protected: - void paintEvent(QPaintEvent *event); - void mouseReleaseEvent(QMouseEvent * event); - void mouseMoveEvent(QMouseEvent * event); - void mousePressEvent(QMouseEvent * event); + void paintEvent(QPaintEvent *event); + void mouseReleaseEvent(QMouseEvent * event); + void mouseMoveEvent(QMouseEvent * event); + void mousePressEvent(QMouseEvent * event); private: - const int linesPerGraduation = 50; - const int tileSize = 65536; // This must be a multiple of the maximum FFT size + const int linesPerGraduation = 50; + const int tileSize = 65536; // This must be a multiple of the maximum FFT size - InputSource *inputSource = nullptr; - FFT *fft = nullptr; - std::unique_ptr window; - fftwf_complex *lineBuffer = nullptr; - QCache pixmapCache; - QCache fftCache; - uint colormap[256]; + InputSource *inputSource = nullptr; + FFT *fft = nullptr; + std::unique_ptr window; + fftwf_complex *lineBuffer = nullptr; + QCache pixmapCache; + QCache fftCache; + uint colormap[256]; - int sampleRate; - int fftSize; - int zoomLevel; - float powerMax; - float powerMin; - bool timeScaleIsEnabled; - bool deltaDragIsEnabled; - int cursorStartX = -1, cursorStartY; - int cursorEndX, cursorEndY; + int sampleRate; + int fftSize; + int zoomLevel; + float powerMax; + float powerMin; + bool timeScaleIsEnabled; + bool deltaDragIsEnabled; + int cursorStartX = -1, cursorStartY; + int cursorEndX, cursorEndY; - QPixmap* getPixmapTile(off_t tile); - float* getFFTTile(off_t tile); - void getLine(float *dest, off_t sample); - void paintTimeAxis(QPainter *painter, QRect rect); - void paintCursors(QPainter *painter, QRect rect); - off_t lineToSample(off_t line); - int sampleToLine(off_t sample); - QString sampleToTime(off_t sample); - int linesPerTile(); - void xyToFreqTime(int x, int y, float *freq, float *time); + QPixmap* getPixmapTile(off_t tile); + float* getFFTTile(off_t tile); + void getLine(float *dest, off_t sample); + void paintTimeAxis(QPainter *painter, QRect rect); + void paintCursors(QPainter *painter, QRect rect); + off_t lineToSample(off_t line); + int sampleToLine(off_t sample); + QString sampleToTime(off_t sample); + int linesPerTile(); + void xyToFreqTime(int x, int y, float *freq, float *time); }; -class TileCacheKey { +class TileCacheKey +{ public: - TileCacheKey(int fftSize, int zoomLevel, off_t sample) { - this->fftSize = fftSize; - this->zoomLevel = zoomLevel; - this->sample = sample; - } + TileCacheKey(int fftSize, int zoomLevel, off_t sample) { + this->fftSize = fftSize; + this->zoomLevel = zoomLevel; + this->sample = sample; + } - bool operator==(const TileCacheKey &k2) const { - return (this->fftSize == k2.fftSize) && - (this->zoomLevel == k2.zoomLevel) && - (this->sample == k2.sample); - } + bool operator==(const TileCacheKey &k2) const { + return (this->fftSize == k2.fftSize) && + (this->zoomLevel == k2.zoomLevel) && + (this->sample == k2.sample); + } - int fftSize; - int zoomLevel; - off_t sample; + int fftSize; + int zoomLevel; + off_t sample; }; diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 9f737b1..438137f 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -24,74 +24,74 @@ #include SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent) - : QDockWidget::QDockWidget(title, parent) + : QDockWidget::QDockWidget(title, parent) { - widget = new QWidget(this); - layout = new QFormLayout(widget); + widget = new QWidget(this); + layout = new QFormLayout(widget); - fileOpenButton = new QPushButton("Open file...", widget); - layout->addRow(fileOpenButton); + fileOpenButton = new QPushButton("Open file...", widget); + layout->addRow(fileOpenButton); - sampleRate = new QLineEdit("8000000"); - sampleRate->setValidator(new QIntValidator(this)); - layout->addRow(new QLabel(tr("Sample rate:")), sampleRate); + sampleRate = new QLineEdit("8000000"); + sampleRate->setValidator(new QIntValidator(this)); + layout->addRow(new QLabel(tr("Sample rate:")), sampleRate); - fftSizeSlider = new QSlider(Qt::Horizontal, widget); - fftSizeSlider->setRange(7, 13); - fftSizeSlider->setValue(10); - layout->addRow(new QLabel(tr("FFT size:")), fftSizeSlider); + fftSizeSlider = new QSlider(Qt::Horizontal, widget); + fftSizeSlider->setRange(7, 13); + fftSizeSlider->setValue(10); + layout->addRow(new QLabel(tr("FFT size:")), fftSizeSlider); - zoomLevelSlider = new QSlider(Qt::Horizontal, widget); - zoomLevelSlider->setRange(0, 5); - zoomLevelSlider->setValue(0); - layout->addRow(new QLabel(tr("Zoom:")), zoomLevelSlider); + zoomLevelSlider = new QSlider(Qt::Horizontal, widget); + zoomLevelSlider->setRange(0, 5); + zoomLevelSlider->setValue(0); + layout->addRow(new QLabel(tr("Zoom:")), zoomLevelSlider); - powerMaxSlider = new QSlider(Qt::Horizontal, widget); - powerMaxSlider->setRange(-100, 20); - powerMaxSlider->setValue(0); - layout->addRow(new QLabel(tr("Power max:")), powerMaxSlider); + powerMaxSlider = new QSlider(Qt::Horizontal, widget); + powerMaxSlider->setRange(-100, 20); + powerMaxSlider->setValue(0); + layout->addRow(new QLabel(tr("Power max:")), powerMaxSlider); - powerMinSlider = new QSlider(Qt::Horizontal, widget); - powerMinSlider->setRange(-100, 20); - powerMinSlider->setValue(-50); - layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); + powerMinSlider = new QSlider(Qt::Horizontal, widget); + powerMinSlider->setRange(-100, 20); + powerMinSlider->setValue(-50); + layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); - timeScaleCheckBox = new QCheckBox(widget); - timeScaleCheckBox->setCheckState(Qt::Checked); - layout->addRow(new QLabel(tr("time overlay:")), timeScaleCheckBox); + timeScaleCheckBox = new QCheckBox(widget); + timeScaleCheckBox->setCheckState(Qt::Checked); + layout->addRow(new QLabel(tr("time overlay:")), timeScaleCheckBox); - cursorFrequencyLabel = new QLabel(); - layout->addRow(new QLabel(tr("Cursor frequency:")), cursorFrequencyLabel); + cursorFrequencyLabel = new QLabel(); + layout->addRow(new QLabel(tr("Cursor frequency:")), cursorFrequencyLabel); - cursorTimeLabel = new QLabel(); - layout->addRow(new QLabel(tr("Cursor time:")), cursorTimeLabel); + cursorTimeLabel = new QLabel(); + layout->addRow(new QLabel(tr("Cursor time:")), cursorTimeLabel); - deltaDragCheckBox = new QCheckBox(widget); - deltaDragCheckBox->setCheckState(Qt::Checked); - layout->addRow(new QLabel(tr("Delta dragging:")), deltaDragCheckBox); + deltaDragCheckBox = new QCheckBox(widget); + deltaDragCheckBox->setCheckState(Qt::Checked); + layout->addRow(new QLabel(tr("Delta dragging:")), deltaDragCheckBox); - deltaFrequencyLabel = new QLabel(); - layout->addRow(new QLabel(tr("Delta frequency:")), deltaFrequencyLabel); + deltaFrequencyLabel = new QLabel(); + layout->addRow(new QLabel(tr("Delta frequency:")), deltaFrequencyLabel); - deltaTimeLabel = new QLabel(); - layout->addRow(new QLabel(tr("Delta time:")), deltaTimeLabel); + deltaTimeLabel = new QLabel(); + layout->addRow(new QLabel(tr("Delta time:")), deltaTimeLabel); - widget->setLayout(layout); - setWidget(widget); + widget->setLayout(layout); + setWidget(widget); - connect(fftSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(fftSizeSliderChanged(int))); - connect(fileOpenButton, SIGNAL(clicked()), this, SLOT(fileOpenButtonClicked())); + connect(fftSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(fftSizeSliderChanged(int))); + connect(fileOpenButton, SIGNAL(clicked()), this, SLOT(fileOpenButtonClicked())); } void SpectrogramControls::fftSizeSliderChanged(int size) { - emit fftSizeChanged((int)pow(2, size)); + emit fftSizeChanged((int)pow(2, size)); } void SpectrogramControls::fileOpenButtonClicked() { - QString fileName = QFileDialog::getOpenFileName( - this, tr("Open File"), "", tr("Sample file (*.cfile *.bin);;All files (*)") - ); - emit openFile(fileName); + QString fileName = QFileDialog::getOpenFileName( + this, tr("Open File"), "", tr("Sample file (*.cfile *.bin);;All files (*)") + ); + emit openFile(fileName); } diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index 3f835aa..943deb5 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -27,34 +27,35 @@ #include #include -class SpectrogramControls : public QDockWidget { - Q_OBJECT +class SpectrogramControls : public QDockWidget +{ + Q_OBJECT public: - SpectrogramControls(const QString & title, QWidget * parent); + SpectrogramControls(const QString & title, QWidget * parent); signals: - void fftSizeChanged(int size); - void openFile(QString fileName); + void fftSizeChanged(int size); + void openFile(QString fileName); private slots: - void fftSizeSliderChanged(int size); - void fileOpenButtonClicked(); + void fftSizeSliderChanged(int size); + void fileOpenButtonClicked(); private: - QWidget *widget; - QFormLayout *layout; + QWidget *widget; + QFormLayout *layout; public: - QPushButton *fileOpenButton; - QLineEdit *sampleRate; - QSlider *fftSizeSlider; - QSlider *zoomLevelSlider; - QSlider *powerMaxSlider; - QSlider *powerMinSlider; - QCheckBox *timeScaleCheckBox; - QLabel *cursorFrequencyLabel; - QLabel *cursorTimeLabel; - QCheckBox *deltaDragCheckBox; - QLabel *deltaFrequencyLabel; - QLabel *deltaTimeLabel; + QPushButton *fileOpenButton; + QLineEdit *sampleRate; + QSlider *fftSizeSlider; + QSlider *zoomLevelSlider; + QSlider *powerMaxSlider; + QSlider *powerMinSlider; + QCheckBox *timeScaleCheckBox; + QLabel *cursorFrequencyLabel; + QLabel *cursorTimeLabel; + QCheckBox *deltaDragCheckBox; + QLabel *deltaFrequencyLabel; + QLabel *deltaTimeLabel; }; From 1d8c2d991b0faf3adf7133a621dd005b0876a60d Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 29 Feb 2016 23:30:34 +0000 Subject: [PATCH 35/99] Remove old selection stuff --- mainwindow.cpp | 43 +------------------------------------------ 1 file changed, 1 insertion(+), 42 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 1d67801..9c02831 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -58,9 +58,6 @@ MainWindow::MainWindow() connect(&spectrogram, SIGNAL(deltaTimeChanged(QString)), dock->deltaTimeLabel, SLOT(setText(QString))); } -QRubberBand *rubberBand=NULL; -QPoint mystart; - bool MainWindow::eventFilter(QObject * /*obj*/, QEvent *event) { if (event->type() == QEvent::Wheel) { @@ -79,45 +76,7 @@ bool MainWindow::eventFilter(QObject * /*obj*/, QEvent *event) } return true; } - } else if (event->type() == QEvent::MouseButtonPress) { - QMouseEvent *mouseEvent = (QMouseEvent*)event; - if (mouseEvent->buttons() == Qt::LeftButton) { - mystart = (mouseEvent->pos()); - if(!rubberBand) - rubberBand = new QRubberBand(QRubberBand::Rectangle, scrollArea.viewport()); - rubberBand->setGeometry(QRect(mystart, mystart)); - rubberBand->show(); - return true; - } - } else if (event->type() == QEvent::MouseMove) { - QMouseEvent *mouseEvent = (QMouseEvent*)event; - if (mouseEvent->buttons() == Qt::LeftButton) { - rubberBand->setGeometry(QRect(mystart, mouseEvent->pos()).normalized()); //Area Bounding - return true; - } - } else if (event->type() == QEvent::MouseButtonRelease) { - QMouseEvent *mouseEvent = (QMouseEvent*)event; - QRect rb = rubberBand->geometry(); - - off_t topSample = spectrogram.lineToSample(scrollArea.verticalScrollBar()->value() + rb.top()); - off_t bottomSample = spectrogram.lineToSample(scrollArea.verticalScrollBar()->value() + rb.bottom()); - - int offset = scrollArea.horizontalScrollBar()->value(); - int width = spectrogram.width(); - float left = (float)clamp(offset + rb.left(), 0, width) / width - 0.5; - float right = (float)clamp(offset + rb.right(), 0, width) / width - 0.5; - - if (rb.width() > 10 && rb.height() > 10) { - selectionTime = {topSample, bottomSample}; - selectionFreq = {left, right}; - emit selectionChanged(selectionTime, selectionFreq); - } else { - rubberBand->hide(); - rubberBand->clearMask(); - emit selectionCleared(); - } - return true; - }; + } return false; } From c797e2b83c00139e950b9df679da0fc36adeb2e3 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Tue, 1 Mar 2016 21:24:32 +0000 Subject: [PATCH 36/99] plot: Add height() --- plot.h | 7 +++++++ plotview.cpp | 8 +++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/plot.h b/plot.h index 850f5fa..c9d2c3e 100644 --- a/plot.h +++ b/plot.h @@ -30,4 +30,11 @@ 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; + int height() const { return _height; }; + +protected: + void setHeight(int height) { _height = height; }; + +private: + int _height = 50; }; diff --git a/plotview.cpp b/plotview.cpp index db22de4..af5c57f 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -121,16 +121,14 @@ void PlotView::paintEvent(QPaintEvent *event) QPainter painter(this); painter.fillRect(rect, Qt::black); - // Split space equally between waveforms for now - int plotHeight = height() / plots.size(); #define PLOT_LAYER(paintFunc) \ { \ - int plotCount = 0; \ + int y = 0; \ for (auto&& plot : plots) { \ - QRect rect = QRect(0, plotCount * plotHeight, width(), plotHeight); \ + QRect rect = QRect(0, y, width(), plot->height()); \ plot->paintFunc(painter, rect, {firstSample, lastSample}); \ - plotCount++; \ + y += plot->height(); \ } \ } From e9735114e163509838884b53e74e657e8b8e17a7 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Tue, 1 Mar 2016 21:47:20 +0000 Subject: [PATCH 37/99] plot: Port Spectrogram to Plot interface --- mainwindow.cpp | 1 - spectrogram.cpp | 26 ++++++++++++++++++++------ spectrogram.h | 10 +++++++++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 9c02831..cf8a04a 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -26,7 +26,6 @@ MainWindow::MainWindow() { setWindowTitle(tr("inspectrum")); - scrollArea.setWidget(&spectrogram); scrollArea.viewport()->installEventFilter(this); setCentralWidget(&scrollArea); diff --git a/spectrogram.cpp b/spectrogram.cpp index b9df358..665d9ce 100644 --- a/spectrogram.cpp +++ b/spectrogram.cpp @@ -44,7 +44,6 @@ Spectrogram::Spectrogram() colormap[i] = QColor::fromHsvF(p * 0.83f, 1.0, 1.0 - p).rgba(); } - setMouseTracking(true); } Spectrogram::~Spectrogram() @@ -67,7 +66,7 @@ void Spectrogram::openFile(QString fileName) pixmapCache.clear(); fftCache.clear(); inputSource = newFile; - resize(fftSize, getHeight()); + setHeight(fftSize); } catch (std::runtime_error e) { // TODO: display error } @@ -116,7 +115,23 @@ void Spectrogram::mousePressEvent(QMouseEvent *event) update(); } -void Spectrogram::paintEvent(QPaintEvent *event) +void Spectrogram::paintBack(QPainter &painter, QRect &rect, range_t sampleRange) +{ + +} + +void Spectrogram::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) +{ + +} + +void Spectrogram::paintFront(QPainter &painter, QRect &rect, range_t sampleRange) +{ + +} + + +/*void Spectrogram::paintEvent(QPaintEvent *event) { QRect rect = event->rect(); QPainter painter(this); @@ -141,7 +156,7 @@ void Spectrogram::paintEvent(QPaintEvent *event) paintTimeAxis(&painter, rect); paintCursors(&painter, rect); } -} +}*/ QPixmap* Spectrogram::getPixmapTile(off_t tile) { @@ -260,7 +275,7 @@ void Spectrogram::setFFTSize(int size) window[i] = 0.5f * (1.0f - cos(Tau * i / (fftSize - 1))); } - resize(fftSize, getHeight()); + setHeight(fftSize); } void Spectrogram::setPowerMax(int power) @@ -280,7 +295,6 @@ void Spectrogram::setPowerMin(int power) void Spectrogram::setZoomLevel(int zoom) { zoomLevel = clamp(zoom, 0, (int)log2(fftSize)); - resize(fftSize, getHeight()); } void Spectrogram::setTimeScaleEnable(int state) diff --git a/spectrogram.h b/spectrogram.h index c52b936..92002eb 100644 --- a/spectrogram.h +++ b/spectrogram.h @@ -23,6 +23,7 @@ #include #include "fft.h" #include "inputsource.h" +#include "plot.h" #include #include @@ -31,13 +32,18 @@ static const double Tau = M_PI * 2.0; class TileCacheKey; -class Spectrogram : public QWidget +class Spectrogram : public Plot { Q_OBJECT public: Spectrogram(); ~Spectrogram(); + + 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); + QSize sizeHint() const; int getHeight(); int getStride(); @@ -50,6 +56,7 @@ signals: void cursorTimeChanged(QString); void deltaFrequencyChanged(QString); void deltaTimeChanged(QString); + void needUpdate(); public slots: void openFile(QString fileName); @@ -60,6 +67,7 @@ public slots: void setZoomLevel(int zoom); void setTimeScaleEnable(int state); void setDeltaDragEnable(int state); + void update() { emit needUpdate(); }; protected: void paintEvent(QPaintEvent *event); From 61a34ae490d08a59ab8f0fc9b3fde276899ee83e Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Tue, 1 Mar 2016 21:49:52 +0000 Subject: [PATCH 38/99] Rename Spectrogram to SpectrogramPlot --- CMakeLists.txt | 2 +- mainwindow.h | 4 +- spectrogram.cpp => spectrogramplot.cpp | 62 +++++++++++++------------- spectrogram.h => spectrogramplot.h | 6 +-- 4 files changed, 37 insertions(+), 37 deletions(-) rename spectrogram.cpp => spectrogramplot.cpp (82%) rename spectrogram.h => spectrogramplot.h (97%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6987842..d01ccaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,8 +36,8 @@ list(APPEND inspectrum_sources memory_source_impl.cc plotview.cpp samplebuffer.cpp - spectrogram.cpp spectrogramcontrols.cpp + spectrogramplot.cpp traceplot.cpp ) diff --git a/mainwindow.h b/mainwindow.h index 18fa2c4..3e54e7c 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -21,8 +21,8 @@ #include #include -#include "spectrogram.h" #include "spectrogramcontrols.h" +#include "spectrogramplot.h" #include "plotview.h" class MainWindow : public QMainWindow @@ -50,7 +50,7 @@ protected: private: QScrollArea scrollArea; - Spectrogram spectrogram; + SpectrogramPlot spectrogram; SpectrogramControls *dock; PlotView *plots; std::pair selectionTime; diff --git a/spectrogram.cpp b/spectrogramplot.cpp similarity index 82% rename from spectrogram.cpp rename to spectrogramplot.cpp index 665d9ce..c7879d5 100644 --- a/spectrogram.cpp +++ b/spectrogramplot.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "spectrogram.h" +#include "spectrogramplot.h" #include #include @@ -29,7 +29,7 @@ #include "util.h" -Spectrogram::Spectrogram() +SpectrogramPlot::SpectrogramPlot() { sampleRate = 8000000; setFFTSize(1024); @@ -46,18 +46,18 @@ Spectrogram::Spectrogram() } -Spectrogram::~Spectrogram() +SpectrogramPlot::~SpectrogramPlot() { delete fft; delete inputSource; } -QSize Spectrogram::sizeHint() const +QSize SpectrogramPlot::sizeHint() const { return QSize(1024, 2048); } -void Spectrogram::openFile(QString fileName) +void SpectrogramPlot::openFile(QString fileName) { if (fileName != nullptr) { try { @@ -73,13 +73,13 @@ void Spectrogram::openFile(QString fileName) } } -void Spectrogram::xyToFreqTime(int x, int y, float *freq, float *time) +void SpectrogramPlot::xyToFreqTime(int x, int y, float *freq, float *time) { *freq = labs(x - (fftSize / 2)) * sampleRate / 2 / (float)fftSize; *time = (float)lineToSample(y) / sampleRate; } -void Spectrogram::mouseReleaseEvent(QMouseEvent *event) +void SpectrogramPlot::mouseReleaseEvent(QMouseEvent *event) { if (deltaDragIsEnabled) { cursorStartX = -1; @@ -87,7 +87,7 @@ void Spectrogram::mouseReleaseEvent(QMouseEvent *event) } } -void Spectrogram::mouseMoveEvent(QMouseEvent *event) +void SpectrogramPlot::mouseMoveEvent(QMouseEvent *event) { float freq, time; xyToFreqTime(event->x(), event->y(), &freq, &time); @@ -104,7 +104,7 @@ void Spectrogram::mouseMoveEvent(QMouseEvent *event) } } -void Spectrogram::mousePressEvent(QMouseEvent *event) +void SpectrogramPlot::mousePressEvent(QMouseEvent *event) { if (cursorStartX == -1) { cursorEndX = cursorStartX = event->x(); @@ -115,23 +115,23 @@ void Spectrogram::mousePressEvent(QMouseEvent *event) update(); } -void Spectrogram::paintBack(QPainter &painter, QRect &rect, range_t sampleRange) +void SpectrogramPlot::paintBack(QPainter &painter, QRect &rect, range_t sampleRange) { } -void Spectrogram::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) +void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) { } -void Spectrogram::paintFront(QPainter &painter, QRect &rect, range_t sampleRange) +void SpectrogramPlot::paintFront(QPainter &painter, QRect &rect, range_t sampleRange) { } -/*void Spectrogram::paintEvent(QPaintEvent *event) +/*void SpectrogramPlot::paintEvent(QPaintEvent *event) { QRect rect = event->rect(); QPainter painter(this); @@ -158,7 +158,7 @@ void Spectrogram::paintFront(QPainter &painter, QRect &rect, range_t samp } }*/ -QPixmap* Spectrogram::getPixmapTile(off_t tile) +QPixmap* SpectrogramPlot::getPixmapTile(off_t tile) { QPixmap *obj = pixmapCache.object(TileCacheKey(fftSize, zoomLevel, tile)); if (obj != 0) @@ -182,7 +182,7 @@ QPixmap* Spectrogram::getPixmapTile(off_t tile) return obj; } -float* Spectrogram::getFFTTile(off_t tile) +float* SpectrogramPlot::getFFTTile(off_t tile) { float *obj = fftCache.object(TileCacheKey(fftSize, zoomLevel, tile)); if (obj != 0) @@ -200,7 +200,7 @@ float* Spectrogram::getFFTTile(off_t tile) return dest; } -void Spectrogram::getLine(float *dest, off_t sample) +void SpectrogramPlot::getLine(float *dest, off_t sample) { if (inputSource && fft) { auto buffer = inputSource->getSamples(sample, fftSize); @@ -223,7 +223,7 @@ void Spectrogram::getLine(float *dest, off_t sample) } } -void Spectrogram::paintCursors(QPainter *painter, QRect rect) +void SpectrogramPlot::paintCursors(QPainter *painter, QRect rect) { if (cursorStartX != -1) { painter->save(); @@ -238,7 +238,7 @@ void Spectrogram::paintCursors(QPainter *painter, QRect rect) } } -void Spectrogram::paintTimeAxis(QPainter *painter, QRect rect) +void SpectrogramPlot::paintTimeAxis(QPainter *painter, QRect rect) { if (timeScaleIsEnabled) { // Round up for firstLine and round each to nearest linesPerGraduation @@ -258,13 +258,13 @@ void Spectrogram::paintTimeAxis(QPainter *painter, QRect rect) } } -void Spectrogram::setSampleRate(int rate) +void SpectrogramPlot::setSampleRate(int rate) { sampleRate = rate; update(); } -void Spectrogram::setFFTSize(int size) +void SpectrogramPlot::setFFTSize(int size) { fftSize = size; delete fft; @@ -278,39 +278,39 @@ void Spectrogram::setFFTSize(int size) setHeight(fftSize); } -void Spectrogram::setPowerMax(int power) +void SpectrogramPlot::setPowerMax(int power) { powerMax = power; pixmapCache.clear(); update(); } -void Spectrogram::setPowerMin(int power) +void SpectrogramPlot::setPowerMin(int power) { powerMin = power; pixmapCache.clear(); update(); } -void Spectrogram::setZoomLevel(int zoom) +void SpectrogramPlot::setZoomLevel(int zoom) { zoomLevel = clamp(zoom, 0, (int)log2(fftSize)); } -void Spectrogram::setTimeScaleEnable(int state) +void SpectrogramPlot::setTimeScaleEnable(int state) { timeScaleIsEnabled = (state == Qt::Checked); pixmapCache.clear(); update(); } -void Spectrogram::setDeltaDragEnable(int state) +void SpectrogramPlot::setDeltaDragEnable(int state) { deltaDragIsEnabled = (state == Qt::Checked); } -int Spectrogram::getHeight() +int SpectrogramPlot::getHeight() { if (!inputSource) return 0; @@ -318,27 +318,27 @@ int Spectrogram::getHeight() return inputSource->count() / getStride(); } -int Spectrogram::getStride() +int SpectrogramPlot::getStride() { return fftSize / pow(2, zoomLevel); } -off_t Spectrogram::lineToSample(off_t line) +off_t SpectrogramPlot::lineToSample(off_t line) { return line * getStride(); } -int Spectrogram::sampleToLine(off_t sample) +int SpectrogramPlot::sampleToLine(off_t sample) { return sample / getStride(); } -QString Spectrogram::sampleToTime(off_t sample) +QString SpectrogramPlot::sampleToTime(off_t sample) { return QString::number((float)sample / sampleRate).append("s"); } -int Spectrogram::linesPerTile() +int SpectrogramPlot::linesPerTile() { return tileSize / fftSize; } diff --git a/spectrogram.h b/spectrogramplot.h similarity index 97% rename from spectrogram.h rename to spectrogramplot.h index 92002eb..624f8b0 100644 --- a/spectrogram.h +++ b/spectrogramplot.h @@ -32,13 +32,13 @@ static const double Tau = M_PI * 2.0; class TileCacheKey; -class Spectrogram : public Plot +class SpectrogramPlot : public Plot { Q_OBJECT public: - Spectrogram(); - ~Spectrogram(); + SpectrogramPlot(); + ~SpectrogramPlot(); void paintBack(QPainter &painter, QRect &rect, range_t sampleRange); void paintMid(QPainter &painter, QRect &rect, range_t sampleRange); From cf55504cdec05219027c9c75c2bac6f9c6d3409e Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Tue, 1 Mar 2016 21:53:23 +0000 Subject: [PATCH 39/99] plot: Move PlotView to mainwindow central widget --- mainwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index cf8a04a..abcd4f4 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -27,18 +27,18 @@ MainWindow::MainWindow() { setWindowTitle(tr("inspectrum")); scrollArea.viewport()->installEventFilter(this); - setCentralWidget(&scrollArea); dock = new SpectrogramControls(tr("Controls"), this); dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); addDockWidget(Qt::LeftDockWidgetArea, dock); plots = new PlotView(); - addDockWidget(Qt::BottomDockWidgetArea, plots); connect(this, SIGNAL(viewChanged(off_t, off_t)), plots, SLOT(viewChanged(off_t, off_t))); connect(this, SIGNAL(selectionChanged(std::pair, std::pair)), plots, SLOT(selectionChanged(std::pair, std::pair))); connect(this, SIGNAL(selectionCleared()), plots, SLOT(selectionCleared())); + scrollArea.setWidget(plots); + setCentralWidget(&scrollArea); connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); connect(dock->sampleRate, SIGNAL(textChanged(QString)), this, SLOT(setSampleRate(QString))); From dac5c402db1f9bffd1bf1dd3744d466be8dd807a Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Tue, 1 Mar 2016 21:54:37 +0000 Subject: [PATCH 40/99] plot: Remove dead colormap code from PlotView --- plotview.cpp | 7 +------ plotview.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/plotview.cpp b/plotview.cpp index af5c57f..5442835 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -32,12 +32,7 @@ PlotView::PlotView() { - for (int i = 0; i < 128; i++) { - colormap[i] = qRgb(i/2, i*1.5, i*1.5); - } - for (int i = 128; i < 256; i++) { - colormap[i] = qRgb(64 + (i-128)*1.5, 192+(i-128)/2, 192+(i-128)/2); - } + } void PlotView::refreshSources() diff --git a/plotview.h b/plotview.h index 7548895..9a9d3be 100644 --- a/plotview.h +++ b/plotview.h @@ -48,7 +48,6 @@ private: bool selection = false; std::pair selectionTime; std::pair selectionFreq; - QRgb colormap[255]; void refreshSources(); }; \ No newline at end of file From 1e929fa9a7a7133712405675d2778f6f4afdc3e8 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Tue, 1 Mar 2016 22:12:13 +0000 Subject: [PATCH 41/99] main: Expand PlotView to fill ScrollArea --- mainwindow.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index abcd4f4..0b4d5ae 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -37,7 +37,10 @@ MainWindow::MainWindow() connect(this, SIGNAL(selectionChanged(std::pair, std::pair)), plots, SLOT(selectionChanged(std::pair, std::pair))); connect(this, SIGNAL(selectionCleared()), plots, SLOT(selectionCleared())); - scrollArea.setWidget(plots); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(plots); + scrollArea.setLayout(layout); setCentralWidget(&scrollArea); connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); From caa2709ffcc5fa399b78582ae1d6c1194ad7a541 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Tue, 1 Mar 2016 22:32:30 +0000 Subject: [PATCH 42/99] plot: Draw centre-line --- CMakeLists.txt | 1 + plot.cpp | 11 ++++++++++- plot.h | 2 +- plotview.cpp | 1 + spectrogramplot.cpp | 5 ----- spectrogramplot.h | 1 - traceplot.cpp | 5 ----- traceplot.h | 1 - 8 files changed, 13 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d01ccaf..102f569 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ list(APPEND inspectrum_sources inputsource.cpp memory_sink_impl.cc memory_source_impl.cc + plot.cpp plotview.cpp samplebuffer.cpp spectrogramcontrols.cpp diff --git a/plot.cpp b/plot.cpp index 9643982..fc58d8e 100644 --- a/plot.cpp +++ b/plot.cpp @@ -17,4 +17,13 @@ * along with this program. If not, see . */ -#include "plot.h" \ No newline at end of file +#include "plot.h" + +void Plot::paintBack(QPainter &painter, QRect &rect, range_t sampleRange) +{ + painter.save(); + QPen pen(Qt::white, 1, Qt::DashLine); + painter.setPen(pen); + painter.drawLine(rect.left(), rect.center().y(), rect.right(), rect.center().y()); + painter.restore(); +} \ No newline at end of file diff --git a/plot.h b/plot.h index c9d2c3e..c635c00 100644 --- a/plot.h +++ b/plot.h @@ -27,7 +27,7 @@ class Plot : public QObject { public: - virtual void paintBack(QPainter &painter, QRect &rect, range_t sampleRange) = 0; + virtual void paintBack(QPainter &painter, QRect &rect, range_t sampleRange); virtual void paintMid(QPainter &painter, QRect &rect, range_t sampleRange) = 0; virtual void paintFront(QPainter &painter, QRect &rect, range_t sampleRange) = 0; int height() const { return _height; }; diff --git a/plotview.cpp b/plotview.cpp index 5442835..1bf0442 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -28,6 +28,7 @@ #include "grsamplebuffer.h" #include "memory_sink.h" #include "memory_source.h" +#include "spectrogramplot.h" #include "traceplot.h" PlotView::PlotView() diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index c7879d5..bacb1c3 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -115,11 +115,6 @@ void SpectrogramPlot::mousePressEvent(QMouseEvent *event) update(); } -void SpectrogramPlot::paintBack(QPainter &painter, QRect &rect, range_t sampleRange) -{ - -} - void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) { diff --git a/spectrogramplot.h b/spectrogramplot.h index 624f8b0..b6c8b9e 100644 --- a/spectrogramplot.h +++ b/spectrogramplot.h @@ -40,7 +40,6 @@ public: SpectrogramPlot(); ~SpectrogramPlot(); - 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); diff --git a/traceplot.cpp b/traceplot.cpp index 9387f6f..133c557 100644 --- a/traceplot.cpp +++ b/traceplot.cpp @@ -24,11 +24,6 @@ TracePlot::TracePlot(std::shared_ptr source) : sampleSourc } -void TracePlot::paintBack(QPainter &painter, QRect &rect, range_t sampleRange) -{ - -} - void TracePlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) { auto firstSample = sampleRange.minimum; diff --git a/traceplot.h b/traceplot.h index 4e34eb9..4a7097e 100644 --- a/traceplot.h +++ b/traceplot.h @@ -30,7 +30,6 @@ class TracePlot : public Plot 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); From 61d1fde23dcd836b98eafada2c411a659d5022ca Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Tue, 1 Mar 2016 23:25:48 +0000 Subject: [PATCH 43/99] spectrogram: Rotate view & draw first tile --- spectrogramplot.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index bacb1c3..d1fa864 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -117,7 +117,8 @@ void SpectrogramPlot::mousePressEvent(QMouseEvent *event) void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) { - + QPixmap *tile = getPixmapTile(0); + painter.drawPixmap(QRect(rect.x(), rect.y(), linesPerTile(), fftSize), *tile); } void SpectrogramPlot::paintFront(QPainter &painter, QRect &rect, range_t sampleRange) @@ -160,13 +161,13 @@ QPixmap* SpectrogramPlot::getPixmapTile(off_t tile) return obj; float *fftTile = getFFTTile(tile); - obj = new QPixmap(fftSize, linesPerTile()); - QImage image(fftSize, linesPerTile(), QImage::Format_RGB32); - for (int y = 0; y < linesPerTile(); y++) { - float *line = &fftTile[y * fftSize]; - for (int x = 0; x < fftSize; x++) { + obj = new QPixmap(linesPerTile(), fftSize); + QImage image(linesPerTile(), fftSize, QImage::Format_RGB32); + for (int x = 0; x < linesPerTile(); x++) { + float *line = &fftTile[x * fftSize]; + for (int y = 0; y < fftSize; y++) { float powerRange = std::abs(int(powerMin - powerMax)); - float normPower = (line[x] - powerMax) * -1.0f / powerRange; + float normPower = (line[y] - powerMax) * -1.0f / powerRange; normPower = clamp(normPower, 0.0f, 1.0f); image.setPixel(x, y, colormap[(uint8_t)(normPower * (256 - 1))]); From d89565bbbc6ec492ce9c81148f25f4e33f2f3a34 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Tue, 1 Mar 2016 23:26:35 +0000 Subject: [PATCH 44/99] spectrogram: Take input source as argument --- mainwindow.cpp | 2 +- plotview.cpp | 3 +++ spectrogramplot.cpp | 6 +++--- spectrogramplot.h | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 0b4d5ae..f6dc151 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -23,7 +23,7 @@ #include "mainwindow.h" #include "util.h" -MainWindow::MainWindow() +MainWindow::MainWindow() : spectrogram(nullptr) { setWindowTitle(tr("inspectrum")); scrollArea.viewport()->installEventFilter(this); diff --git a/plotview.cpp b/plotview.cpp index 1bf0442..b01e2dd 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -40,6 +40,9 @@ void PlotView::refreshSources() { plots.clear(); + auto sp = new SpectrogramPlot(mainSampleSource); + plots.emplace_back(sp); + gr::top_block_sptr iq_tb = gr::make_top_block("multiply"); auto iq_mem_source = gr::blocks::memory_source::make(8); auto iq_mem_sink = gr::blocks::memory_sink::make(8); diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index d1fa864..e72aa33 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -29,10 +29,11 @@ #include "util.h" -SpectrogramPlot::SpectrogramPlot() +SpectrogramPlot::SpectrogramPlot(SampleSource> *src) { + inputSource = src; sampleRate = 8000000; - setFFTSize(1024); + setFFTSize(512); zoomLevel = 0; powerMax = 0.0f; powerMin = -50.0f; @@ -66,7 +67,6 @@ void SpectrogramPlot::openFile(QString fileName) pixmapCache.clear(); fftCache.clear(); inputSource = newFile; - setHeight(fftSize); } catch (std::runtime_error e) { // TODO: display error } diff --git a/spectrogramplot.h b/spectrogramplot.h index b6c8b9e..cc11272 100644 --- a/spectrogramplot.h +++ b/spectrogramplot.h @@ -37,7 +37,7 @@ class SpectrogramPlot : public Plot Q_OBJECT public: - SpectrogramPlot(); + SpectrogramPlot(SampleSource> *src); ~SpectrogramPlot(); void paintMid(QPainter &painter, QRect &rect, range_t sampleRange); @@ -48,7 +48,7 @@ public: int getStride(); off_t lineToSample(off_t line); - InputSource *inputSource = nullptr; + SampleSource> *inputSource = nullptr; signals: void cursorFrequencyChanged(QString); From da7e4a31045d5dee21d1d368a60c61aa68e25054 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Wed, 2 Mar 2016 00:02:08 +0000 Subject: [PATCH 45/99] spectrogram: Plot some more tiles --- spectrogramplot.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index e72aa33..5e519a9 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -117,8 +117,10 @@ void SpectrogramPlot::mousePressEvent(QMouseEvent *event) void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) { - QPixmap *tile = getPixmapTile(0); - painter.drawPixmap(QRect(rect.x(), rect.y(), linesPerTile(), fftSize), *tile); + for (int x = rect.left(); x < rect.right(); x += linesPerTile()) { + QPixmap *tile = getPixmapTile(x * fftSize); + painter.drawPixmap(QRect(x, rect.y(), linesPerTile(), fftSize), *tile); + } } void SpectrogramPlot::paintFront(QPainter &painter, QRect &rect, range_t sampleRange) From fc019afb325552eeb0e96047960b58339895489c Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Wed, 2 Mar 2016 22:18:38 +0000 Subject: [PATCH 46/99] main: Rip out a bunch of old signals etc. --- main.cpp | 2 +- mainwindow.cpp | 113 +++---------------------------------------------- mainwindow.h | 22 ---------- 3 files changed, 6 insertions(+), 131 deletions(-) diff --git a/main.cpp b/main.cpp index 01ee997..4142166 100644 --- a/main.cpp +++ b/main.cpp @@ -50,7 +50,7 @@ int main(int argc, char *argv[]) fputs("ERROR: could not parse rate\n", stderr); return 1; } - mainWin.changeSampleRate(rate); + // TODO: set sample rate } const QStringList args = parser.positionalArguments(); diff --git a/mainwindow.cpp b/mainwindow.cpp index f6dc151..b284629 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -23,7 +23,7 @@ #include "mainwindow.h" #include "util.h" -MainWindow::MainWindow() : spectrogram(nullptr) +MainWindow::MainWindow() { setWindowTitle(tr("inspectrum")); scrollArea.viewport()->installEventFilter(this); @@ -33,121 +33,18 @@ MainWindow::MainWindow() : spectrogram(nullptr) addDockWidget(Qt::LeftDockWidgetArea, dock); plots = new PlotView(); - connect(this, SIGNAL(viewChanged(off_t, off_t)), plots, SLOT(viewChanged(off_t, off_t))); - connect(this, SIGNAL(selectionChanged(std::pair, std::pair)), - plots, SLOT(selectionChanged(std::pair, std::pair))); - connect(this, SIGNAL(selectionCleared()), plots, SLOT(selectionCleared())); - QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(plots); scrollArea.setLayout(layout); setCentralWidget(&scrollArea); - - connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); - connect(dock->sampleRate, SIGNAL(textChanged(QString)), this, SLOT(setSampleRate(QString))); - connect(dock, SIGNAL(fftSizeChanged(int)), this, SLOT(setFFTSize(int))); - connect(dock->zoomLevelSlider, SIGNAL(valueChanged(int)), this, SLOT(setZoomLevel(int))); - connect(dock->powerMaxSlider, SIGNAL(valueChanged(int)), &spectrogram, SLOT(setPowerMax(int))); - connect(dock->powerMinSlider, SIGNAL(valueChanged(int)), &spectrogram, SLOT(setPowerMin(int))); - - connect(scrollArea.verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(spectrogramSliderMoved(int))); - - connect(dock->timeScaleCheckBox, SIGNAL(stateChanged(int)), &spectrogram, SLOT(setTimeScaleEnable(int))); - connect(&spectrogram, SIGNAL(cursorFrequencyChanged(QString)), dock->cursorFrequencyLabel, SLOT(setText(QString))); - connect(&spectrogram, SIGNAL(cursorTimeChanged(QString)), dock->cursorTimeLabel, SLOT(setText(QString))); - connect(dock->deltaDragCheckBox, SIGNAL(stateChanged(int)), &spectrogram, SLOT(setDeltaDragEnable(int))); - connect(&spectrogram, SIGNAL(deltaFrequencyChanged(QString)), dock->deltaFrequencyLabel, SLOT(setText(QString))); - connect(&spectrogram, SIGNAL(deltaTimeChanged(QString)), dock->deltaTimeLabel, SLOT(setText(QString))); -} - -bool MainWindow::eventFilter(QObject * /*obj*/, QEvent *event) -{ - if (event->type() == QEvent::Wheel) { - QWheelEvent *wheelEvent = (QWheelEvent*)event; - QSlider *slider = nullptr; - if (QApplication::keyboardModifiers() & Qt::ControlModifier) { - slider = dock->zoomLevelSlider; - } else if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { - slider = dock->fftSizeSlider; - } - if (slider != nullptr) { - if (wheelEvent->angleDelta().y() > 0) { - slider->setValue(slider->value() + 1); - } else if (wheelEvent->angleDelta().y() < 0) { - slider->setValue(slider->value() - 1); - } - return true; - } - } - return false; -} - -void MainWindow::setSampleRate(QString rate) -{ - spectrogram.setSampleRate(rate.toInt()); -} - -void MainWindow::changeSampleRate(int rate) -{ - spectrogram.setSampleRate(rate); - dock->sampleRate->setText(QString::number(rate)); -} - -void MainWindow::setFFTSize(int size) -{ - off_t sample = getCenterSample(); - spectrogram.setFFTSize(size); - scrollArea.verticalScrollBar()->setValue(getScrollPos(sample)); - emitViewChanged(); -} - -void MainWindow::setZoomLevel(int zoom) -{ - off_t sample = getCenterSample(); - spectrogram.setZoomLevel(zoom); - scrollArea.verticalScrollBar()->setValue(getScrollPos(sample)); - emitViewChanged(); -} - -void MainWindow::spectrogramSliderMoved(int value) -{ - emitViewChanged(); -} - -void MainWindow::emitViewChanged() -{ - emit viewChanged(getTopSample(), getBottomSample()); -} - -off_t MainWindow::getTopSample() -{ - int height = scrollArea.height(); - return scrollArea.verticalScrollBar()->value() * spectrogram.getStride(); -} - -off_t MainWindow::getCenterSample() -{ - int height = scrollArea.height(); - return (scrollArea.verticalScrollBar()->value() + height / 2) * spectrogram.getStride(); -} - -off_t MainWindow::getBottomSample() -{ - int height = scrollArea.height(); - return (scrollArea.verticalScrollBar()->value() + height) * spectrogram.getStride(); -} - -int MainWindow::getScrollPos(off_t sample) -{ - int height = scrollArea.height(); - return sample / spectrogram.getStride() - height / 2; } void MainWindow::openFile(QString fileName) { QString title="%1: %2"; this->setWindowTitle(title.arg(QApplication::applicationName(),fileName.section('/',-1,-1))); - spectrogram.openFile(fileName); - plots->inputSourceChanged(spectrogram.inputSource); - emitViewChanged(); + // TODO: error check, ownership + plots->inputSourceChanged(new InputSource(fileName.toUtf8().constData())); + // TODO: don't hardcode this + plots->viewChanged(0, 102400); } diff --git a/mainwindow.h b/mainwindow.h index 3e54e7c..b5510c2 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -22,7 +22,6 @@ #include #include #include "spectrogramcontrols.h" -#include "spectrogramplot.h" #include "plotview.h" class MainWindow : public QMainWindow @@ -35,30 +34,9 @@ public: public slots: void openFile(QString fileName); - void setSampleRate(QString rate); - void setFFTSize(int size); - void setZoomLevel(int zoom); - void spectrogramSliderMoved(int value); - -signals: - void viewChanged(off_t firstSample, off_t lastSample); - void selectionChanged(std::pair selectionTime, std::pair selectionFreq); - void selectionCleared(); - -protected: - bool eventFilter(QObject *obj, QEvent *event); private: QScrollArea scrollArea; - SpectrogramPlot spectrogram; SpectrogramControls *dock; PlotView *plots; - std::pair selectionTime; - std::pair selectionFreq; - - void emitViewChanged(); - off_t getTopSample(); - off_t getCenterSample(); - off_t getBottomSample(); - int getScrollPos(off_t sample); }; From d5ebd4f2cf9ba81ea358507523bc3aefbde9cfe0 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Wed, 2 Mar 2016 23:36:25 +0000 Subject: [PATCH 47/99] plot: Convert PlotView to derive from QAbstractScrollArea --- mainwindow.cpp | 6 +----- mainwindow.h | 1 - plotview.cpp | 4 ++-- plotview.h | 4 ++-- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index b284629..737f8e9 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -26,17 +26,13 @@ MainWindow::MainWindow() { setWindowTitle(tr("inspectrum")); - scrollArea.viewport()->installEventFilter(this); dock = new SpectrogramControls(tr("Controls"), this); dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); addDockWidget(Qt::LeftDockWidgetArea, dock); plots = new PlotView(); - QVBoxLayout *layout = new QVBoxLayout; - layout->addWidget(plots); - scrollArea.setLayout(layout); - setCentralWidget(&scrollArea); + setCentralWidget(plots); } void MainWindow::openFile(QString fileName) diff --git a/mainwindow.h b/mainwindow.h index b5510c2..5601386 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -36,7 +36,6 @@ public slots: void openFile(QString fileName); private: - QScrollArea scrollArea; SpectrogramControls *dock; PlotView *plots; }; diff --git a/plotview.cpp b/plotview.cpp index b01e2dd..7f8fca4 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -33,7 +33,7 @@ PlotView::PlotView() { - + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } void PlotView::refreshSources() @@ -117,7 +117,7 @@ void PlotView::paintEvent(QPaintEvent *event) if (lastSample - firstSample <= 0) return; QRect rect = QRect(0, 0, width(), height()); - QPainter painter(this); + QPainter painter(viewport()); painter.fillRect(rect, Qt::black); diff --git a/plotview.h b/plotview.h index 9a9d3be..2600ade 100644 --- a/plotview.h +++ b/plotview.h @@ -19,12 +19,12 @@ #pragma once -#include +#include #include #include "inputsource.h" #include "plot.h" -class PlotView : public QDockWidget +class PlotView : public QAbstractScrollArea { Q_OBJECT From 74aaa9665acccddfac2e592df37906d1890eb840 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Wed, 2 Mar 2016 23:47:42 +0000 Subject: [PATCH 48/99] plot: Tweak default height --- plot.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plot.h b/plot.h index c635c00..cb66e78 100644 --- a/plot.h +++ b/plot.h @@ -36,5 +36,6 @@ protected: void setHeight(int height) { _height = height; }; private: - int _height = 50; + // TODO: don't hardcode this + int _height = 200; }; From a912406fbf6f69cd6b4c958da0ee7515ff664bfe Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Wed, 2 Mar 2016 23:48:38 +0000 Subject: [PATCH 49/99] plot: Scrolling again! --- mainwindow.cpp | 2 -- plotview.cpp | 14 +++++++------- plotview.h | 3 --- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 737f8e9..f2defed 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -41,6 +41,4 @@ void MainWindow::openFile(QString fileName) this->setWindowTitle(title.arg(QApplication::applicationName(),fileName.section('/',-1,-1))); // TODO: error check, ownership plots->inputSourceChanged(new InputSource(fileName.toUtf8().constData())); - // TODO: don't hardcode this - plots->viewChanged(0, 102400); } diff --git a/plotview.cpp b/plotview.cpp index 7f8fca4..6fd9d4d 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -20,6 +20,7 @@ #include "plotview.h" #include #include +#include #include #include #include @@ -89,13 +90,9 @@ void PlotView::inputSourceChanged(AbstractSampleSource *src) mainSampleSource = derived; refreshSources(); -} -void PlotView::viewChanged(off_t firstSample, off_t lastSample) -{ - this->firstSample = firstSample; - this->lastSample = lastSample; - update(); + horizontalScrollBar()->setMinimum(0); + horizontalScrollBar()->setMaximum(mainSampleSource->count()); } void PlotView::selectionChanged(std::pair selectionTime, std::pair selectionFreq) @@ -114,7 +111,10 @@ void PlotView::selectionCleared() void PlotView::paintEvent(QPaintEvent *event) { - if (lastSample - firstSample <= 0) return; + if (mainSampleSource == nullptr) return; + off_t firstSample = horizontalScrollBar()->value(); + // TODO: don't hardcode width + off_t lastSample = firstSample + 10240; QRect rect = QRect(0, 0, width(), height()); QPainter painter(viewport()); diff --git a/plotview.h b/plotview.h index 2600ade..72ead19 100644 --- a/plotview.h +++ b/plotview.h @@ -33,7 +33,6 @@ public: public slots: void inputSourceChanged(AbstractSampleSource *input); - void viewChanged(off_t firstSample, off_t lastSample); void selectionChanged(std::pair selectionTime, std::pair selectionFreq); void selectionCleared(); @@ -43,8 +42,6 @@ protected: private: SampleSource> *mainSampleSource = nullptr; std::vector> plots; - off_t firstSample = 0; - off_t lastSample = 0; bool selection = false; std::pair selectionTime; std::pair selectionFreq; From 8cfd3aa97b00b70ba5e11a5484293b60d28abe0d Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 01:17:43 +0000 Subject: [PATCH 50/99] plotview: Hook up setFFTSize --- mainwindow.cpp | 2 ++ plotview.cpp | 20 +++++++++++++------- plotview.h | 5 +++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index f2defed..b2e5692 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -33,6 +33,8 @@ MainWindow::MainWindow() plots = new PlotView(); setCentralWidget(plots); + + connect(dock, SIGNAL(fftSizeChanged(int)), plots, SLOT(setFFTSize(int))); } void MainWindow::openFile(QString fileName) diff --git a/plotview.cpp b/plotview.cpp index 6fd9d4d..fdb75a7 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -29,7 +29,6 @@ #include "grsamplebuffer.h" #include "memory_sink.h" #include "memory_source.h" -#include "spectrogramplot.h" #include "traceplot.h" PlotView::PlotView() @@ -41,16 +40,16 @@ void PlotView::refreshSources() { plots.clear(); - auto sp = new SpectrogramPlot(mainSampleSource); - plots.emplace_back(sp); + spectrogramPlot = new SpectrogramPlot(mainSampleSource); + plots.emplace_back(spectrogramPlot); gr::top_block_sptr iq_tb = gr::make_top_block("multiply"); auto iq_mem_source = gr::blocks::memory_source::make(8); auto iq_mem_sink = gr::blocks::memory_sink::make(8); auto multiply = gr::blocks::multiply_const_cc::make(20); - if (selection) { - float centre = (selectionFreq.first + selectionFreq.second) / 2; - float cutoff = std::abs(selectionFreq.first - centre); + if (selection || true) { + float centre = -0.05; //(selectionFreq.first + selectionFreq.second) / 2; + float cutoff = 0.02; //std::abs(selectionFreq.first - centre); auto lp_taps = gr::filter::firdes::low_pass(1.0, 1.0, cutoff, cutoff / 2); auto filter = gr::filter::freq_xlating_fir_filter_ccf::make(1, lp_taps, centre, 1.0); @@ -109,12 +108,19 @@ void PlotView::selectionCleared() refreshSources(); } +void PlotView::setFFTSize(int size) +{ + fftSize = size; + spectrogramPlot->setFFTSize(size); + viewport()->update(); +} + void PlotView::paintEvent(QPaintEvent *event) { if (mainSampleSource == nullptr) return; off_t firstSample = horizontalScrollBar()->value(); // TODO: don't hardcode width - off_t lastSample = firstSample + 10240; + off_t lastSample = firstSample + fftSize * width(); QRect rect = QRect(0, 0, width(), height()); QPainter painter(viewport()); diff --git a/plotview.h b/plotview.h index 72ead19..ffa5ed2 100644 --- a/plotview.h +++ b/plotview.h @@ -23,6 +23,7 @@ #include #include "inputsource.h" #include "plot.h" +#include "spectrogramplot.h" class PlotView : public QAbstractScrollArea { @@ -35,16 +36,20 @@ public slots: void inputSourceChanged(AbstractSampleSource *input); void selectionChanged(std::pair selectionTime, std::pair selectionFreq); void selectionCleared(); + void setFFTSize(int size); protected: void paintEvent(QPaintEvent *event); private: SampleSource> *mainSampleSource = nullptr; + SpectrogramPlot *spectrogramPlot; std::vector> plots; bool selection = false; std::pair selectionTime; std::pair selectionFreq; + int fftSize; + void refreshSources(); }; \ No newline at end of file From af7fb31eeb7c4b574d3c628cdb7252f9af5b3058 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 01:40:50 +0000 Subject: [PATCH 51/99] plotview: Hook up zoomLevel & fix up scrolling --- mainwindow.cpp | 1 + plotview.cpp | 12 ++++++++++-- plotview.h | 2 ++ spectrogramplot.cpp | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index b2e5692..dd31c9b 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -35,6 +35,7 @@ MainWindow::MainWindow() setCentralWidget(plots); connect(dock, SIGNAL(fftSizeChanged(int)), plots, SLOT(setFFTSize(int))); + connect(dock->zoomLevelSlider, SIGNAL(valueChanged(int)), plots, SLOT(setZoomLevel(int))); } void MainWindow::openFile(QString fileName) diff --git a/plotview.cpp b/plotview.cpp index fdb75a7..aec5c28 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -112,6 +112,15 @@ void PlotView::setFFTSize(int size) { fftSize = size; spectrogramPlot->setFFTSize(size); + horizontalScrollBar()->setSingleStep(size * pow(2, zoomLevel)); + horizontalScrollBar()->setPageStep(size * pow(2, zoomLevel) * 10); + viewport()->update(); +} + +void PlotView::setZoomLevel(int zoom) +{ + zoomLevel = zoom; + spectrogramPlot->setZoomLevel(zoom); viewport()->update(); } @@ -119,8 +128,7 @@ void PlotView::paintEvent(QPaintEvent *event) { if (mainSampleSource == nullptr) return; off_t firstSample = horizontalScrollBar()->value(); - // TODO: don't hardcode width - off_t lastSample = firstSample + fftSize * width(); + off_t lastSample = firstSample + fftSize * width() / pow(2, zoomLevel); QRect rect = QRect(0, 0, width(), height()); QPainter painter(viewport()); diff --git a/plotview.h b/plotview.h index ffa5ed2..8caeee5 100644 --- a/plotview.h +++ b/plotview.h @@ -37,6 +37,7 @@ public slots: void selectionChanged(std::pair selectionTime, std::pair selectionFreq); void selectionCleared(); void setFFTSize(int size); + void setZoomLevel(int zoom); protected: void paintEvent(QPaintEvent *event); @@ -50,6 +51,7 @@ private: std::pair selectionFreq; int fftSize; + int zoomLevel; void refreshSources(); }; \ No newline at end of file diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index 5e519a9..295d1c0 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -118,7 +118,7 @@ void SpectrogramPlot::mousePressEvent(QMouseEvent *event) void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) { for (int x = rect.left(); x < rect.right(); x += linesPerTile()) { - QPixmap *tile = getPixmapTile(x * fftSize); + QPixmap *tile = getPixmapTile(sampleRange.minimum + x * getStride()); painter.drawPixmap(QRect(x, rect.y(), linesPerTile(), fftSize), *tile); } } From 5c29d72e541df65e27c597a0401a0063b6c0aa6c Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 02:45:43 +0000 Subject: [PATCH 52/99] plotview: Tweak scrolling --- plotview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plotview.cpp b/plotview.cpp index aec5c28..effe04a 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -112,8 +112,8 @@ void PlotView::setFFTSize(int size) { fftSize = size; spectrogramPlot->setFFTSize(size); - horizontalScrollBar()->setSingleStep(size * pow(2, zoomLevel)); - horizontalScrollBar()->setPageStep(size * pow(2, zoomLevel) * 10); + horizontalScrollBar()->setSingleStep(size * 10 / pow(2, zoomLevel)); + horizontalScrollBar()->setPageStep(size * 100 / pow(2, zoomLevel)); viewport()->update(); } From 570056460bfcfa06ff942b774ff5e38c2abeea0b Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 15:50:19 +0000 Subject: [PATCH 53/99] controls: Remove time scale checkbox --- spectrogramcontrols.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 3c8197f..ebb6553 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -56,10 +56,6 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent powerMinSlider->setValue(-50); layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); - timeScaleCheckBox = new QCheckBox(widget); - timeScaleCheckBox->setCheckState(Qt::Checked); - layout->addRow(new QLabel(tr("time overlay:")), timeScaleCheckBox); - cursorFrequencyLabel = new QLabel(); layout->addRow(new QLabel(tr("Cursor frequency:")), cursorFrequencyLabel); From 342ab935420b5c842e677358efcfa0e0f21e46f5 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 15:52:53 +0000 Subject: [PATCH 54/99] controls: Add enable cursors checkbox --- spectrogramcontrols.cpp | 4 ++++ spectrogramcontrols.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index ebb6553..58d5f49 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -56,6 +56,10 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent powerMinSlider->setValue(-50); layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); + cursorsCheckBox = new QCheckBox(widget); + cursorsCheckBox->setCheckState(Qt::Unchecked); + layout->addRow(new QLabel(tr("Enable cursors:")), cursorsCheckBox); + cursorFrequencyLabel = new QLabel(); layout->addRow(new QLabel(tr("Cursor frequency:")), cursorFrequencyLabel); diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index 943deb5..0da8858 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -52,7 +52,7 @@ public: QSlider *zoomLevelSlider; QSlider *powerMaxSlider; QSlider *powerMinSlider; - QCheckBox *timeScaleCheckBox; + QCheckBox *cursorsCheckBox; QLabel *cursorFrequencyLabel; QLabel *cursorTimeLabel; QCheckBox *deltaDragCheckBox; From 590791af2f6b970f8c023acf60619fe91a5267c7 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 16:25:24 +0000 Subject: [PATCH 55/99] cursors: Add cursors widget (for time selection) --- CMakeLists.txt | 1 + cursors.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ cursors.h | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 cursors.cpp create mode 100644 cursors.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 102f569..d664df3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ endif (NOT CMAKE_CXX_FLAGS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") list(APPEND inspectrum_sources + cursors.cpp main.cpp fft.cpp mainwindow.cpp diff --git a/cursors.cpp b/cursors.cpp new file mode 100644 index 0000000..5a9e38a --- /dev/null +++ b/cursors.cpp @@ -0,0 +1,45 @@ +/* + * 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 +#include "cursors.h" + +Cursors::Cursors(QWidget * parent) + : QWidget::QWidget(parent) +{ + +} + +void Cursors::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + painter.save(); + + // Draw translucent white fill for highlight + painter.fillRect(rect(), QBrush(QColor(255, 255, 255, 50))); + + // Draw vertical edges + QPen pen(Qt::white, 1, Qt::DashLine); + painter.setPen(pen); + painter.drawLine(rect().left(), rect().top(), rect().left(), rect().bottom()); + painter.drawLine(rect().right(), rect().top(), rect().right(), rect().bottom()); + + painter.restore(); +} diff --git a/cursors.h b/cursors.h new file mode 100644 index 0000000..6d0ef6c --- /dev/null +++ b/cursors.h @@ -0,0 +1,34 @@ +/* + * 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 + +class Cursors : public QWidget +{ + Q_OBJECT + +public: + Cursors(QWidget * parent); + +protected: + void paintEvent(QPaintEvent *event); + +}; From 6ee980b294c0a6eea334565a3e83902151253c51 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 17:26:54 +0000 Subject: [PATCH 56/99] plotview: Connect up cursors --- mainwindow.cpp | 1 + plotview.cpp | 21 ++++++++++++++++++++- plotview.h | 5 +++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index dd31c9b..f7eda87 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -36,6 +36,7 @@ MainWindow::MainWindow() connect(dock, SIGNAL(fftSizeChanged(int)), plots, SLOT(setFFTSize(int))); connect(dock->zoomLevelSlider, SIGNAL(valueChanged(int)), plots, SLOT(setZoomLevel(int))); + connect(dock->cursorsCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableCursors); } void MainWindow::openFile(QString fileName) diff --git a/plotview.cpp b/plotview.cpp index effe04a..9433f56 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -31,9 +31,10 @@ #include "memory_source.h" #include "traceplot.h" -PlotView::PlotView() +PlotView::PlotView() : cursors(this) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + enableCursors(false); } void PlotView::refreshSources() @@ -81,6 +82,14 @@ void PlotView::refreshSources() update(); } +void PlotView::enableCursors(bool enabled) +{ + if (enabled) + cursors.show(); + else + cursors.hide(); +} + void PlotView::inputSourceChanged(AbstractSampleSource *src) { auto derived = dynamic_cast>*>(src); @@ -151,3 +160,13 @@ void PlotView::paintEvent(QPaintEvent *event) #undef PLOT_LAYER } + +void PlotView::resizeEvent(QResizeEvent * event) +{ + QRect rect = viewport()->rect(); + + // Resize cursors + // TODO: don't hardcode this + int margin = rect.width() / 3; + cursors.setGeometry(QRect(rect.left() + margin, rect.top(), rect.right() - rect.left() - 2 * margin, rect.height())); +} diff --git a/plotview.h b/plotview.h index 8caeee5..b359352 100644 --- a/plotview.h +++ b/plotview.h @@ -21,6 +21,8 @@ #include #include + +#include "cursors.h" #include "inputsource.h" #include "plot.h" #include "spectrogramplot.h" @@ -33,6 +35,7 @@ public: PlotView(); public slots: + void enableCursors(bool enable); void inputSourceChanged(AbstractSampleSource *input); void selectionChanged(std::pair selectionTime, std::pair selectionFreq); void selectionCleared(); @@ -41,8 +44,10 @@ public slots: protected: void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent * event); private: + Cursors cursors; SampleSource> *mainSampleSource = nullptr; SpectrogramPlot *spectrogramPlot; std::vector> plots; From b9f82e9944244b43bec6f09e103ddf1a21d0a714 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 18:27:51 +0000 Subject: [PATCH 57/99] controls: Reorgnise controls --- spectrogramcontrols.cpp | 24 ++++++++++++++++++------ spectrogramcontrols.h | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 58d5f49..75df47e 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -36,6 +36,10 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent sampleRate->setValidator(new QIntValidator(this)); layout->addRow(new QLabel(tr("Sample rate:")), sampleRate); + // Spectrogram settings + layout->addRow(new QLabel()); // TODO: find a better way to add an empty row? + layout->addRow(new QLabel(tr("Spectrogram"))); + fftSizeSlider = new QSlider(Qt::Horizontal, widget); fftSizeSlider->setRange(7, 13); fftSizeSlider->setValue(10); @@ -56,16 +60,24 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent powerMinSlider->setValue(-50); layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); + // Pointer position info + layout->addRow(new QLabel()); // TODO: find a better way to add an empty row? + layout->addRow(new QLabel(tr("Pointer"))); + + pointerFrequencyLabel = new QLabel(); + layout->addRow(new QLabel(tr("Pointer frequency:")), pointerFrequencyLabel); + + pointerTimeLabel = new QLabel(); + layout->addRow(new QLabel(tr("Pointer time:")), pointerTimeLabel); + + // Selection settings + layout->addRow(new QLabel()); // TODO: find a better way to add an empty row? + layout->addRow(new QLabel(tr("Selection"))); + cursorsCheckBox = new QCheckBox(widget); cursorsCheckBox->setCheckState(Qt::Unchecked); layout->addRow(new QLabel(tr("Enable cursors:")), cursorsCheckBox); - cursorFrequencyLabel = new QLabel(); - layout->addRow(new QLabel(tr("Cursor frequency:")), cursorFrequencyLabel); - - cursorTimeLabel = new QLabel(); - layout->addRow(new QLabel(tr("Cursor time:")), cursorTimeLabel); - deltaDragCheckBox = new QCheckBox(widget); deltaDragCheckBox->setCheckState(Qt::Checked); layout->addRow(new QLabel(tr("Delta dragging:")), deltaDragCheckBox); diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index 0da8858..9b18df4 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -53,8 +53,8 @@ public: QSlider *powerMaxSlider; QSlider *powerMinSlider; QCheckBox *cursorsCheckBox; - QLabel *cursorFrequencyLabel; - QLabel *cursorTimeLabel; + QLabel *pointerFrequencyLabel; + QLabel *pointerTimeLabel; QCheckBox *deltaDragCheckBox; QLabel *deltaFrequencyLabel; QLabel *deltaTimeLabel; From 19734da0b22a66b95b71effb41376bfd8f9f0d90 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 20:52:34 +0000 Subject: [PATCH 58/99] plotview: Store view range --- plotview.cpp | 34 ++++++++++++++++++++++++++-------- plotview.h | 5 ++++- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/plotview.cpp b/plotview.cpp index 9433f56..cabad05 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -31,7 +31,7 @@ #include "memory_source.h" #include "traceplot.h" -PlotView::PlotView() : cursors(this) +PlotView::PlotView() : cursors(this), viewRange({0, 0}) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); enableCursors(false); @@ -40,6 +40,8 @@ PlotView::PlotView() : cursors(this) void PlotView::refreshSources() { plots.clear(); + if (mainSampleSource == nullptr) + return; spectrogramPlot = new SpectrogramPlot(mainSampleSource); plots.emplace_back(spectrogramPlot); @@ -120,24 +122,24 @@ void PlotView::selectionCleared() void PlotView::setFFTSize(int size) { fftSize = size; - spectrogramPlot->setFFTSize(size); + if (spectrogramPlot != nullptr) + spectrogramPlot->setFFTSize(size); horizontalScrollBar()->setSingleStep(size * 10 / pow(2, zoomLevel)); horizontalScrollBar()->setPageStep(size * 100 / pow(2, zoomLevel)); - viewport()->update(); + updateView(); } void PlotView::setZoomLevel(int zoom) { zoomLevel = zoom; - spectrogramPlot->setZoomLevel(zoom); - viewport()->update(); + if (spectrogramPlot != nullptr) + spectrogramPlot->setZoomLevel(zoom); + updateView(); } void PlotView::paintEvent(QPaintEvent *event) { if (mainSampleSource == nullptr) return; - off_t firstSample = horizontalScrollBar()->value(); - off_t lastSample = firstSample + fftSize * width() / pow(2, zoomLevel); QRect rect = QRect(0, 0, width(), height()); QPainter painter(viewport()); @@ -149,7 +151,7 @@ void PlotView::paintEvent(QPaintEvent *event) int y = 0; \ for (auto&& plot : plots) { \ QRect rect = QRect(0, y, width(), plot->height()); \ - plot->paintFunc(painter, rect, {firstSample, lastSample}); \ + plot->paintFunc(painter, rect, {viewRange.first, viewRange.second});\ y += plot->height(); \ } \ } @@ -169,4 +171,20 @@ void PlotView::resizeEvent(QResizeEvent * event) // TODO: don't hardcode this int margin = rect.width() / 3; cursors.setGeometry(QRect(rect.left() + margin, rect.top(), rect.right() - rect.left() - 2 * margin, rect.height())); + + updateView(); +} + +void PlotView::scrollContentsBy(int dx, int dy) +{ + updateView(); +} + +void PlotView::updateView() +{ + viewRange = { + horizontalScrollBar()->value(), + horizontalScrollBar()->value() + fftSize * width() / (int)pow(2, zoomLevel) + }; + viewport()->update(); } diff --git a/plotview.h b/plotview.h index b359352..3c0e3a6 100644 --- a/plotview.h +++ b/plotview.h @@ -45,12 +45,14 @@ public slots: protected: void paintEvent(QPaintEvent *event); void resizeEvent(QResizeEvent * event); + void scrollContentsBy(int dx, int dy); private: Cursors cursors; SampleSource> *mainSampleSource = nullptr; - SpectrogramPlot *spectrogramPlot; + SpectrogramPlot *spectrogramPlot = nullptr; std::vector> plots; + std::pair viewRange; bool selection = false; std::pair selectionTime; std::pair selectionFreq; @@ -59,4 +61,5 @@ private: int zoomLevel; void refreshSources(); + void updateView(); }; \ No newline at end of file From 7d75ec426acbe635e89765ead6ab73f3d8022f7f Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 20:54:50 +0000 Subject: [PATCH 59/99] spectrogram: Remove some dead code (ruler, old paintEvent) --- spectrogramplot.cpp | 48 --------------------------------------------- spectrogramplot.h | 2 -- 2 files changed, 50 deletions(-) diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index 295d1c0..e3f38d1 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -128,34 +128,6 @@ void SpectrogramPlot::paintFront(QPainter &painter, QRect &rect, range_t } - -/*void SpectrogramPlot::paintEvent(QPaintEvent *event) -{ - QRect rect = event->rect(); - QPainter painter(this); - painter.fillRect(rect, Qt::black); - - if (inputSource != nullptr) { - int height = rect.height(); - off_t y = rect.y(); - - QImage image(fftSize, height, QImage::Format_RGB32); - - while (height > 0) { - int tileOffset = y % linesPerTile(); // To handle drawing a partial first tile - int drawHeight = std::min(linesPerTile() - tileOffset, height); // Draw rest of first tile, full tile, or partial final tile - off_t tileId = lineToSample(y - tileOffset); - QPixmap *tile = getPixmapTile(tileId); - painter.drawPixmap(QRect(0, y, fftSize, drawHeight), *tile, QRect(0, tileOffset, fftSize, drawHeight)); - y += drawHeight; - height -= drawHeight; - } - - paintTimeAxis(&painter, rect); - paintCursors(&painter, rect); - } -}*/ - QPixmap* SpectrogramPlot::getPixmapTile(off_t tile) { QPixmap *obj = pixmapCache.object(TileCacheKey(fftSize, zoomLevel, tile)); @@ -236,26 +208,6 @@ void SpectrogramPlot::paintCursors(QPainter *painter, QRect rect) } } -void SpectrogramPlot::paintTimeAxis(QPainter *painter, QRect rect) -{ - if (timeScaleIsEnabled) { - // Round up for firstLine and round each to nearest linesPerGraduation - int firstLine = ((rect.y() + linesPerGraduation - 1) / linesPerGraduation) * linesPerGraduation; - int lastLine = ((rect.y() + rect.height()) / linesPerGraduation) * linesPerGraduation; - - painter->save(); - QPen pen(Qt::white, 1, Qt::SolidLine); - painter->setPen(pen); - QFontMetrics fm(painter->font()); - int textOffset = fm.ascent() / 2 - 1; - for (int line = firstLine; line <= lastLine; line += linesPerGraduation) { - painter->drawLine(0, line, 10, line); - painter->drawText(12, line + textOffset, sampleToTime(lineToSample(line))); - } - painter->restore(); - } -} - void SpectrogramPlot::setSampleRate(int rate) { sampleRate = rate; diff --git a/spectrogramplot.h b/spectrogramplot.h index cc11272..e4e8f2d 100644 --- a/spectrogramplot.h +++ b/spectrogramplot.h @@ -69,7 +69,6 @@ public slots: void update() { emit needUpdate(); }; protected: - void paintEvent(QPaintEvent *event); void mouseReleaseEvent(QMouseEvent * event); void mouseMoveEvent(QMouseEvent * event); void mousePressEvent(QMouseEvent * event); @@ -99,7 +98,6 @@ private: QPixmap* getPixmapTile(off_t tile); float* getFFTTile(off_t tile); void getLine(float *dest, off_t sample); - void paintTimeAxis(QPainter *painter, QRect rect); void paintCursors(QPainter *painter, QRect rect); int sampleToLine(off_t sample); QString sampleToTime(off_t sample); From b789d3a295dd4f944944f4dd341650f6a72f8746 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 22:02:30 +0000 Subject: [PATCH 60/99] samplesource: Add subscription functionality --- CMakeLists.txt | 1 + samplebuffer.cpp | 12 ++++++++++++ samplebuffer.h | 5 +++-- samplesource.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ samplesource.h | 11 +++++++++++ subscriber.h | 27 +++++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 samplesource.cpp create mode 100644 subscriber.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d664df3..ca6c214 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ list(APPEND inspectrum_sources plot.cpp plotview.cpp samplebuffer.cpp + samplesource.cpp spectrogramcontrols.cpp spectrogramplot.cpp traceplot.cpp diff --git a/samplebuffer.cpp b/samplebuffer.cpp index a34a2f8..b4c6f65 100644 --- a/samplebuffer.cpp +++ b/samplebuffer.cpp @@ -19,6 +19,12 @@ #include "samplebuffer.h" +template +SampleBuffer::SampleBuffer(SampleSource *src) : src(src) +{ + src->subscribe(this); +} + template std::unique_ptr SampleBuffer::getSamples(off_t start, off_t length) { @@ -28,5 +34,11 @@ std::unique_ptr SampleBuffer::getSamples(off_t start, off_t l return dest; } +template +void SampleBuffer::invalidEvent() +{ + SampleSource::invalidate(); +} + template class SampleBuffer, std::complex>; template class SampleBuffer, float>; diff --git a/samplebuffer.h b/samplebuffer.h index 93fc135..511d14a 100644 --- a/samplebuffer.h +++ b/samplebuffer.h @@ -24,13 +24,14 @@ #include "samplesource.h" template -class SampleBuffer : public SampleSource +class SampleBuffer : public SampleSource, public Subscriber { private: SampleSource *src; public: - SampleBuffer(SampleSource *src) : src(src) {}; + SampleBuffer(SampleSource *src); + void invalidEvent(); virtual std::unique_ptr getSamples(off_t start, off_t length); virtual void work(void *input, void *output, int count) = 0; virtual off_t count() { diff --git a/samplesource.cpp b/samplesource.cpp new file mode 100644 index 0000000..123a203 --- /dev/null +++ b/samplesource.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015, 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" + +template +void SampleSource::subscribe(Subscriber *subscriber) +{ + subscribers.insert(subscriber); +} + +template +void SampleSource::invalidate() +{ + for (auto subscriber : subscribers) { + subscriber->invalidateEvent(); + } +} + +template +void SampleSource::unsubscribe(Subscriber *subscriber) +{ + subscribers.erase(subscriber); +} + +template class SampleSource>; +template class SampleSource; diff --git a/samplesource.h b/samplesource.h index 22d5bbd..7035218 100644 --- a/samplesource.h +++ b/samplesource.h @@ -21,7 +21,9 @@ #include #include +#include #include "abstractsamplesource.h" +#include "subscriber.h" template class SampleSource : public AbstractSampleSource @@ -31,5 +33,14 @@ public: virtual ~SampleSource() {}; virtual std::unique_ptr getSamples(off_t start, off_t length) = 0; + virtual void invalidateEvent() { }; virtual off_t count() = 0; + void subscribe(Subscriber *subscriber); + void unsubscribe(Subscriber *subscriber); + +protected: + virtual void invalidate(); + +private: + std::set subscribers; }; diff --git a/subscriber.h b/subscriber.h new file mode 100644 index 0000000..bbf4c67 --- /dev/null +++ b/subscriber.h @@ -0,0 +1,27 @@ +/* + * 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 + + +class Subscriber +{ +public: + virtual void invalidateEvent() = 0; +}; From e1b99805a24c28398909c1e2f43c0c9537eac7f2 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 22:18:33 +0000 Subject: [PATCH 61/99] spectrogram: Remove more dead code (openFile) --- spectrogramplot.cpp | 15 --------------- spectrogramplot.h | 1 - 2 files changed, 16 deletions(-) diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index e3f38d1..d7c0d3c 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -58,21 +58,6 @@ QSize SpectrogramPlot::sizeHint() const return QSize(1024, 2048); } -void SpectrogramPlot::openFile(QString fileName) -{ - if (fileName != nullptr) { - try { - InputSource *newFile = new InputSource(fileName.toUtf8().constData()); - delete inputSource; - pixmapCache.clear(); - fftCache.clear(); - inputSource = newFile; - } catch (std::runtime_error e) { - // TODO: display error - } - } -} - void SpectrogramPlot::xyToFreqTime(int x, int y, float *freq, float *time) { *freq = labs(x - (fftSize / 2)) * sampleRate / 2 / (float)fftSize; diff --git a/spectrogramplot.h b/spectrogramplot.h index e4e8f2d..71feda8 100644 --- a/spectrogramplot.h +++ b/spectrogramplot.h @@ -58,7 +58,6 @@ signals: void needUpdate(); public slots: - void openFile(QString fileName); void setSampleRate(int rate); void setFFTSize(int size); void setPowerMax(int power); From 2135e03c01edb461f46cbb02a276b32af45cb92f Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 22:20:57 +0000 Subject: [PATCH 62/99] input: Start refactoring InputSource to be able to open/close files --- inputsource.cpp | 63 ++++++++++++++++++++++++++++++++++++------------- inputsource.h | 13 +++++----- mainwindow.cpp | 7 +++--- plotview.cpp | 3 ++- plotview.h | 2 +- 5 files changed, 60 insertions(+), 28 deletions(-) diff --git a/inputsource.cpp b/inputsource.cpp index 1fea732..24d182b 100644 --- a/inputsource.cpp +++ b/inputsource.cpp @@ -27,31 +27,60 @@ #include -InputSource::InputSource(const char *filename) +InputSource::InputSource() { - m_file = fopen(filename, "rb"); - if (m_file == nullptr) - throw std::runtime_error("Error opening file"); - - struct stat sb; - if (fstat(fileno(m_file), &sb) != 0) - throw std::runtime_error("Error fstating file"); - m_file_size = sb.st_size; - sampleCount = m_file_size / sizeof(std::complex); - - m_data = (std::complex*)mmap(NULL, m_file_size, PROT_READ, MAP_SHARED, fileno(m_file), 0); - if (m_data == 0) - throw std::runtime_error("Error mmapping file"); } InputSource::~InputSource() { - munmap(m_data, m_file_size); - fclose(m_file); + cleanup(); +} + +void InputSource::cleanup() +{ + if (mmapData != nullptr) { + munmap(mmapData, fileSize); + mmapData = nullptr; + fileSize = 0; + } + + if (inputFile != nullptr) { + fclose(inputFile); + inputFile = nullptr; + } +} + +void InputSource::openFile(const char *filename) +{ + FILE *file = fopen(filename, "rb"); + if (file == nullptr) + throw std::runtime_error("Error opening file"); + + struct stat sb; + if (fstat(fileno(file), &sb) != 0) + throw std::runtime_error("Error fstating file"); + off_t size = sb.st_size; + sampleCount = size / sizeof(std::complex); + + auto data = (std::complex*)mmap(NULL, size, PROT_READ, MAP_SHARED, fileno(file), 0); + if (data == nullptr) + throw std::runtime_error("Error mmapping file"); + + cleanup(); + + inputFile = file; + fileSize = size; + mmapData = data; } std::unique_ptr[]> InputSource::getSamples(off_t start, off_t length) { + if (inputFile == nullptr) + return nullptr; + + if (mmapData == nullptr) + return nullptr; + if(start < 0 || length < 0) return nullptr; @@ -59,6 +88,6 @@ std::unique_ptr[]> InputSource::getSamples(off_t start, off_ return nullptr; std::unique_ptr[]> dest(new std::complex[length]); - memcpy(dest.get(), &m_data[start], length * sizeof(std::complex)); + memcpy(dest.get(), &mmapData[start], length * sizeof(std::complex)); return dest; } diff --git a/inputsource.h b/inputsource.h index ea31e1e..75feeac 100644 --- a/inputsource.h +++ b/inputsource.h @@ -25,15 +25,16 @@ class InputSource : public SampleSource> { private: - FILE *m_file; - off_t m_file_size; - off_t sampleCount; - std::complex *m_data; + FILE *inputFile = nullptr; + off_t fileSize = 0; + off_t sampleCount = 0; + std::complex *mmapData = nullptr; public: - InputSource(const char *filename); + InputSource(); ~InputSource(); - + void cleanup(); + void openFile(const char *filename); std::unique_ptr[]> getSamples(off_t start, off_t length); off_t count() { return sampleCount; diff --git a/mainwindow.cpp b/mainwindow.cpp index f7eda87..7535212 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -31,7 +31,9 @@ MainWindow::MainWindow() dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); addDockWidget(Qt::LeftDockWidgetArea, dock); - plots = new PlotView(); + InputSource *input = new InputSource(); + + plots = new PlotView(input); setCentralWidget(plots); connect(dock, SIGNAL(fftSizeChanged(int)), plots, SLOT(setFFTSize(int))); @@ -43,6 +45,5 @@ void MainWindow::openFile(QString fileName) { QString title="%1: %2"; this->setWindowTitle(title.arg(QApplication::applicationName(),fileName.section('/',-1,-1))); - // TODO: error check, ownership - plots->inputSourceChanged(new InputSource(fileName.toUtf8().constData())); + // TODO: open a file again } diff --git a/plotview.cpp b/plotview.cpp index cabad05..a0a0582 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -31,8 +31,9 @@ #include "memory_source.h" #include "traceplot.h" -PlotView::PlotView() : cursors(this), viewRange({0, 0}) +PlotView::PlotView(InputSource *input) : cursors(this), viewRange({0, 0}) { + mainSampleSource = input; setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); enableCursors(false); } diff --git a/plotview.h b/plotview.h index 3c0e3a6..6f6a6c0 100644 --- a/plotview.h +++ b/plotview.h @@ -32,7 +32,7 @@ class PlotView : public QAbstractScrollArea Q_OBJECT public: - PlotView(); + PlotView(InputSource *input); public slots: void enableCursors(bool enable); From fb7a8921490a22893acbf5f8e84c74c7581dcff5 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 22:29:38 +0000 Subject: [PATCH 63/99] input: Hook up file opening & invalidationEvent --- inputsource.cpp | 2 ++ mainwindow.cpp | 4 ++-- mainwindow.h | 1 + plotview.cpp | 9 +++------ plotview.h | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/inputsource.cpp b/inputsource.cpp index 24d182b..de1bbc5 100644 --- a/inputsource.cpp +++ b/inputsource.cpp @@ -71,6 +71,8 @@ void InputSource::openFile(const char *filename) inputFile = file; fileSize = size; mmapData = data; + + invalidate(); } std::unique_ptr[]> InputSource::getSamples(off_t start, off_t length) diff --git a/mainwindow.cpp b/mainwindow.cpp index 7535212..f2f8f64 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -31,7 +31,7 @@ MainWindow::MainWindow() dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); addDockWidget(Qt::LeftDockWidgetArea, dock); - InputSource *input = new InputSource(); + input = new InputSource(); plots = new PlotView(input); setCentralWidget(plots); @@ -45,5 +45,5 @@ void MainWindow::openFile(QString fileName) { QString title="%1: %2"; this->setWindowTitle(title.arg(QApplication::applicationName(),fileName.section('/',-1,-1))); - // TODO: open a file again + input->openFile(fileName.toUtf8().constData()); } diff --git a/mainwindow.h b/mainwindow.h index 5601386..f29bd99 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -38,4 +38,5 @@ public slots: private: SpectrogramControls *dock; PlotView *plots; + InputSource *input; }; diff --git a/plotview.cpp b/plotview.cpp index a0a0582..7822696 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -36,6 +36,8 @@ PlotView::PlotView(InputSource *input) : cursors(this), viewRange({0, 0}) mainSampleSource = input; setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); enableCursors(false); + + mainSampleSource->subscribe(this); } void PlotView::refreshSources() @@ -93,13 +95,8 @@ void PlotView::enableCursors(bool enabled) cursors.hide(); } -void PlotView::inputSourceChanged(AbstractSampleSource *src) +void PlotView::invalidateEvent() { - auto derived = dynamic_cast>*>(src); - if (derived == nullptr) - throw new std::runtime_error("SampleSource doesn't provide correct type for GRSampleBuffer"); - - mainSampleSource = derived; refreshSources(); horizontalScrollBar()->setMinimum(0); diff --git a/plotview.h b/plotview.h index 6f6a6c0..3b204f2 100644 --- a/plotview.h +++ b/plotview.h @@ -27,7 +27,7 @@ #include "plot.h" #include "spectrogramplot.h" -class PlotView : public QAbstractScrollArea +class PlotView : public QAbstractScrollArea, Subscriber { Q_OBJECT @@ -36,7 +36,7 @@ public: public slots: void enableCursors(bool enable); - void inputSourceChanged(AbstractSampleSource *input); + void invalidateEvent(); void selectionChanged(std::pair selectionTime, std::pair selectionFreq); void selectionCleared(); void setFFTSize(int size); From ffdd314f78e4a62b378cdf6dd0c55722c423863d Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 22:43:56 +0000 Subject: [PATCH 64/99] samplebuffer: Fix invalidateEvent typos --- samplebuffer.cpp | 2 +- samplebuffer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samplebuffer.cpp b/samplebuffer.cpp index b4c6f65..f550474 100644 --- a/samplebuffer.cpp +++ b/samplebuffer.cpp @@ -35,7 +35,7 @@ std::unique_ptr SampleBuffer::getSamples(off_t start, off_t l } template -void SampleBuffer::invalidEvent() +void SampleBuffer::invalidateEvent() { SampleSource::invalidate(); } diff --git a/samplebuffer.h b/samplebuffer.h index 511d14a..c9f0fae 100644 --- a/samplebuffer.h +++ b/samplebuffer.h @@ -31,7 +31,7 @@ private: public: SampleBuffer(SampleSource *src); - void invalidEvent(); + void invalidateEvent(); virtual std::unique_ptr getSamples(off_t start, off_t length); virtual void work(void *input, void *output, int count) = 0; virtual off_t count() { From 9b78f334aa3c6c4ff04bb80d0e1b491a0eb22809 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 23:05:09 +0000 Subject: [PATCH 65/99] plotview: Don't recreate the plots all the time --- plotview.cpp | 41 ++++++++++++++++++----------------------- plotview.h | 6 +++++- spectrogramplot.cpp | 3 +++ traceplot.h | 1 + 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/plotview.cpp b/plotview.cpp index 7822696..b860241 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -29,7 +29,6 @@ #include "grsamplebuffer.h" #include "memory_sink.h" #include "memory_source.h" -#include "traceplot.h" PlotView::PlotView(InputSource *input) : cursors(this), viewRange({0, 0}) { @@ -37,18 +36,18 @@ PlotView::PlotView(InputSource *input) : cursors(this), viewRange({0, 0}) setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); enableCursors(false); - mainSampleSource->subscribe(this); -} - -void PlotView::refreshSources() -{ - plots.clear(); - if (mainSampleSource == nullptr) - return; - spectrogramPlot = new SpectrogramPlot(mainSampleSource); plots.emplace_back(spectrogramPlot); + iqPlot = createIQPlot(mainSampleSource); + plots.emplace_back(iqPlot); + plots.emplace_back(createQuadratureDemodPlot(static_cast>*>(iqPlot->source().get()))); + + mainSampleSource->subscribe(this); +} + +TracePlot* PlotView::createIQPlot(SampleSource> *src) +{ gr::top_block_sptr iq_tb = gr::make_top_block("multiply"); auto iq_mem_source = gr::blocks::memory_source::make(8); auto iq_mem_sink = gr::blocks::memory_sink::make(8); @@ -67,6 +66,12 @@ void PlotView::refreshSources() iq_tb->connect(multiply, 0, iq_mem_sink, 0); } + auto iq_src = std::make_shared, std::complex>>(mainSampleSource, iq_tb, iq_mem_source, iq_mem_sink); + return new TracePlot(iq_src); +} + +TracePlot* PlotView::createQuadratureDemodPlot(SampleSource> *src) +{ gr::top_block_sptr quad_demod_tb = gr::make_top_block("quad_demod"); auto quad_demod_mem_source = gr::blocks::memory_source::make(8); auto quad_demod_mem_sink = gr::blocks::memory_sink::make(4); @@ -74,17 +79,11 @@ 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); - 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 - ) + return new TracePlot( + std::make_shared, float>>( + dynamic_cast>*>(src), quad_demod_tb, quad_demod_mem_source, quad_demod_mem_sink ) ); - update(); } void PlotView::enableCursors(bool enabled) @@ -97,8 +96,6 @@ void PlotView::enableCursors(bool enabled) void PlotView::invalidateEvent() { - refreshSources(); - horizontalScrollBar()->setMinimum(0); horizontalScrollBar()->setMaximum(mainSampleSource->count()); } @@ -108,13 +105,11 @@ void PlotView::selectionChanged(std::pair selectionTime, std::pair this->selectionTime = selectionTime; this->selectionFreq = selectionFreq; selection = true; - refreshSources(); } void PlotView::selectionCleared() { selection = false; - refreshSources(); } void PlotView::setFFTSize(int size) diff --git a/plotview.h b/plotview.h index 3b204f2..5983b1c 100644 --- a/plotview.h +++ b/plotview.h @@ -25,7 +25,9 @@ #include "cursors.h" #include "inputsource.h" #include "plot.h" +#include "samplesource.h" #include "spectrogramplot.h" +#include "traceplot.h" class PlotView : public QAbstractScrollArea, Subscriber { @@ -51,6 +53,7 @@ private: Cursors cursors; SampleSource> *mainSampleSource = nullptr; SpectrogramPlot *spectrogramPlot = nullptr; + TracePlot *iqPlot = nullptr; std::vector> plots; std::pair viewRange; bool selection = false; @@ -60,6 +63,7 @@ private: int fftSize; int zoomLevel; - void refreshSources(); + TracePlot* createIQPlot(SampleSource> *src); + TracePlot* createQuadratureDemodPlot(SampleSource> *src); void updateView(); }; \ No newline at end of file diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index d7c0d3c..41f5585 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -102,6 +102,9 @@ void SpectrogramPlot::mousePressEvent(QMouseEvent *event) void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) { + if (!inputSource || inputSource->count() == 0) + return; + for (int x = rect.left(); x < rect.right(); x += linesPerTile()) { QPixmap *tile = getPixmapTile(sampleRange.minimum + x * getStride()); painter.drawPixmap(QRect(x, rect.y(), linesPerTile(), fftSize), *tile); diff --git a/traceplot.h b/traceplot.h index 4a7097e..bae457d 100644 --- a/traceplot.h +++ b/traceplot.h @@ -32,6 +32,7 @@ public: void paintMid(QPainter &painter, QRect &rect, range_t sampleRange); void paintFront(QPainter &painter, QRect &rect, range_t sampleRange); + std::shared_ptr source() { return sampleSource; }; private: std::shared_ptr sampleSource; From 0694d3fd45161f1cd1814682d53d13c878adec6e Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 23:09:11 +0000 Subject: [PATCH 66/99] samplebuffer: Add some nullptr checks to fix crashyness --- samplebuffer.cpp | 3 +++ traceplot.cpp | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/samplebuffer.cpp b/samplebuffer.cpp index f550474..437eee7 100644 --- a/samplebuffer.cpp +++ b/samplebuffer.cpp @@ -29,6 +29,9 @@ template std::unique_ptr SampleBuffer::getSamples(off_t start, off_t length) { auto samples = src->getSamples(start, length); + if (samples == nullptr) + return nullptr; + std::unique_ptr dest(new Tout[length]); work(samples.get(), dest.get(), length); return dest; diff --git a/traceplot.cpp b/traceplot.cpp index 133c557..83b221c 100644 --- a/traceplot.cpp +++ b/traceplot.cpp @@ -32,6 +32,9 @@ void TracePlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRa // Is it a 2-channel (complex) trace? if (auto src = dynamic_cast>*>(sampleSource.get())) { auto samples = src->getSamples(firstSample, length); + if (samples == nullptr) + return; + painter.setPen(Qt::red); plotTrace(painter, rect, reinterpret_cast(samples.get()), length, 2); painter.setPen(Qt::blue); @@ -40,6 +43,9 @@ void TracePlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRa // Otherwise is it single channel? } else if (auto src = dynamic_cast*>(sampleSource.get())) { auto samples = src->getSamples(firstSample, length); + if (samples == nullptr) + return; + painter.setPen(Qt::green); plotTrace(painter, rect, samples.get(), length, 1); } else { From 1f1e9d5c6129a55a9397ba9abebe80cf8c211c8c Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 23:20:07 +0000 Subject: [PATCH 67/99] mainwindow: Reconnect open file button --- mainwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/mainwindow.cpp b/mainwindow.cpp index f2f8f64..e2955e7 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -36,6 +36,7 @@ MainWindow::MainWindow() plots = new PlotView(input); setCentralWidget(plots); + connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); connect(dock, SIGNAL(fftSizeChanged(int)), plots, SLOT(setFFTSize(int))); connect(dock->zoomLevelSlider, SIGNAL(valueChanged(int)), plots, SLOT(setZoomLevel(int))); connect(dock->cursorsCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableCursors); From d0728351f6c54e29fc8c8aa1e41d3a2356581ff8 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 23:28:35 +0000 Subject: [PATCH 68/99] controls: Set defaults after making connections --- mainwindow.cpp | 3 +++ spectrogramcontrols.cpp | 16 ++++++++++------ spectrogramcontrols.h | 1 + 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index e2955e7..b2474ce 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -40,6 +40,9 @@ MainWindow::MainWindow() connect(dock, SIGNAL(fftSizeChanged(int)), plots, SLOT(setFFTSize(int))); connect(dock->zoomLevelSlider, SIGNAL(valueChanged(int)), plots, SLOT(setZoomLevel(int))); connect(dock->cursorsCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableCursors); + + // Set defaults after making connections so everything is in sync + dock->setDefaults(); } void MainWindow::openFile(QString fileName) diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 75df47e..80935b9 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -42,22 +42,18 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent fftSizeSlider = new QSlider(Qt::Horizontal, widget); fftSizeSlider->setRange(7, 13); - fftSizeSlider->setValue(10); layout->addRow(new QLabel(tr("FFT size:")), fftSizeSlider); zoomLevelSlider = new QSlider(Qt::Horizontal, widget); zoomLevelSlider->setRange(0, 10); - zoomLevelSlider->setValue(0); layout->addRow(new QLabel(tr("Zoom:")), zoomLevelSlider); powerMaxSlider = new QSlider(Qt::Horizontal, widget); powerMaxSlider->setRange(-100, 20); - powerMaxSlider->setValue(0); layout->addRow(new QLabel(tr("Power max:")), powerMaxSlider); powerMinSlider = new QSlider(Qt::Horizontal, widget); powerMinSlider->setRange(-100, 20); - powerMinSlider->setValue(-50); layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); // Pointer position info @@ -75,11 +71,9 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent layout->addRow(new QLabel(tr("Selection"))); cursorsCheckBox = new QCheckBox(widget); - cursorsCheckBox->setCheckState(Qt::Unchecked); layout->addRow(new QLabel(tr("Enable cursors:")), cursorsCheckBox); deltaDragCheckBox = new QCheckBox(widget); - deltaDragCheckBox->setCheckState(Qt::Checked); layout->addRow(new QLabel(tr("Delta dragging:")), deltaDragCheckBox); deltaFrequencyLabel = new QLabel(); @@ -95,6 +89,16 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent connect(fileOpenButton, SIGNAL(clicked()), this, SLOT(fileOpenButtonClicked())); } +void SpectrogramControls::setDefaults() +{ + fftSizeSlider->setValue(9); + zoomLevelSlider->setValue(0); + powerMaxSlider->setValue(0); + powerMinSlider->setValue(-50); + cursorsCheckBox->setCheckState(Qt::Unchecked); + deltaDragCheckBox->setCheckState(Qt::Checked); +} + void SpectrogramControls::fftSizeSliderChanged(int size) { emit fftSizeChanged((int)pow(2, size)); diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index 9b18df4..af1f2a7 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -33,6 +33,7 @@ class SpectrogramControls : public QDockWidget public: SpectrogramControls(const QString & title, QWidget * parent); + void setDefaults(); signals: void fftSizeChanged(int size); From bf2cb2957381908dd299f191d0a88839465f3811 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 23:33:08 +0000 Subject: [PATCH 69/99] spectrogram: Hook up power min/max again --- mainwindow.cpp | 2 ++ plotview.cpp | 16 ++++++++++++++++ plotview.h | 4 ++++ 3 files changed, 22 insertions(+) diff --git a/mainwindow.cpp b/mainwindow.cpp index b2474ce..ef079eb 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -39,6 +39,8 @@ MainWindow::MainWindow() connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); connect(dock, SIGNAL(fftSizeChanged(int)), plots, SLOT(setFFTSize(int))); connect(dock->zoomLevelSlider, SIGNAL(valueChanged(int)), plots, SLOT(setZoomLevel(int))); + 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); // Set defaults after making connections so everything is in sync diff --git a/plotview.cpp b/plotview.cpp index b860241..585cc8b 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -130,6 +130,22 @@ void PlotView::setZoomLevel(int zoom) updateView(); } +void PlotView::setPowerMin(int power) +{ + powerMin = power; + if (spectrogramPlot != nullptr) + spectrogramPlot->setPowerMin(power); + updateView(); +} + +void PlotView::setPowerMax(int power) +{ + powerMax = power; + if (spectrogramPlot != nullptr) + spectrogramPlot->setPowerMax(power); + updateView(); +} + void PlotView::paintEvent(QPaintEvent *event) { if (mainSampleSource == nullptr) return; diff --git a/plotview.h b/plotview.h index 5983b1c..d46814d 100644 --- a/plotview.h +++ b/plotview.h @@ -43,6 +43,8 @@ public slots: void selectionCleared(); void setFFTSize(int size); void setZoomLevel(int zoom); + void setPowerMin(int power); + void setPowerMax(int power); protected: void paintEvent(QPaintEvent *event); @@ -62,6 +64,8 @@ private: int fftSize; int zoomLevel; + int powerMin; + int powerMax; TracePlot* createIQPlot(SampleSource> *src); TracePlot* createQuadratureDemodPlot(SampleSource> *src); From d989579d62e6b1b37996383f4e36c6a9677c6c4e Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Thu, 3 Mar 2016 23:54:59 +0000 Subject: [PATCH 70/99] spectrogram: Fix caching/tileIDs --- spectrogramplot.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index 41f5585..9c9031b 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -105,9 +105,15 @@ void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sa if (!inputSource || inputSource->count() == 0) return; + off_t sampleOffset = sampleRange.minimum % getStride(); + off_t tileID = sampleRange.minimum - sampleOffset; + int xoffset = sampleOffset / fftSize; for (int x = rect.left(); x < rect.right(); x += linesPerTile()) { - QPixmap *tile = getPixmapTile(sampleRange.minimum + x * getStride()); - painter.drawPixmap(QRect(x, rect.y(), linesPerTile(), fftSize), *tile); + QPixmap *tile = getPixmapTile(tileID); + // TODO: don't draw past rect.right() + painter.drawPixmap(QRect(x, rect.y(), linesPerTile() - xoffset, fftSize), *tile, QRect(xoffset, 0, linesPerTile() - xoffset, fftSize)); + xoffset = 0; + tileID += getStride() * linesPerTile(); } } From a1af3fe0becf9a487847872f24a77a95fe1555c7 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Fri, 4 Mar 2016 00:13:48 +0000 Subject: [PATCH 71/99] plot: Add default impl of all paint* functions --- plot.cpp | 12 +++++++++++- plot.h | 4 ++-- spectrogramplot.cpp | 5 ----- spectrogramplot.h | 1 - traceplot.cpp | 5 ----- traceplot.h | 1 - 6 files changed, 13 insertions(+), 15 deletions(-) diff --git a/plot.cpp b/plot.cpp index fc58d8e..84de2b9 100644 --- a/plot.cpp +++ b/plot.cpp @@ -26,4 +26,14 @@ void Plot::paintBack(QPainter &painter, QRect &rect, range_t sampleRange) painter.setPen(pen); painter.drawLine(rect.left(), rect.center().y(), rect.right(), rect.center().y()); painter.restore(); -} \ No newline at end of file +} + +void Plot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) +{ + +} + +void Plot::paintFront(QPainter &painter, QRect &rect, range_t sampleRange) +{ + +} diff --git a/plot.h b/plot.h index cb66e78..14f6cc4 100644 --- a/plot.h +++ b/plot.h @@ -28,8 +28,8 @@ class Plot : public QObject public: virtual void paintBack(QPainter &painter, QRect &rect, range_t sampleRange); - virtual void paintMid(QPainter &painter, QRect &rect, range_t sampleRange) = 0; - virtual void paintFront(QPainter &painter, QRect &rect, range_t sampleRange) = 0; + virtual void paintMid(QPainter &painter, QRect &rect, range_t sampleRange); + virtual void paintFront(QPainter &painter, QRect &rect, range_t sampleRange); int height() const { return _height; }; protected: diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index 9c9031b..95c2955 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -117,11 +117,6 @@ void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sa } } -void SpectrogramPlot::paintFront(QPainter &painter, QRect &rect, range_t sampleRange) -{ - -} - QPixmap* SpectrogramPlot::getPixmapTile(off_t tile) { QPixmap *obj = pixmapCache.object(TileCacheKey(fftSize, zoomLevel, tile)); diff --git a/spectrogramplot.h b/spectrogramplot.h index 71feda8..bfafd91 100644 --- a/spectrogramplot.h +++ b/spectrogramplot.h @@ -41,7 +41,6 @@ public: ~SpectrogramPlot(); void paintMid(QPainter &painter, QRect &rect, range_t sampleRange); - void paintFront(QPainter &painter, QRect &rect, range_t sampleRange); QSize sizeHint() const; int getHeight(); diff --git a/traceplot.cpp b/traceplot.cpp index 83b221c..c056465 100644 --- a/traceplot.cpp +++ b/traceplot.cpp @@ -72,8 +72,3 @@ void TracePlot::plotTrace(QPainter &painter, QRect &rect, float *samples, off_t yprev = y; } } - -void TracePlot::paintFront(QPainter &painter, QRect &rect, range_t sampleRange) -{ - -} diff --git a/traceplot.h b/traceplot.h index bae457d..3c56d6b 100644 --- a/traceplot.h +++ b/traceplot.h @@ -31,7 +31,6 @@ public: TracePlot(std::shared_ptr source); void paintMid(QPainter &painter, QRect &rect, range_t sampleRange); - void paintFront(QPainter &painter, QRect &rect, range_t sampleRange); std::shared_ptr source() { return sampleSource; }; private: From d78986a978e248e8576d3b1d98e6a5e25dd9fded Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Fri, 4 Mar 2016 00:14:57 +0000 Subject: [PATCH 72/99] spectrogram: Fix inverted spectrogram --- spectrogramplot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index 95c2955..09d90b6 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -133,7 +133,7 @@ QPixmap* SpectrogramPlot::getPixmapTile(off_t tile) float normPower = (line[y] - powerMax) * -1.0f / powerRange; normPower = clamp(normPower, 0.0f, 1.0f); - image.setPixel(x, y, colormap[(uint8_t)(normPower * (256 - 1))]); + image.setPixel(x, fftSize - y - 1, colormap[(uint8_t)(normPower * (256 - 1))]); } } obj->convertFromImage(image); From 12de91d0397c1db9b221ae1aba4eb2a56698820d Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Fri, 4 Mar 2016 15:53:58 +0000 Subject: [PATCH 73/99] samplebuffer: Unsubscribe from src on destruct --- samplebuffer.cpp | 6 ++++++ samplebuffer.h | 1 + 2 files changed, 7 insertions(+) diff --git a/samplebuffer.cpp b/samplebuffer.cpp index 437eee7..6965ff7 100644 --- a/samplebuffer.cpp +++ b/samplebuffer.cpp @@ -25,6 +25,12 @@ SampleBuffer::SampleBuffer(SampleSource *src) : src(src) src->subscribe(this); } +template +SampleBuffer::~SampleBuffer() +{ + src->unsubscribe(this); +} + template std::unique_ptr SampleBuffer::getSamples(off_t start, off_t length) { diff --git a/samplebuffer.h b/samplebuffer.h index c9f0fae..4574931 100644 --- a/samplebuffer.h +++ b/samplebuffer.h @@ -31,6 +31,7 @@ private: public: SampleBuffer(SampleSource *src); + ~SampleBuffer(); void invalidateEvent(); virtual std::unique_ptr getSamples(off_t start, off_t length); virtual void work(void *input, void *output, int count) = 0; From fffefbe4bec4b2a87c1baf49345ee0247254f78d Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Fri, 4 Mar 2016 17:03:25 +0000 Subject: [PATCH 74/99] samplesource: Add sample rate property --- inputsource.cpp | 11 +++++++++++ inputsource.h | 3 +++ mainwindow.cpp | 6 ++++++ mainwindow.h | 1 + samplebuffer.h | 3 +++ samplesource.h | 1 + spectrogramcontrols.cpp | 3 ++- 7 files changed, 27 insertions(+), 1 deletion(-) diff --git a/inputsource.cpp b/inputsource.cpp index de1bbc5..664f8cc 100644 --- a/inputsource.cpp +++ b/inputsource.cpp @@ -75,6 +75,17 @@ void InputSource::openFile(const char *filename) invalidate(); } +void InputSource::setSampleRate(off_t rate) +{ + sampleRate = rate; + invalidate(); +} + +off_t InputSource::rate() +{ + return sampleRate; +} + std::unique_ptr[]> InputSource::getSamples(off_t start, off_t length) { if (inputFile == nullptr) diff --git a/inputsource.h b/inputsource.h index 75feeac..e991cef 100644 --- a/inputsource.h +++ b/inputsource.h @@ -28,6 +28,7 @@ private: FILE *inputFile = nullptr; off_t fileSize = 0; off_t sampleCount = 0; + off_t sampleRate = 0; std::complex *mmapData = nullptr; public: @@ -39,4 +40,6 @@ public: off_t count() { return sampleCount; }; + void setSampleRate(off_t rate); + off_t rate(); }; diff --git a/mainwindow.cpp b/mainwindow.cpp index ef079eb..d66d449 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -37,6 +37,7 @@ MainWindow::MainWindow() setCentralWidget(plots); connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); + connect(dock->sampleRate, SIGNAL(textChanged(QString)), this, SLOT(setSampleRate(QString))); connect(dock, SIGNAL(fftSizeChanged(int)), plots, SLOT(setFFTSize(int))); connect(dock->zoomLevelSlider, SIGNAL(valueChanged(int)), plots, SLOT(setZoomLevel(int))); connect(dock->powerMaxSlider, SIGNAL(valueChanged(int)), plots, SLOT(setPowerMax(int))); @@ -53,3 +54,8 @@ void MainWindow::openFile(QString fileName) this->setWindowTitle(title.arg(QApplication::applicationName(),fileName.section('/',-1,-1))); input->openFile(fileName.toUtf8().constData()); } + +void MainWindow::setSampleRate(QString rate) +{ + input->setSampleRate(rate.toInt()); +} diff --git a/mainwindow.h b/mainwindow.h index f29bd99..2a3429c 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -34,6 +34,7 @@ public: public slots: void openFile(QString fileName); + void setSampleRate(QString rate); private: SpectrogramControls *dock; diff --git a/samplebuffer.h b/samplebuffer.h index 4574931..b7a5140 100644 --- a/samplebuffer.h +++ b/samplebuffer.h @@ -38,4 +38,7 @@ public: virtual off_t count() { return src->count(); }; + off_t rate() { + return src->rate(); + }; }; diff --git a/samplesource.h b/samplesource.h index 7035218..e69ab8c 100644 --- a/samplesource.h +++ b/samplesource.h @@ -35,6 +35,7 @@ public: virtual std::unique_ptr getSamples(off_t start, off_t length) = 0; virtual void invalidateEvent() { }; virtual off_t count() = 0; + virtual off_t rate() = 0; void subscribe(Subscriber *subscriber); void unsubscribe(Subscriber *subscriber); diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 80935b9..17fbee9 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -32,7 +32,7 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent fileOpenButton = new QPushButton("Open file...", widget); layout->addRow(fileOpenButton); - sampleRate = new QLineEdit("8000000"); + sampleRate = new QLineEdit(); sampleRate->setValidator(new QIntValidator(this)); layout->addRow(new QLabel(tr("Sample rate:")), sampleRate); @@ -91,6 +91,7 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent void SpectrogramControls::setDefaults() { + sampleRate->setText("8000000"); fftSizeSlider->setValue(9); zoomLevelSlider->setValue(0); powerMaxSlider->setValue(0); From c66c7081578dda67851aea66d1051622e436c045 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Fri, 4 Mar 2016 17:03:42 +0000 Subject: [PATCH 75/99] Style --- samplebuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samplebuffer.h b/samplebuffer.h index b7a5140..cfbac64 100644 --- a/samplebuffer.h +++ b/samplebuffer.h @@ -32,7 +32,7 @@ private: public: SampleBuffer(SampleSource *src); ~SampleBuffer(); - void invalidateEvent(); + void invalidateEvent(); virtual std::unique_ptr getSamples(off_t start, off_t length); virtual void work(void *input, void *output, int count) = 0; virtual off_t count() { From a82a1af33dd726a39d64f8bb53472a67bf501af8 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sat, 5 Mar 2016 15:57:11 +0000 Subject: [PATCH 76/99] cursors: Reimplement Cursors as QObject and implement dragging --- cursors.cpp | 62 +++++++++++++++++++++++++++++++++++++++------ cursors.h | 22 +++++++++++++--- plotview.cpp | 17 +++++++++---- plotview.h | 2 ++ spectrogramplot.cpp | 36 -------------------------- 5 files changed, 86 insertions(+), 53 deletions(-) diff --git a/cursors.cpp b/cursors.cpp index 5a9e38a..6995ddb 100644 --- a/cursors.cpp +++ b/cursors.cpp @@ -17,29 +17,75 @@ * along with this program. If not, see . */ -#include +#include +#include #include "cursors.h" -Cursors::Cursors(QWidget * parent) - : QWidget::QWidget(parent) +Cursors::Cursors(QObject * parent) : QObject::QObject(parent) { } -void Cursors::paintEvent(QPaintEvent *event) +// Return true if point is over a cursor, put cursor ID in `cursor` +bool Cursors::pointOverCursor(QPoint point, int &cursor) { - QPainter painter(this); + int margin = 5; + for (int i = 0; i < 2; i++) { + range_t range = {cursorPositions[i] - margin, cursorPositions[i] + margin}; + if (range.contains(point.x())) { + cursor = i; + return true; + } + } + return false; +} +bool Cursors::eventFilter(QObject *obj, QEvent *event) +{ + // Start dragging on left mouse button press, if over a cursor + if (event->type() == QEvent::MouseButtonPress) { + QMouseEvent *mouseEvent = static_cast(event); + if (mouseEvent->button() == Qt::LeftButton) { + if (pointOverCursor(mouseEvent->pos(), selectedCursor)) { + dragging = true; + return true; + } + } + + // Update current cursor positon if we're dragging + } else if (event->type() == QEvent::MouseMove) { + QMouseEvent *mouseEvent = static_cast(event); + if (dragging) { + cursorPositions[selectedCursor] = mouseEvent->pos().x(); + emit cursorsMoved(); + } + + // Stop dragging on left mouse button release + } else if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = static_cast(event); + if (mouseEvent->button() == Qt::LeftButton) { + dragging = false; + return true; + } + } + return false; +} + +void Cursors::paintFront(QPainter &painter, QRect &rect, range_t sampleRange) +{ painter.save(); // Draw translucent white fill for highlight - painter.fillRect(rect(), QBrush(QColor(255, 255, 255, 50))); + painter.fillRect( + QRect(cursorPositions[0], rect.top(), cursorPositions[1] - cursorPositions[0], rect.bottom()), + QBrush(QColor(255, 255, 255, 50)) + ); // Draw vertical edges QPen pen(Qt::white, 1, Qt::DashLine); painter.setPen(pen); - painter.drawLine(rect().left(), rect().top(), rect().left(), rect().bottom()); - painter.drawLine(rect().right(), rect().top(), rect().right(), rect().bottom()); + painter.drawLine(cursorPositions[0], rect.top(), cursorPositions[0], rect.bottom()); + painter.drawLine(cursorPositions[1], rect.top(), cursorPositions[1], rect.bottom()); painter.restore(); } diff --git a/cursors.h b/cursors.h index 6d0ef6c..ef02964 100644 --- a/cursors.h +++ b/cursors.h @@ -19,16 +19,30 @@ #pragma once -#include +#include +#include +#include +#include "util.h" -class Cursors : public QWidget +class Cursors : public QObject { Q_OBJECT public: - Cursors(QWidget * parent); + Cursors(QObject * parent); + void paintFront(QPainter &painter, QRect &rect, range_t sampleRange); + +signals: + void cursorsMoved(); protected: - void paintEvent(QPaintEvent *event); + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + bool pointOverCursor(QPoint point, int &cursor); + + bool dragging = false; + int selectedCursor = 0; + int cursorPositions[2] = {0, 50}; }; diff --git a/plotview.cpp b/plotview.cpp index 585cc8b..683ecdd 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -35,6 +35,8 @@ PlotView::PlotView(InputSource *input) : cursors(this), viewRange({0, 0}) mainSampleSource = input; setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); enableCursors(false); + viewport()->installEventFilter(&cursors); + connect(&cursors, SIGNAL(cursorsMoved()), this, SLOT(cursorsMoved())); spectrogramPlot = new SpectrogramPlot(mainSampleSource); plots.emplace_back(spectrogramPlot); @@ -86,12 +88,15 @@ TracePlot* PlotView::createQuadratureDemodPlot(SampleSource> ); } +void PlotView::cursorsMoved() +{ + viewport()->update(); +} + void PlotView::enableCursors(bool enabled) { - if (enabled) - cursors.show(); - else - cursors.hide(); + cursorsEnabled = enabled; + viewport()->update(); } void PlotView::invalidateEvent() @@ -168,6 +173,8 @@ void PlotView::paintEvent(QPaintEvent *event) PLOT_LAYER(paintBack); PLOT_LAYER(paintMid); PLOT_LAYER(paintFront); + if (cursorsEnabled) + cursors.paintFront(painter, rect, {viewRange.first, viewRange.second}); #undef PLOT_LAYER } @@ -179,7 +186,7 @@ void PlotView::resizeEvent(QResizeEvent * event) // Resize cursors // TODO: don't hardcode this int margin = rect.width() / 3; - cursors.setGeometry(QRect(rect.left() + margin, rect.top(), rect.right() - rect.left() - 2 * margin, rect.height())); + //cursors.setGeometry(QRect(rect.left() + margin, rect.top(), rect.right() - rect.left() - 2 * margin, rect.height())); updateView(); } diff --git a/plotview.h b/plotview.h index d46814d..55a46a7 100644 --- a/plotview.h +++ b/plotview.h @@ -37,6 +37,7 @@ public: PlotView(InputSource *input); public slots: + void cursorsMoved(); void enableCursors(bool enable); void invalidateEvent(); void selectionChanged(std::pair selectionTime, std::pair selectionFreq); @@ -66,6 +67,7 @@ private: int zoomLevel; int powerMin; int powerMax; + bool cursorsEnabled; TracePlot* createIQPlot(SampleSource> *src); TracePlot* createQuadratureDemodPlot(SampleSource> *src); diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index 09d90b6..f2fc330 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -64,42 +64,6 @@ void SpectrogramPlot::xyToFreqTime(int x, int y, float *freq, float *time) *time = (float)lineToSample(y) / sampleRate; } -void SpectrogramPlot::mouseReleaseEvent(QMouseEvent *event) -{ - if (deltaDragIsEnabled) { - cursorStartX = -1; - update(); - } -} - -void SpectrogramPlot::mouseMoveEvent(QMouseEvent *event) -{ - float freq, time; - xyToFreqTime(event->x(), event->y(), &freq, &time); - emit cursorFrequencyChanged(QString::number(freq) + " Hz"); - emit cursorTimeChanged(QString::number(time) + " s"); - if (cursorStartX != -1) { - float s_freq, s_time; - xyToFreqTime(cursorStartX, cursorStartY, &s_freq, &s_time); - emit deltaFrequencyChanged(QString::number(fabs(s_freq - freq)) + " Hz"); - emit deltaTimeChanged(QString::number(fabs(s_time - time)) + " s"); - cursorEndX = event->x(); - cursorEndY = event->y(); - update(); - } -} - -void SpectrogramPlot::mousePressEvent(QMouseEvent *event) -{ - if (cursorStartX == -1) { - cursorEndX = cursorStartX = event->x(); - cursorEndY = cursorStartY = event->y(); - } else { - cursorStartX = -1; - } - update(); -} - void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) { if (!inputSource || inputSource->count() == 0) From eaf422165f6baa53b8f21ffbbc1503f32584b9ab Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sat, 5 Mar 2016 16:16:55 +0000 Subject: [PATCH 77/99] plotview: Abstract out coord -> sample calculation --- plotview.cpp | 7 ++++++- plotview.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/plotview.cpp b/plotview.cpp index 683ecdd..7f143cf 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -48,6 +48,11 @@ PlotView::PlotView(InputSource *input) : cursors(this), viewRange({0, 0}) mainSampleSource->subscribe(this); } +off_t PlotView::coordToSample(int x) +{ + return fftSize * x / (int)pow(2, zoomLevel); +} + TracePlot* PlotView::createIQPlot(SampleSource> *src) { gr::top_block_sptr iq_tb = gr::make_top_block("multiply"); @@ -200,7 +205,7 @@ void PlotView::updateView() { viewRange = { horizontalScrollBar()->value(), - horizontalScrollBar()->value() + fftSize * width() / (int)pow(2, zoomLevel) + horizontalScrollBar()->value() + coordToSample(width()) }; viewport()->update(); } diff --git a/plotview.h b/plotview.h index 55a46a7..22d31b6 100644 --- a/plotview.h +++ b/plotview.h @@ -69,6 +69,7 @@ private: int powerMax; bool cursorsEnabled; + off_t coordToSample(int x); TracePlot* createIQPlot(SampleSource> *src); TracePlot* createQuadratureDemodPlot(SampleSource> *src); void updateView(); From bc2b1dc9001769d08ae09eb43a7ccff99a2e8889 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sat, 5 Mar 2016 17:10:40 +0000 Subject: [PATCH 78/99] cursors: Connect cursors to time/freq display on dock --- cursors.cpp | 11 +++++++++++ cursors.h | 1 + mainwindow.cpp | 4 ++++ plotview.cpp | 4 ++++ plotview.h | 3 +++ spectrogramcontrols.cpp | 6 ++++++ spectrogramcontrols.h | 3 +++ 7 files changed, 32 insertions(+) diff --git a/cursors.cpp b/cursors.cpp index 6995ddb..909d934 100644 --- a/cursors.cpp +++ b/cursors.cpp @@ -89,3 +89,14 @@ void Cursors::paintFront(QPainter &painter, QRect &rect, range_t sampleRa painter.restore(); } + +range_t Cursors::selection() +{ + // TODO: ensure correct ordering during dragging, not here + if (cursorPositions[0] < cursorPositions[1]) { + return {cursorPositions[0], cursorPositions[1]}; + + } else { + return {cursorPositions[1], cursorPositions[0]}; + } +} diff --git a/cursors.h b/cursors.h index ef02964..96d3b4c 100644 --- a/cursors.h +++ b/cursors.h @@ -31,6 +31,7 @@ class Cursors : public QObject public: Cursors(QObject * parent); void paintFront(QPainter &painter, QRect &rect, range_t sampleRange); + range_t selection(); signals: void cursorsMoved(); diff --git a/mainwindow.cpp b/mainwindow.cpp index d66d449..cdd26b0 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -36,6 +36,7 @@ MainWindow::MainWindow() plots = new PlotView(input); setCentralWidget(plots); + // Connect dock inputs connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); connect(dock->sampleRate, SIGNAL(textChanged(QString)), this, SLOT(setSampleRate(QString))); connect(dock, SIGNAL(fftSizeChanged(int)), plots, SLOT(setFFTSize(int))); @@ -44,6 +45,9 @@ MainWindow::MainWindow() connect(dock->powerMinSlider, SIGNAL(valueChanged(int)), plots, SLOT(setPowerMin(int))); connect(dock->cursorsCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableCursors); + // Connect dock outputs + connect(plots, SIGNAL(timeSelectionChanged(float)), dock, SLOT(timeSelectionChanged(float))); + // Set defaults after making connections so everything is in sync dock->setDefaults(); } diff --git a/plotview.cpp b/plotview.cpp index 7f143cf..636cf5b 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -95,6 +95,10 @@ TracePlot* PlotView::createQuadratureDemodPlot(SampleSource> void PlotView::cursorsMoved() { + int selection = cursors.selection().length(); + off_t sampleCount = coordToSample(selection); + float selectionTime = sampleCount / (float)mainSampleSource->rate(); + emit timeSelectionChanged(selectionTime); viewport()->update(); } diff --git a/plotview.h b/plotview.h index 22d31b6..7a7da49 100644 --- a/plotview.h +++ b/plotview.h @@ -36,6 +36,9 @@ class PlotView : public QAbstractScrollArea, Subscriber public: PlotView(InputSource *input); +signals: + void timeSelectionChanged(float time); + public slots: void cursorsMoved(); void enableCursors(bool enable); diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 17fbee9..415393b 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -112,3 +112,9 @@ void SpectrogramControls::fileOpenButtonClicked() ); emit openFile(fileName); } + +void SpectrogramControls::timeSelectionChanged(float time) +{ + deltaTimeLabel->setText(QString::number(time) + "s"); + deltaFrequencyLabel->setText(QString::number(1 / time) + "Hz"); +} diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index af1f2a7..20a7345 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -39,6 +39,9 @@ signals: void fftSizeChanged(int size); void openFile(QString fileName); +public slots: + void timeSelectionChanged(float time); + private slots: void fftSizeSliderChanged(int size); void fileOpenButtonClicked(); From 473438b5d54a363ecd0033dabedfb4bcf6e96d4f Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sat, 5 Mar 2016 17:22:00 +0000 Subject: [PATCH 79/99] controls: Clean up selection labels a bit --- spectrogramcontrols.cpp | 20 ++++++++------------ spectrogramcontrols.h | 5 ++--- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 415393b..a6d235a 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -66,21 +66,18 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent pointerTimeLabel = new QLabel(); layout->addRow(new QLabel(tr("Pointer time:")), pointerTimeLabel); - // Selection settings + // Time selection settings layout->addRow(new QLabel()); // TODO: find a better way to add an empty row? - layout->addRow(new QLabel(tr("Selection"))); + layout->addRow(new QLabel(tr("Time selection"))); cursorsCheckBox = new QCheckBox(widget); layout->addRow(new QLabel(tr("Enable cursors:")), cursorsCheckBox); - deltaDragCheckBox = new QCheckBox(widget); - layout->addRow(new QLabel(tr("Delta dragging:")), deltaDragCheckBox); + timeSelectionFreqLabel = new QLabel(); + layout->addRow(new QLabel(tr("Frequency:")), timeSelectionFreqLabel); - deltaFrequencyLabel = new QLabel(); - layout->addRow(new QLabel(tr("Delta frequency:")), deltaFrequencyLabel); - - deltaTimeLabel = new QLabel(); - layout->addRow(new QLabel(tr("Delta time:")), deltaTimeLabel); + timeSelectionTimeLabel = new QLabel(); + layout->addRow(new QLabel(tr("Time:")), timeSelectionTimeLabel); widget->setLayout(layout); setWidget(widget); @@ -97,7 +94,6 @@ void SpectrogramControls::setDefaults() powerMaxSlider->setValue(0); powerMinSlider->setValue(-50); cursorsCheckBox->setCheckState(Qt::Unchecked); - deltaDragCheckBox->setCheckState(Qt::Checked); } void SpectrogramControls::fftSizeSliderChanged(int size) @@ -115,6 +111,6 @@ void SpectrogramControls::fileOpenButtonClicked() void SpectrogramControls::timeSelectionChanged(float time) { - deltaTimeLabel->setText(QString::number(time) + "s"); - deltaFrequencyLabel->setText(QString::number(1 / time) + "Hz"); + timeSelectionTimeLabel->setText(QString::number(time) + "s"); + timeSelectionFreqLabel->setText(QString::number(1 / time) + "Hz"); } diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index 20a7345..0399972 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -59,7 +59,6 @@ public: QCheckBox *cursorsCheckBox; QLabel *pointerFrequencyLabel; QLabel *pointerTimeLabel; - QCheckBox *deltaDragCheckBox; - QLabel *deltaFrequencyLabel; - QLabel *deltaTimeLabel; + QLabel *timeSelectionFreqLabel; + QLabel *timeSelectionTimeLabel; }; From f8e30fa12876f5b9ac812169d8449af8c298cbe9 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sat, 5 Mar 2016 18:30:10 +0000 Subject: [PATCH 80/99] plotview: Remove old selection slots --- plotview.cpp | 12 ------------ plotview.h | 4 +--- util.h | 4 ++-- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/plotview.cpp b/plotview.cpp index 636cf5b..ca14828 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -114,18 +114,6 @@ void PlotView::invalidateEvent() horizontalScrollBar()->setMaximum(mainSampleSource->count()); } -void PlotView::selectionChanged(std::pair selectionTime, std::pair selectionFreq) -{ - this->selectionTime = selectionTime; - this->selectionFreq = selectionFreq; - selection = true; -} - -void PlotView::selectionCleared() -{ - selection = false; -} - void PlotView::setFFTSize(int size) { fftSize = size; diff --git a/plotview.h b/plotview.h index 7a7da49..1117ad2 100644 --- a/plotview.h +++ b/plotview.h @@ -43,8 +43,6 @@ public slots: void cursorsMoved(); void enableCursors(bool enable); void invalidateEvent(); - void selectionChanged(std::pair selectionTime, std::pair selectionFreq); - void selectionCleared(); void setFFTSize(int size); void setZoomLevel(int zoom); void setPowerMin(int power); @@ -63,7 +61,7 @@ private: std::vector> plots; std::pair viewRange; bool selection = false; - std::pair selectionTime; + range_t selectionTime; std::pair selectionFreq; int fftSize; diff --git a/util.h b/util.h index 94ee8fa..5b7a3a8 100644 --- a/util.h +++ b/util.h @@ -28,8 +28,8 @@ template const T& clamp (const T& value, const T& min, const T& max) template struct range_t { - const T minimum; - const T maximum; + T minimum; + T maximum; const T length() { return maximum - minimum; From 158e95c54a5f5570a457e30e3f4d1215da4db70a Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sat, 5 Mar 2016 19:15:23 +0000 Subject: [PATCH 81/99] plotview: coordToSample -> samplesPerLine --- plotview.cpp | 14 +++++++------- plotview.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plotview.cpp b/plotview.cpp index ca14828..bc4d088 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -48,11 +48,6 @@ PlotView::PlotView(InputSource *input) : cursors(this), viewRange({0, 0}) mainSampleSource->subscribe(this); } -off_t PlotView::coordToSample(int x) -{ - return fftSize * x / (int)pow(2, zoomLevel); -} - TracePlot* PlotView::createIQPlot(SampleSource> *src) { gr::top_block_sptr iq_tb = gr::make_top_block("multiply"); @@ -96,7 +91,7 @@ TracePlot* PlotView::createQuadratureDemodPlot(SampleSource> void PlotView::cursorsMoved() { int selection = cursors.selection().length(); - off_t sampleCount = coordToSample(selection); + off_t sampleCount = selection * samplesPerLine(); float selectionTime = sampleCount / (float)mainSampleSource->rate(); emit timeSelectionChanged(selectionTime); viewport()->update(); @@ -188,6 +183,11 @@ void PlotView::resizeEvent(QResizeEvent * event) updateView(); } +off_t PlotView::samplesPerLine() +{ + return fftSize / (int)pow(2, zoomLevel); +} + void PlotView::scrollContentsBy(int dx, int dy) { updateView(); @@ -197,7 +197,7 @@ void PlotView::updateView() { viewRange = { horizontalScrollBar()->value(), - horizontalScrollBar()->value() + coordToSample(width()) + horizontalScrollBar()->value() + width() * samplesPerLine() }; viewport()->update(); } diff --git a/plotview.h b/plotview.h index 1117ad2..aa1cf61 100644 --- a/plotview.h +++ b/plotview.h @@ -70,8 +70,8 @@ private: int powerMax; bool cursorsEnabled; - off_t coordToSample(int x); TracePlot* createIQPlot(SampleSource> *src); TracePlot* createQuadratureDemodPlot(SampleSource> *src); + off_t samplesPerLine(); void updateView(); }; \ No newline at end of file From c6166d70f9ae0d36f21ca57708fe64b521588301 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sat, 5 Mar 2016 19:15:55 +0000 Subject: [PATCH 82/99] plotview: Store time selection --- plotview.cpp | 8 ++++++-- plotview.h | 2 +- util.h | 13 +++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/plotview.cpp b/plotview.cpp index bc4d088..327342c 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -90,8 +90,12 @@ TracePlot* PlotView::createQuadratureDemodPlot(SampleSource> void PlotView::cursorsMoved() { - int selection = cursors.selection().length(); - off_t sampleCount = selection * samplesPerLine(); + selectedSamples = { + horizontalScrollBar()->value() + cursors.selection().minimum * samplesPerLine(), + horizontalScrollBar()->value() + cursors.selection().maximum * samplesPerLine() + }; + + off_t sampleCount = selectedSamples.length(); float selectionTime = sampleCount / (float)mainSampleSource->rate(); emit timeSelectionChanged(selectionTime); viewport()->update(); diff --git a/plotview.h b/plotview.h index aa1cf61..8a48be3 100644 --- a/plotview.h +++ b/plotview.h @@ -61,7 +61,7 @@ private: std::vector> plots; std::pair viewRange; bool selection = false; - range_t selectionTime; + range_t selectedSamples; std::pair selectionFreq; int fftSize; diff --git a/util.h b/util.h index 5b7a3a8..8f1129b 100644 --- a/util.h +++ b/util.h @@ -31,6 +31,19 @@ struct range_t { T minimum; T maximum; + range_t& operator=(const range_t &other) { + minimum = other.minimum; + maximum = other.maximum; + } + + range_t& operator=(const std::initializer_list &other) { + if (other.size() == 2) { + minimum = *other.begin(); + maximum = *(other.begin() + 1); + } + return *this; + } + const T length() { return maximum - minimum; } From e288f93b2968e8063307df5346d679ea450ee384 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 5 Mar 2016 20:30:25 +0000 Subject: [PATCH 83/99] cursors: Re-position cursors on view change --- cursors.cpp | 7 +++++++ cursors.h | 1 + plotview.cpp | 22 +++++++++++++++------- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/cursors.cpp b/cursors.cpp index 909d934..38446de 100644 --- a/cursors.cpp +++ b/cursors.cpp @@ -100,3 +100,10 @@ range_t Cursors::selection() return {cursorPositions[1], cursorPositions[0]}; } } + +void Cursors::setSelection(range_t selection) +{ + cursorPositions[0] = selection.minimum; + cursorPositions[1] = selection.maximum; + emit cursorsMoved(); +} diff --git a/cursors.h b/cursors.h index 96d3b4c..13a7ceb 100644 --- a/cursors.h +++ b/cursors.h @@ -32,6 +32,7 @@ public: Cursors(QObject * parent); void paintFront(QPainter &painter, QRect &rect, range_t sampleRange); range_t selection(); + void setSelection(range_t selection); signals: void cursorsMoved(); diff --git a/plotview.cpp b/plotview.cpp index 327342c..28ab01c 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -104,6 +104,10 @@ void PlotView::cursorsMoved() void PlotView::enableCursors(bool enabled) { cursorsEnabled = enabled; + if (enabled) { + int margin = viewport()->rect().width() / 3; + cursors.setSelection({viewport()->rect().left() + margin, viewport()->rect().right() - margin}); + } viewport()->update(); } @@ -177,13 +181,6 @@ void PlotView::paintEvent(QPaintEvent *event) void PlotView::resizeEvent(QResizeEvent * event) { - QRect rect = viewport()->rect(); - - // Resize cursors - // TODO: don't hardcode this - int margin = rect.width() / 3; - //cursors.setGeometry(QRect(rect.left() + margin, rect.top(), rect.right() - rect.left() - 2 * margin, rect.height())); - updateView(); } @@ -199,9 +196,20 @@ void PlotView::scrollContentsBy(int dx, int dy) void PlotView::updateView() { + // Update current view viewRange = { horizontalScrollBar()->value(), horizontalScrollBar()->value() + width() * samplesPerLine() }; + + // Update cursors + QRect rect = viewport()->rect(); + range_t newSelection = { + (int)((selectedSamples.minimum - horizontalScrollBar()->value()) / samplesPerLine()), + (int)((selectedSamples.maximum - horizontalScrollBar()->value()) / samplesPerLine()) + }; + cursors.setSelection(newSelection); + + // Re-paint viewport()->update(); } From f2b7f918d22eeed449c4aa140e627e2b97e222f9 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 6 Mar 2016 00:48:45 +0000 Subject: [PATCH 84/99] cursors: Split cursors into n segments to align with multiple bits --- cursors.cpp | 19 ++++++++++++++++--- cursors.h | 2 ++ mainwindow.cpp | 1 + plotview.cpp | 7 +++++++ plotview.h | 1 + spectrogramcontrols.cpp | 14 ++++++++++++++ spectrogramcontrols.h | 4 ++++ 7 files changed, 45 insertions(+), 3 deletions(-) diff --git a/cursors.cpp b/cursors.cpp index 38446de..9af4ad8 100644 --- a/cursors.cpp +++ b/cursors.cpp @@ -75,15 +75,22 @@ void Cursors::paintFront(QPainter &painter, QRect &rect, range_t sampleRa { painter.save(); + QRect cursorRect(cursorPositions[0], rect.top(), cursorPositions[1] - cursorPositions[0], rect.height()); // Draw translucent white fill for highlight painter.fillRect( - QRect(cursorPositions[0], rect.top(), cursorPositions[1] - cursorPositions[0], rect.bottom()), + cursorRect, QBrush(QColor(255, 255, 255, 50)) ); + // Draw vertical edges for individual bits + painter.setPen(QPen(Qt::gray, 1, Qt::DashLine)); + for (int i = 1; i < bitCount; i++) { + int pos = cursorPositions[0] + (i * cursorRect.width() / bitCount); + painter.drawLine(pos, rect.top(), pos, rect.bottom()); + } + // Draw vertical edges - QPen pen(Qt::white, 1, Qt::DashLine); - painter.setPen(pen); + painter.setPen(QPen(Qt::white, 1, Qt::SolidLine)); painter.drawLine(cursorPositions[0], rect.top(), cursorPositions[0], rect.bottom()); painter.drawLine(cursorPositions[1], rect.top(), cursorPositions[1], rect.bottom()); @@ -101,6 +108,12 @@ range_t Cursors::selection() } } +void Cursors::setBits(int bits) +{ + bitCount = std::max(bits, 1); + +} + void Cursors::setSelection(range_t selection) { cursorPositions[0] = selection.minimum; diff --git a/cursors.h b/cursors.h index 13a7ceb..9c6cedc 100644 --- a/cursors.h +++ b/cursors.h @@ -32,6 +32,7 @@ public: Cursors(QObject * parent); void paintFront(QPainter &painter, QRect &rect, range_t sampleRange); range_t selection(); + void setBits(int bits); void setSelection(range_t selection); signals: @@ -43,6 +44,7 @@ protected: private: bool pointOverCursor(QPoint point, int &cursor); + int bitCount = 1; bool dragging = false; int selectedCursor = 0; int cursorPositions[2] = {0, 50}; diff --git a/mainwindow.cpp b/mainwindow.cpp index cdd26b0..1cda6ca 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -44,6 +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->cursorBitsSpinBox, static_cast(&QSpinBox::valueChanged), plots, &PlotView::setCursorBits); // Connect dock outputs connect(plots, SIGNAL(timeSelectionChanged(float)), dock, SLOT(timeSelectionChanged(float))); diff --git a/plotview.cpp b/plotview.cpp index 28ab01c..d436311 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -117,6 +117,12 @@ void PlotView::invalidateEvent() horizontalScrollBar()->setMaximum(mainSampleSource->count()); } +void PlotView::setCursorBits(int bits) +{ + cursors.setBits(bits); + viewport()->update(); +} + void PlotView::setFFTSize(int size) { fftSize = size; @@ -201,6 +207,7 @@ void PlotView::updateView() horizontalScrollBar()->value(), horizontalScrollBar()->value() + width() * samplesPerLine() }; + horizontalScrollBar()->setMaximum(mainSampleSource->count() - ((width() - 1) * samplesPerLine())); // Update cursors QRect rect = viewport()->rect(); diff --git a/plotview.h b/plotview.h index 8a48be3..e182c6c 100644 --- a/plotview.h +++ b/plotview.h @@ -43,6 +43,7 @@ public slots: void cursorsMoved(); void enableCursors(bool enable); void invalidateEvent(); + void setCursorBits(int bits); void setFFTSize(int size); void setZoomLevel(int zoom); void setPowerMin(int power); diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index a6d235a..a4b0f71 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -73,12 +73,21 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent cursorsCheckBox = new QCheckBox(widget); layout->addRow(new QLabel(tr("Enable cursors:")), cursorsCheckBox); + cursorBitsSpinBox = new QSpinBox(); + layout->addRow(new QLabel(tr("Bits:")), cursorBitsSpinBox); + timeSelectionFreqLabel = new QLabel(); layout->addRow(new QLabel(tr("Frequency:")), timeSelectionFreqLabel); timeSelectionTimeLabel = new QLabel(); layout->addRow(new QLabel(tr("Time:")), timeSelectionTimeLabel); + bitSelectionFreqLabel = new QLabel(); + layout->addRow(new QLabel(tr("Bit frequency:")), bitSelectionFreqLabel); + + bitSelectionTimeLabel = new QLabel(); + layout->addRow(new QLabel(tr("Bit time:")), bitSelectionTimeLabel); + widget->setLayout(layout); setWidget(widget); @@ -94,6 +103,7 @@ void SpectrogramControls::setDefaults() powerMaxSlider->setValue(0); powerMinSlider->setValue(-50); cursorsCheckBox->setCheckState(Qt::Unchecked); + cursorBitsSpinBox->setValue(1); } void SpectrogramControls::fftSizeSliderChanged(int size) @@ -113,4 +123,8 @@ void SpectrogramControls::timeSelectionChanged(float time) { timeSelectionTimeLabel->setText(QString::number(time) + "s"); timeSelectionFreqLabel->setText(QString::number(1 / time) + "Hz"); + + int bits = cursorBitsSpinBox->value(); + bitSelectionTimeLabel->setText(QString::number(time / bits) + "s"); + bitSelectionFreqLabel->setText(QString::number(bits / time) + "Hz"); } diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index 0399972..b1ebd94 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -57,8 +58,11 @@ public: QSlider *powerMaxSlider; QSlider *powerMinSlider; QCheckBox *cursorsCheckBox; + QSpinBox *cursorBitsSpinBox; QLabel *pointerFrequencyLabel; QLabel *pointerTimeLabel; QLabel *timeSelectionFreqLabel; QLabel *timeSelectionTimeLabel; + QLabel *bitSelectionFreqLabel; + QLabel *bitSelectionTimeLabel; }; From 5b03a23ba1d607ed62703b3258ce3559b9b75376 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 6 Mar 2016 01:00:55 +0000 Subject: [PATCH 85/99] main: Re-add command-line sample rate setting --- main.cpp | 2 +- mainwindow.cpp | 5 +++++ mainwindow.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 4142166..701e197 100644 --- a/main.cpp +++ b/main.cpp @@ -50,7 +50,7 @@ int main(int argc, char *argv[]) fputs("ERROR: could not parse rate\n", stderr); return 1; } - // TODO: set sample rate + mainWin.setSampleRate(rate); } const QStringList args = parser.positionalArguments(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 1cda6ca..e30cc9d 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -64,3 +64,8 @@ void MainWindow::setSampleRate(QString rate) { input->setSampleRate(rate.toInt()); } + +void MainWindow::setSampleRate(int rate) +{ + dock->sampleRate->setText(QString::number(rate)); +} diff --git a/mainwindow.h b/mainwindow.h index 2a3429c..4f628a9 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -35,6 +35,7 @@ public: public slots: void openFile(QString fileName); void setSampleRate(QString rate); + void setSampleRate(int rate); private: SpectrogramControls *dock; From 515db6995032af85b17dfc76c2583e0f2d360e83 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 6 Mar 2016 01:25:18 +0000 Subject: [PATCH 86/99] cursors: Set more reasonable bit-count limits --- spectrogramcontrols.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index a4b0f71..be3c761 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -74,6 +74,8 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent layout->addRow(new QLabel(tr("Enable cursors:")), cursorsCheckBox); cursorBitsSpinBox = new QSpinBox(); + cursorBitsSpinBox->setMinimum(1); + cursorBitsSpinBox->setMaximum(9999); layout->addRow(new QLabel(tr("Bits:")), cursorBitsSpinBox); timeSelectionFreqLabel = new QLabel(); From 710fbd97f3da9a242e42fc49ae070653fc06054e Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 6 Mar 2016 16:50:35 +0000 Subject: [PATCH 87/99] plotview: Setup default fftSize/zoomLevel to prevent divide-by-zero during load --- plotview.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plotview.h b/plotview.h index e182c6c..05c9319 100644 --- a/plotview.h +++ b/plotview.h @@ -65,8 +65,8 @@ private: range_t selectedSamples; std::pair selectionFreq; - int fftSize; - int zoomLevel; + int fftSize = 1024; + int zoomLevel = 0; int powerMin; int powerMax; bool cursorsEnabled; From 2b8f5dd7593708e5fcc8106bd3d0f0f57fa87c2f Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 6 Mar 2016 16:51:15 +0000 Subject: [PATCH 88/99] spectrogram: Workaround crash on final partial tile --- spectrogramplot.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index f2fc330..077daac 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -75,6 +75,7 @@ void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sa for (int x = rect.left(); x < rect.right(); x += linesPerTile()) { QPixmap *tile = getPixmapTile(tileID); // TODO: don't draw past rect.right() + // TODO: handle partial final tile painter.drawPixmap(QRect(x, rect.y(), linesPerTile() - xoffset, fftSize), *tile, QRect(xoffset, 0, linesPerTile() - xoffset, fftSize)); xoffset = 0; tileID += getStride() * linesPerTile(); @@ -127,6 +128,8 @@ void SpectrogramPlot::getLine(float *dest, off_t sample) { if (inputSource && fft) { auto buffer = inputSource->getSamples(sample, fftSize); + if (buffer == nullptr) + return; for (int i = 0; i < fftSize; i++) { buffer[i].real(buffer[i].real() * window[i]); From e29d8306e9ee3be2e0cb104280393455cd4d4ba2 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 6 Mar 2016 17:36:11 +0000 Subject: [PATCH 89/99] spectrogram: Cleanup dead code --- spectrogramplot.cpp | 44 -------------------------------------------- spectrogramplot.h | 15 --------------- 2 files changed, 59 deletions(-) diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index 077daac..9c19073 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -37,8 +37,6 @@ SpectrogramPlot::SpectrogramPlot(SampleSource> *src) zoomLevel = 0; powerMax = 0.0f; powerMin = -50.0f; - timeScaleIsEnabled = true; - deltaDragIsEnabled = true; for (int i = 0; i < 256; i++) { float p = (float)i / 256; @@ -53,17 +51,6 @@ SpectrogramPlot::~SpectrogramPlot() delete inputSource; } -QSize SpectrogramPlot::sizeHint() const -{ - return QSize(1024, 2048); -} - -void SpectrogramPlot::xyToFreqTime(int x, int y, float *freq, float *time) -{ - *freq = labs(x - (fftSize / 2)) * sampleRate / 2 / (float)fftSize; - *time = (float)lineToSample(y) / sampleRate; -} - void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) { if (!inputSource || inputSource->count() == 0) @@ -149,25 +136,9 @@ void SpectrogramPlot::getLine(float *dest, off_t sample) } } -void SpectrogramPlot::paintCursors(QPainter *painter, QRect rect) -{ - if (cursorStartX != -1) { - painter->save(); - QPen pen(Qt::white, 1, Qt::DashLine); - painter->setPen(pen); - painter->drawLine(rect.left(), cursorStartY, rect.right(), cursorStartY); - painter->drawLine(cursorStartX, rect.top(), cursorStartX, rect.bottom()); - painter->drawLine(rect.left(), cursorEndY, rect.right(), cursorEndY); - painter->drawLine(cursorEndX, rect.top(), cursorEndX, rect.bottom()); - painter->restore(); - - } -} - void SpectrogramPlot::setSampleRate(int rate) { sampleRate = rate; - update(); } void SpectrogramPlot::setFFTSize(int size) @@ -188,14 +159,12 @@ void SpectrogramPlot::setPowerMax(int power) { powerMax = power; pixmapCache.clear(); - update(); } void SpectrogramPlot::setPowerMin(int power) { powerMin = power; pixmapCache.clear(); - update(); } void SpectrogramPlot::setZoomLevel(int zoom) @@ -203,19 +172,6 @@ void SpectrogramPlot::setZoomLevel(int zoom) zoomLevel = clamp(zoom, 0, (int)log2(fftSize)); } -void SpectrogramPlot::setTimeScaleEnable(int state) -{ - timeScaleIsEnabled = (state == Qt::Checked); - pixmapCache.clear(); - update(); -} - -void SpectrogramPlot::setDeltaDragEnable(int state) -{ - deltaDragIsEnabled = (state == Qt::Checked); -} - - int SpectrogramPlot::getHeight() { if (!inputSource) diff --git a/spectrogramplot.h b/spectrogramplot.h index bfafd91..48cb30e 100644 --- a/spectrogramplot.h +++ b/spectrogramplot.h @@ -49,22 +49,12 @@ public: SampleSource> *inputSource = nullptr; -signals: - void cursorFrequencyChanged(QString); - void cursorTimeChanged(QString); - void deltaFrequencyChanged(QString); - void deltaTimeChanged(QString); - void needUpdate(); - public slots: void setSampleRate(int rate); void setFFTSize(int size); void setPowerMax(int power); void setPowerMin(int power); void setZoomLevel(int zoom); - void setTimeScaleEnable(int state); - void setDeltaDragEnable(int state); - void update() { emit needUpdate(); }; protected: void mouseReleaseEvent(QMouseEvent * event); @@ -88,10 +78,6 @@ private: int zoomLevel; float powerMax; float powerMin; - bool timeScaleIsEnabled; - bool deltaDragIsEnabled; - int cursorStartX = -1, cursorStartY; - int cursorEndX, cursorEndY; QPixmap* getPixmapTile(off_t tile); float* getFFTTile(off_t tile); @@ -100,7 +86,6 @@ private: int sampleToLine(off_t sample); QString sampleToTime(off_t sample); int linesPerTile(); - void xyToFreqTime(int x, int y, float *freq, float *time); }; class TileCacheKey From 04716ed74860d455ac16077635b5e3c9f506830f Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 6 Mar 2016 17:36:29 +0000 Subject: [PATCH 90/99] spectrogram: Don't delete inputSource - doesn't own it anymore --- spectrogramplot.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index 9c19073..fb14fd8 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -48,7 +48,6 @@ SpectrogramPlot::SpectrogramPlot(SampleSource> *src) SpectrogramPlot::~SpectrogramPlot() { delete fft; - delete inputSource; } void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) From 74a5307b58e5dfb0aee988014470fd64aa5f4267 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 6 Mar 2016 17:38:51 +0000 Subject: [PATCH 91/99] spectrogram: Use unique_ptr for fft --- spectrogramplot.cpp | 8 +------- spectrogramplot.h | 3 +-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index fb14fd8..a05255b 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -45,11 +45,6 @@ SpectrogramPlot::SpectrogramPlot(SampleSource> *src) } -SpectrogramPlot::~SpectrogramPlot() -{ - delete fft; -} - void SpectrogramPlot::paintMid(QPainter &painter, QRect &rect, range_t sampleRange) { if (!inputSource || inputSource->count() == 0) @@ -143,8 +138,7 @@ void SpectrogramPlot::setSampleRate(int rate) void SpectrogramPlot::setFFTSize(int size) { fftSize = size; - delete fft; - fft = new FFT(fftSize); + fft.reset(new FFT(fftSize)); window.reset(new float[fftSize]); for (int i = 0; i < fftSize; i++) { diff --git a/spectrogramplot.h b/spectrogramplot.h index 48cb30e..4a067c0 100644 --- a/spectrogramplot.h +++ b/spectrogramplot.h @@ -38,7 +38,6 @@ class SpectrogramPlot : public Plot public: SpectrogramPlot(SampleSource> *src); - ~SpectrogramPlot(); void paintMid(QPainter &painter, QRect &rect, range_t sampleRange); @@ -66,7 +65,7 @@ private: const int linesPerGraduation = 50; const int tileSize = 65536; // This must be a multiple of the maximum FFT size - FFT *fft = nullptr; + std::unique_ptr fft; std::unique_ptr window; fftwf_complex *lineBuffer = nullptr; QCache pixmapCache; From e1dd912d7283a30d758ea08a09391ee5c66d5996 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 6 Mar 2016 23:47:45 +0000 Subject: [PATCH 92/99] plotview: Add vertical scrolling --- plotview.cpp | 13 ++++++++++++- plotview.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/plotview.cpp b/plotview.cpp index d436311..f8de444 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -168,7 +168,7 @@ void PlotView::paintEvent(QPaintEvent *event) #define PLOT_LAYER(paintFunc) \ { \ - int y = 0; \ + int y = -verticalScrollBar()->value(); \ for (auto&& plot : plots) { \ QRect rect = QRect(0, y, width(), plot->height()); \ plot->paintFunc(painter, rect, {viewRange.first, viewRange.second});\ @@ -185,6 +185,15 @@ void PlotView::paintEvent(QPaintEvent *event) #undef PLOT_LAYER } +int PlotView::plotsHeight() +{ + int height = 0; + for (auto&& plot : plots) { + height += plot->height(); + } + return height; +} + void PlotView::resizeEvent(QResizeEvent * event) { updateView(); @@ -209,6 +218,8 @@ void PlotView::updateView() }; horizontalScrollBar()->setMaximum(mainSampleSource->count() - ((width() - 1) * samplesPerLine())); + verticalScrollBar()->setMaximum(std::max(0, plotsHeight() - viewport()->height())); + // Update cursors QRect rect = viewport()->rect(); range_t newSelection = { diff --git a/plotview.h b/plotview.h index 05c9319..1b837d4 100644 --- a/plotview.h +++ b/plotview.h @@ -73,6 +73,7 @@ private: TracePlot* createIQPlot(SampleSource> *src); TracePlot* createQuadratureDemodPlot(SampleSource> *src); + int plotsHeight(); off_t samplesPerLine(); void updateView(); }; \ No newline at end of file From 78ba7aab3a41aa0e2c98e865f8dce019781e7f8a Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 6 Mar 2016 23:56:06 +0000 Subject: [PATCH 93/99] controls: Remove pointer info --- spectrogramcontrols.cpp | 10 ---------- spectrogramcontrols.h | 2 -- 2 files changed, 12 deletions(-) diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index be3c761..4482770 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -56,16 +56,6 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent powerMinSlider->setRange(-100, 20); layout->addRow(new QLabel(tr("Power min:")), powerMinSlider); - // Pointer position info - layout->addRow(new QLabel()); // TODO: find a better way to add an empty row? - layout->addRow(new QLabel(tr("Pointer"))); - - pointerFrequencyLabel = new QLabel(); - layout->addRow(new QLabel(tr("Pointer frequency:")), pointerFrequencyLabel); - - pointerTimeLabel = new QLabel(); - layout->addRow(new QLabel(tr("Pointer time:")), pointerTimeLabel); - // Time selection settings layout->addRow(new QLabel()); // TODO: find a better way to add an empty row? layout->addRow(new QLabel(tr("Time selection"))); diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index b1ebd94..49bfaff 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -59,8 +59,6 @@ public: QSlider *powerMinSlider; QCheckBox *cursorsCheckBox; QSpinBox *cursorBitsSpinBox; - QLabel *pointerFrequencyLabel; - QLabel *pointerTimeLabel; QLabel *timeSelectionFreqLabel; QLabel *timeSelectionTimeLabel; QLabel *bitSelectionFreqLabel; From c3d5e9eafd2374a0dc1aea5366274054e54ca8c1 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 6 Mar 2016 23:56:17 +0000 Subject: [PATCH 94/99] controls: Clear cursor labels when disabled --- spectrogramcontrols.cpp | 28 +++++++++++++++++++++++----- spectrogramcontrols.h | 3 +++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 4482770..04fc901 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -85,6 +85,22 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent connect(fftSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(fftSizeSliderChanged(int))); connect(fileOpenButton, SIGNAL(clicked()), this, SLOT(fileOpenButtonClicked())); + connect(cursorsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(cursorsStateChanged(int))); +} + +void SpectrogramControls::clearCursorLabels() +{ + timeSelectionTimeLabel->setText(""); + timeSelectionFreqLabel->setText(""); + bitSelectionTimeLabel->setText(""); + bitSelectionFreqLabel->setText(""); +} + +void SpectrogramControls::cursorsStateChanged(int state) +{ + if (state == Qt::Unchecked) { + clearCursorLabels(); + } } void SpectrogramControls::setDefaults() @@ -113,10 +129,12 @@ void SpectrogramControls::fileOpenButtonClicked() void SpectrogramControls::timeSelectionChanged(float time) { - timeSelectionTimeLabel->setText(QString::number(time) + "s"); - timeSelectionFreqLabel->setText(QString::number(1 / time) + "Hz"); + if (cursorsCheckBox->checkState() == Qt::Checked) { + timeSelectionTimeLabel->setText(QString::number(time) + "s"); + timeSelectionFreqLabel->setText(QString::number(1 / time) + "Hz"); - int bits = cursorBitsSpinBox->value(); - bitSelectionTimeLabel->setText(QString::number(time / bits) + "s"); - bitSelectionFreqLabel->setText(QString::number(bits / time) + "Hz"); + int bits = cursorBitsSpinBox->value(); + bitSelectionTimeLabel->setText(QString::number(time / bits) + "s"); + bitSelectionFreqLabel->setText(QString::number(bits / time) + "Hz"); + } } diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index 49bfaff..bf9100b 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -46,10 +46,13 @@ public slots: private slots: void fftSizeSliderChanged(int size); void fileOpenButtonClicked(); + void cursorsStateChanged(int state); private: QWidget *widget; QFormLayout *layout; + void clearCursorLabels(); + public: QPushButton *fileOpenButton; QLineEdit *sampleRate; From f1750da64ecf1690c21fa84fa63df29e1fde8a7c Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 6 Mar 2016 23:57:44 +0000 Subject: [PATCH 95/99] cursors: Update cursor labels when changing bit count --- plotview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plotview.cpp b/plotview.cpp index f8de444..c040644 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -120,6 +120,7 @@ void PlotView::invalidateEvent() void PlotView::setCursorBits(int bits) { cursors.setBits(bits); + cursorsMoved(); viewport()->update(); } From 3b40a65852c4c842cc41072983f9b806d4cedbcd Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 6 Mar 2016 23:48:23 +0000 Subject: [PATCH 96/99] plotview: Disable time-domain plots for now --- plotview.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plotview.cpp b/plotview.cpp index c040644..9c53147 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -41,10 +41,6 @@ PlotView::PlotView(InputSource *input) : cursors(this), viewRange({0, 0}) spectrogramPlot = new SpectrogramPlot(mainSampleSource); plots.emplace_back(spectrogramPlot); - iqPlot = createIQPlot(mainSampleSource); - plots.emplace_back(iqPlot); - plots.emplace_back(createQuadratureDemodPlot(static_cast>*>(iqPlot->source().get()))); - mainSampleSource->subscribe(this); } From a735394859689b7f2a13eab541c6d9535dab5b0c Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 7 Mar 2016 00:34:46 +0000 Subject: [PATCH 97/99] controls: Update FFT size and zoom level in one operation Zoom level needs to be clamped to FFT size so this prevents crashiness later on --- mainwindow.cpp | 3 +-- plotview.cpp | 15 ++++++++------- plotview.h | 3 +-- spectrogramcontrols.cpp | 7 ++++--- spectrogramcontrols.h | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index e30cc9d..b29574c 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -39,8 +39,7 @@ MainWindow::MainWindow() // Connect dock inputs connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); connect(dock->sampleRate, SIGNAL(textChanged(QString)), this, SLOT(setSampleRate(QString))); - connect(dock, SIGNAL(fftSizeChanged(int)), plots, SLOT(setFFTSize(int))); - connect(dock->zoomLevelSlider, SIGNAL(valueChanged(int)), plots, SLOT(setZoomLevel(int))); + 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->cursorsCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableCursors); diff --git a/plotview.cpp b/plotview.cpp index 9c53147..5459637 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -120,21 +120,22 @@ void PlotView::setCursorBits(int bits) viewport()->update(); } -void PlotView::setFFTSize(int size) +void PlotView::setFFTAndZoom(int size, int zoom) { + // Set new FFT size fftSize = size; if (spectrogramPlot != nullptr) spectrogramPlot->setFFTSize(size); - horizontalScrollBar()->setSingleStep(size * 10 / pow(2, zoomLevel)); - horizontalScrollBar()->setPageStep(size * 100 / pow(2, zoomLevel)); - updateView(); -} -void PlotView::setZoomLevel(int zoom) -{ + // Set new zoom level zoomLevel = zoom; if (spectrogramPlot != nullptr) spectrogramPlot->setZoomLevel(zoom); + + // Update horizontal (time) scrollbar + horizontalScrollBar()->setSingleStep(size * 10 / pow(2, zoomLevel)); + horizontalScrollBar()->setPageStep(size * 100 / pow(2, zoomLevel)); + updateView(); } diff --git a/plotview.h b/plotview.h index 1b837d4..ca8d04d 100644 --- a/plotview.h +++ b/plotview.h @@ -44,8 +44,7 @@ public slots: void enableCursors(bool enable); void invalidateEvent(); void setCursorBits(int bits); - void setFFTSize(int size); - void setZoomLevel(int zoom); + void setFFTAndZoom(int fftSize, int zoomLevel); void setPowerMin(int power); void setPowerMax(int power); diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 04fc901..01d3a83 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -83,7 +83,8 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent widget->setLayout(layout); setWidget(widget); - connect(fftSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(fftSizeSliderChanged(int))); + connect(fftSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(fftOrZoomChanged(int))); + connect(zoomLevelSlider, SIGNAL(valueChanged(int)), this, SLOT(fftOrZoomChanged(int))); connect(fileOpenButton, SIGNAL(clicked()), this, SLOT(fileOpenButtonClicked())); connect(cursorsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(cursorsStateChanged(int))); } @@ -114,9 +115,9 @@ void SpectrogramControls::setDefaults() cursorBitsSpinBox->setValue(1); } -void SpectrogramControls::fftSizeSliderChanged(int size) +void SpectrogramControls::fftOrZoomChanged(int value) { - emit fftSizeChanged((int)pow(2, size)); + emit fftOrZoomChanged((int)pow(2, fftSizeSlider->value()), zoomLevelSlider->value()); } void SpectrogramControls::fileOpenButtonClicked() diff --git a/spectrogramcontrols.h b/spectrogramcontrols.h index bf9100b..09c0a5e 100644 --- a/spectrogramcontrols.h +++ b/spectrogramcontrols.h @@ -37,14 +37,14 @@ public: void setDefaults(); signals: - void fftSizeChanged(int size); + void fftOrZoomChanged(int fftSize, int zoomLevel); void openFile(QString fileName); public slots: void timeSelectionChanged(float time); private slots: - void fftSizeSliderChanged(int size); + void fftOrZoomChanged(int value); void fileOpenButtonClicked(); void cursorsStateChanged(int state); From aeb295fe285328c4a324604542b47022c210e392 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 7 Mar 2016 00:40:59 +0000 Subject: [PATCH 98/99] controls: Emit zoomLevel final multiplier (not a power) --- plotview.cpp | 6 +++--- spectrogramcontrols.cpp | 4 +++- spectrogramplot.cpp | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/plotview.cpp b/plotview.cpp index 5459637..b2f8da5 100644 --- a/plotview.cpp +++ b/plotview.cpp @@ -133,8 +133,8 @@ void PlotView::setFFTAndZoom(int size, int zoom) spectrogramPlot->setZoomLevel(zoom); // Update horizontal (time) scrollbar - horizontalScrollBar()->setSingleStep(size * 10 / pow(2, zoomLevel)); - horizontalScrollBar()->setPageStep(size * 100 / pow(2, zoomLevel)); + horizontalScrollBar()->setSingleStep(size * 10 / zoomLevel); + horizontalScrollBar()->setPageStep(size * 100 / zoomLevel); updateView(); } @@ -199,7 +199,7 @@ void PlotView::resizeEvent(QResizeEvent * event) off_t PlotView::samplesPerLine() { - return fftSize / (int)pow(2, zoomLevel); + return fftSize / zoomLevel; } void PlotView::scrollContentsBy(int dx, int dy) diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 01d3a83..70cdc9a 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -117,7 +117,9 @@ void SpectrogramControls::setDefaults() void SpectrogramControls::fftOrZoomChanged(int value) { - emit fftOrZoomChanged((int)pow(2, fftSizeSlider->value()), zoomLevelSlider->value()); + int fftSize = pow(2, fftSizeSlider->value()); + int zoomLevel = pow(2, zoomLevelSlider->value()); + emit fftOrZoomChanged(fftSize, zoomLevel); } void SpectrogramControls::fileOpenButtonClicked() diff --git a/spectrogramplot.cpp b/spectrogramplot.cpp index a05255b..92fdee8 100644 --- a/spectrogramplot.cpp +++ b/spectrogramplot.cpp @@ -162,7 +162,7 @@ void SpectrogramPlot::setPowerMin(int power) void SpectrogramPlot::setZoomLevel(int zoom) { - zoomLevel = clamp(zoom, 0, (int)log2(fftSize)); + zoomLevel = zoom; } int SpectrogramPlot::getHeight() @@ -175,7 +175,7 @@ int SpectrogramPlot::getHeight() int SpectrogramPlot::getStride() { - return fftSize / pow(2, zoomLevel); + return fftSize / zoomLevel; } off_t SpectrogramPlot::lineToSample(off_t line) From 49471c5b081e78cc088dd3dfdaa82bf0e858aa27 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 7 Mar 2016 00:43:05 +0000 Subject: [PATCH 99/99] controls: Clamp zoomLevel to fftSize --- spectrogramcontrols.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 70cdc9a..cfcb102 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -118,7 +118,7 @@ void SpectrogramControls::setDefaults() void SpectrogramControls::fftOrZoomChanged(int value) { int fftSize = pow(2, fftSizeSlider->value()); - int zoomLevel = pow(2, zoomLevelSlider->value()); + int zoomLevel = std::min(fftSize, (int)pow(2, zoomLevelSlider->value())); emit fftOrZoomChanged(fftSize, zoomLevel); }