/* * 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 #include #include #include #include #include "mainwindow.h" #include "util.h" MainWindow::MainWindow() { setWindowTitle(tr("inspectrum")); QPixmapCache::setCacheLimit(40960); dock = new SpectrogramControls(tr("Controls"), this); dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); addDockWidget(Qt::LeftDockWidgetArea, dock); input = new InputSource(); plots = new PlotView(input); setCentralWidget(plots); createActions(); // Connect dock inputs connect(dock, &SpectrogramControls::fftSizeChanged, plots, &PlotView::setFFTSize); connect(dock->powerMaxSlider, SIGNAL(valueChanged(int)), plots, SLOT(setPowerMax(int))); connect(dock->powerMinSlider, SIGNAL(valueChanged(int)), plots, SLOT(setPowerMin(int))); connect(dock->scalesCheckBox, &QCheckBox::stateChanged, plots, &PlotView::enableScales); connect(dock->cursorSymbolsSpinBox, static_cast(&QSpinBox::valueChanged), plots, &PlotView::setCursorSegments); // Connect dock outputs connect(plots, SIGNAL(timeSelectionChanged(float)), dock, SLOT(timeSelectionChanged(float))); // Set defaults after making connections so everything is in sync dock->setDefaults(); } void MainWindow::createActions() { QSettings settings; QToolBar *fileToolBar = addToolBar(tr("File")); const QIcon openIcon = QIcon::fromTheme("document-open"); QAction *openAct = new QAction(openIcon, tr("&Open..."), this); openAct->setShortcuts(QKeySequence::Open); connect(openAct, &QAction::triggered, this, &MainWindow::fileOpenButtonClicked); fileToolBar->addAction(openAct); sampleRate = new QLineEdit(); connect(sampleRate, &QLineEdit::textChanged, this, static_cast(&MainWindow::setSampleRate)); int savedSampleRate = settings.value("SampleRate", 8000000).toInt(); sampleRate->setText(QString::number(savedSampleRate)); sampleRate->setValidator(new QIntValidator(this)); fileToolBar->addWidget(new QLabel(tr("Sample rate: "))); fileToolBar->addWidget(sampleRate); QToolBar *zoomToolBar = addToolBar(tr("Zoom")); const QIcon zoomInIcon = QIcon::fromTheme("zoom-in"); QAction *zoomInAct = new QAction(zoomInIcon, tr("Zoom in"), this); connect(zoomInAct, &QAction::triggered, plots, &PlotView::zoomIn); zoomToolBar->addAction(zoomInAct); const QIcon zoomOutIcon = QIcon::fromTheme("zoom-out"); QAction *zoomOutAct = new QAction(zoomOutIcon, tr("Zoom out"), this); connect(zoomOutAct, &QAction::triggered, plots, &PlotView::zoomOut); zoomToolBar->addAction(zoomOutAct); QToolBar *cursorsToolBar = addToolBar(tr("Cursors")); const QIcon toggleCursorsIcon = QIcon(":/icons/show-cursors.svg"); QAction *toggleCursorsAct = new QAction(toggleCursorsIcon, tr("Toggle cursors"), this); toggleCursorsAct->setCheckable(true); connect(toggleCursorsAct, &QAction::toggled, plots, &PlotView::enableCursors); cursorsToolBar->addAction(toggleCursorsAct); } void MainWindow::fileOpenButtonClicked() { QSettings settings; QString fileName; QFileDialog fileSelect(this); fileSelect.setNameFilter(tr("All files (*);;" "complex file (*.cfile *.cf32 *.fc32);;" "complex HackRF file (*.cs8 *.sc8 *.c8);;" "complex Fancy file (*.cs16 *.sc16 *.c16);;" "complex RTL-SDR file (*.cu8 *.uc8)")); // Try and load a saved state { QByteArray savedState = settings.value("OpenFileState").toByteArray(); fileSelect.restoreState(savedState); // Filter doesn't seem to be considered part of the saved state QString lastUsedFilter = settings.value("OpenFileFilter").toString(); if(lastUsedFilter.size()) fileSelect.selectNameFilter(lastUsedFilter); } if(fileSelect.exec()) { fileName = fileSelect.selectedFiles()[0]; // Remember the state of the dialog for the next time QByteArray dialogState = fileSelect.saveState(); settings.setValue("OpenFileState", dialogState); settings.setValue("OpenFileFilter", fileSelect.selectedNameFilter()); } if (!fileName.isEmpty()) openFile(fileName); } void MainWindow::openFile(QString fileName) { QString title="%1: %2"; this->setWindowTitle(title.arg(QApplication::applicationName(),fileName.section('/',-1,-1))); // Try to parse osmocom_fft filenames and extract the sample rate and center frequency. // Example file name: "name-f2.411200e+09-s5.000000e+06-t20160807180210.cfile" QRegExp rx("(.*)-f(.*)-s(.*)-.*\\.cfile"); QString basename = fileName.section('/',-1,-1); if (rx.exactMatch(basename)) { QString centerfreq = rx.cap(2); QString samplerate = rx.cap(3); std::stringstream ss(samplerate.toUtf8().constData()); // Needs to be a double as the number is in scientific format double rate; ss >> rate; if (!ss.fail()) { setSampleRate(rate); } } try { input->openFile(fileName.toUtf8().constData()); } catch (const std::exception &ex) { QMessageBox msgBox(QMessageBox::Critical, "Inspectrum openFile error", QString("%1: %2").arg(fileName).arg(ex.what())); msgBox.exec(); } } void MainWindow::setSampleRate(QString rate) { int sampleRate = rate.toInt(); input->setSampleRate(sampleRate); plots->setSampleRate(sampleRate); // Save the sample rate in settings as we're likely to be opening the same file across multiple runs QSettings settings; settings.setValue("SampleRate", sampleRate); } void MainWindow::setSampleRate(int rate) { sampleRate->setText(QString::number(rate)); }