Compare commits

..

57 Commits

Author SHA1 Message Date
Michal Krenek (Mikos)
625ba9d698 Refactoring 2017-04-27 23:22:29 +02:00
Michal Krenek (Mikos)
6800f5bd8e Allow subtracting baseline 2017-04-27 23:01:15 +02:00
Michal Krenek (Mikos)
4571075bd9 Use np.linspace() instead of np.arange() in soapy_power, rtl_power and rx_power backends 2017-04-27 16:46:18 +02:00
Michal Krenek (Mikos)
5505f0dfd7 Write whole backend command with params to console 2017-04-21 17:20:29 +02:00
Michal Krenek (Mikos)
2544b9a164 Fix human readable time formatting 2017-04-12 17:49:26 +02:00
Michal Krenek (Mikos)
8aa6d50bb8 Show scan progress if interval is >= 1 s 2017-04-12 17:44:22 +02:00
Michal Krenek (Mikos)
31471b3386 Allow setting hackrf_sweep backend to -1 in GUI 2017-04-03 17:56:08 +02:00
Michal Krenek (Mikos)
1c9e7681b8 Allow manual setting of specific gains in hackrf_sweep backend 2017-04-03 17:50:38 +02:00
Michal Krenek (Mikos)
edf8ae17e2 Update setup.py to be compatible with latest version of subzero 2017-03-31 16:43:49 +02:00
Michal Krenek (Mikos)
c6e305343c Unhide console on Windows right before exit (we don't want to leave zombies behind) 2017-03-30 16:17:03 +02:00
Michal Krenek (Mikos)
c140e9313e Code cleanup 2017-03-30 16:08:47 +02:00
Michal Krenek (Mikos)
c93bbf3de5 Fix link in README.rst 2017-03-30 15:51:23 +02:00
Michal Krenek (Mikos)
36be8e8371 Update README.rst with informations about Windows installer 2017-03-30 15:47:37 +02:00
Michal Krenek (Mikos)
06956c4c1f Move classifiers down in setup.py 2017-03-30 14:55:15 +02:00
Michal Krenek (Mikos)
9b04a924f2 Update .desktop file 2017-03-30 14:51:14 +02:00
Michal Krenek (Mikos)
415687685a Update version to 2.2.0 2017-03-30 14:49:07 +02:00
Michal Krenek (Mikos)
9d21d49157 Always open console on Windows (but hide it)
Otherwise program crashes when trying to communicate with subprocess
(e.g. soapy_power) if started in pythonw.exe, bacause console is missing.
2017-03-30 14:27:33 +02:00
Michal Krenek (Mikos)
e9391aedc0 Split help command executable in settings 2017-03-28 15:52:49 +02:00
Michal Krenek (Mikos)
05f4a59b66 Hide console window for subprocesses on Windows 2017-03-28 12:26:45 +02:00
Michal Krenek (Mikos)
fa462f885d Make help commands in settings compatible with Python 3.4 2017-03-28 10:44:03 +02:00
Michal Krenek (Mikos)
9ee6f631dc Fix shortcut in MSI installer 2017-03-28 00:36:32 +02:00
Michal Krenek (Mikos)
9271e42766 Switch from cx_Freeze to PyInstaller / subzero for building frozen Windows executables 2017-03-28 00:32:11 +02:00
Michal Krenek (Mikos)
e75f9a98d9 Merge pull request #28 from michaellass/hackrf_ratelimit
hackrf_sweep: Improve low frequency range capabilities
2017-03-24 22:58:56 +01:00
Michael Lass
5985cb4bfe hackrf_sweep: fix race condition when stopping
When stopping, the process may die right before we try to read from its stdout,
generating the following error:

Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/qspectrumanalyzer/backends/hackrf_sweep.py", line 146, in run
    buf = self.process.stdout.read(4)
AttributeError: 'NoneType' object has no attribute 'stdout'

Catch the error instead of crashing.
2017-03-24 21:57:52 +01:00
Michael Lass
bc3319ba82 hackrf_sweep: Reduce bin_size_min to 3 kHz
Using rate limiting, smaller bin sizes than 40 kHz can be used. This is useful
when using the hackrf_sweep backend with narrow frequency ranges.
2017-03-24 21:38:52 +01:00
Michael Lass
a49f1a3187 hackrf_sweep: Implement rate limiting by ignoring sweeps
For low frequency ranges the sweep range of hackrf_sweep is pretty high.
To allow using the hackrf_sweep backend with those small ranges, we can
simply ignore sweeps based on a user-set sweep interval.
2017-03-24 21:37:31 +01:00
Michal Krenek (Mikos)
12da1d6626 Merge pull request #22 from michaellass/check_bincount
Check bincount in data coming from backend
2017-03-24 21:02:10 +01:00
Michael Lass
9dc1433bdf Check bincount in data coming from backend
The backend may return fewer data points than expected. This can e.g. be
observed for the hackrf_sweep backend on high system load. Currently
this leads to an error when trying to insert the data into the
HistoryBuffer and causes the application to crash:

ValueError: cannot copy sequence with size 24750 to array axis with dimension 25000

As a workaround, just ignore data in this case.
2017-03-24 17:50:03 +01:00
Michal Krenek (Mikos)
8109c537f6 Allow setting gain with more precision; tune default parameters of backends 2017-03-24 16:51:32 +01:00
Michal Krenek (Mikos)
87585c106b Fix numbering in README.rst 2017-03-24 02:50:03 +01:00
Michal Krenek (Mikos)
02859deccf Add Windows installation instructions to README.rst 2017-03-24 02:48:21 +01:00
Michal Krenek (Mikos)
2385c228c6 Fix README.rst 2017-03-24 01:33:05 +01:00
Michal Krenek (Mikos)
11979ba641 Update README.rst, clarify backends section 2017-03-24 01:22:03 +01:00
Michal Krenek (Mikos)
9135dc58e4 Make soapy_power dependency optional (but don't remove it from setup.py and PKGBUILD, because it is default backend) 2017-03-24 00:15:56 +01:00
Michal Krenek (Mikos)
92e1771db5 Add support for creating frozen executables with cx_Freeze; use new setup_qt module for building Qt UI files 2017-03-23 18:11:05 +01:00
Michal Krenek (Mikos)
8ac3346c1e Fix some GUI issues on Windows 2017-03-22 02:16:26 +01:00
Michal Krenek (Mikos)
7e84cd7a1c Fix another Windows peculiarity. How can anyone sane use that fscking OS?!? 2017-03-21 23:39:30 +01:00
Michal Krenek (Mikos)
5d41c6c4a8 Update version to 2.1.0 2017-03-21 17:43:39 +01:00
Michal Krenek (Mikos)
07d504ab30 Don't show number of hops in status bar if it isn't available 2017-03-21 16:59:48 +01:00
Michal Krenek (Mikos)
d8d158c100 Make QSpectrumAnalyzer compatible with latest soapy_power 1.5.0 2017-03-21 16:39:26 +01:00
Michal Krenek (Mikos)
c574761a8c Allow setting bandwidth and LNB LO (for upconverters or downconverters); show info about device in settings 2017-03-21 10:36:08 +01:00
Michal Krenek (Mikos)
ea969b89e2 Add missing Qt.py dependency to PKGBUILD 2017-03-17 20:23:45 +01:00
Michal Krenek (Mikos)
b8666417b0 Update version to 2.0.0 2017-03-17 15:03:07 +01:00
Michal Krenek (Mikos)
e0f7202ff9 Update soapy_power_bin format to include both acquisition start time and stop time 2017-03-16 16:43:12 +01:00
Michal Krenek (Mikos)
9de4c25269 Fix pipe for communication with soapy_power on Windows 2017-03-15 00:58:10 +01:00
Michal Krenek (Mikos)
50ff8d517d Allow backend executable to be multiple words (e.g. python /path/to/soapy_power.py) 2017-03-15 00:38:10 +01:00
Michal Krenek (Mikos)
58ea054d70 Merge pull request #20 from xmikos/pyqt5
Switch from PyQt4 to Qt.py wrapper (supports PyQt4 / PyQt5 / PySide / PySide2)
2017-03-13 22:53:30 +01:00
Michal Krenek (Mikos)
228255b7e1 Switch from PyQt4 to Qt.py wrapper (supports PyQt4 / PyQt5 / PySide / PySide2) 2017-03-13 22:50:54 +01:00
Michal Krenek (Mikos)
d1c6ca4d14 Use pipe for communication with soapy_power process, to avoid reading garbage from stdout (some SoapySDR drivers like LimeSDR always outputs something to stdout) 2017-03-13 17:41:22 +01:00
Michal Krenek (Mikos)
47ea0b99b5 New version 1.5.0 (soapy_sdr backend now fully functioning) 2017-03-10 17:39:04 +01:00
Michal Krenek (Mikos)
dc27e90653 Update README.rst 2017-02-19 11:37:30 +01:00
Michal Krenek (Mikos)
e7b998f69d Fix typo in last commit (need some sleep) 2017-02-19 00:52:05 +01:00
Michal Krenek (Mikos)
98a8b6bd1d Fix setup.py (missing qspectrumanalyzer.backends package) 2017-02-19 00:38:47 +01:00
Michal Krenek (Mikos)
e9264886e8 Separate backends into independent files 2017-02-14 23:01:15 +01:00
Michal Krenek (Mikos)
d351a858da Merge pull request #11 from miek/hackrf_sweep_fix
Fix old references to device_index from hackrf_sweep merge
2017-02-14 15:57:02 +01:00
Mike Walters
e73e921440 Fix old references to device_index from hackrf_sweep merge 2017-02-14 13:40:23 +00:00
Michal Krenek (Mikos)
7fa0985133 Merge pull request #10 from xmikos/hackrf_sweep
Merge hackrf_sweep branch into master
2017-02-14 13:54:21 +01:00
42 changed files with 3373 additions and 1648 deletions

View File

@ -2,4 +2,3 @@ include LICENSE
include README.rst
include qspectrumanalyzer.desktop
include qspectrumanalyzer.png
include qspectrumanalyzer.svg

View File

@ -1,14 +1,20 @@
# Maintainer: Michal Krenek (Mikos) <m.krenek@gmail.com>
pkgname=qspectrumanalyzer
pkgver=1.4.0
pkgver=2.2.0
pkgrel=1
pkgdesc="Spectrum analyzer for RTL-SDR (GUI for rtl_power based on PyQtGraph)"
pkgdesc="Spectrum analyzer for multiple SDR platforms (PyQtGraph based GUI for soapy_power, hackrf_sweep, rtl_power, rx_power and other backends)"
arch=('any')
url="https://github.com/xmikos/qspectrumanalyzer"
license=('GPL3')
depends=('python-pyqt4' 'python-pyqtgraph' 'rtl-sdr')
depends=('python-qt.py' 'python-pyqt5' 'python-pyqtgraph' 'soapy_power>=1.6.0')
makedepends=('python-setuptools')
optdepends=('rtl_power_fftw-git: alternative rtl_power implementation using FFTW library')
optdepends=(
'hackrf: hackrf_sweep backend (wideband spectrum monitoring with sweep rate of 8 GHz/s)'
'rtl_power_fftw-git: alternative RTL-SDR backend using FFTW library (much faster than rtl_power)'
'rtl-sdr-keenerd-git: better version of rtl_power backend'
'rtl-sdr: original rtl_power backend (slightly broken, use rtl-sdr-keenerd-git instead)'
'rx_tools: rx_power backend (universal SoapySDR based backend, but seems slow and buggy)'
)
source=(https://github.com/xmikos/qspectrumanalyzer/archive/v$pkgver.tar.gz)
build() {

View File

@ -1,7 +1,8 @@
QSpectrumAnalyzer
=================
Spectrum analyzer for RTL-SDR (GUI for rtl_power based on PyQtGraph)
Spectrum analyzer for multiple SDR platforms (PyQtGraph based GUI for soapy_power,
hackrf_sweep, rtl_power, rx_power and other backends)
Screenshots
-----------
@ -14,37 +15,73 @@ Requirements
------------
- Python >= 3.3
- PyQt >= 4.5
- PyQt4 / PyQt5 / PySide / PySide2
- Qt.py (https://github.com/mottosso/Qt.py)
- PyQtGraph (http://www.pyqtgraph.org)
- rtl-sdr (https://github.com/keenerd/rtl-sdr)
- Optional: rtl_power_fftw (https://github.com/AD-Vega/rtl-power-fftw)
- soapy_power (https://github.com/xmikos/soapy_power)
- Optional: hackrf / rtl-sdr / rtl_power_fftw / rx_tools
You should use `Keenerds fork of rtl-sdr <https://github.com/keenerd/rtl-sdr>`_
(latest Git revision), because ``rtl_power`` in original rtl-sdr (from osmocom.org)
Backends
--------
Default backend
***************
- **soapy_power** (https://github.com/xmikos/soapy_power)
``soapy_power`` is the default and recommended universal SDR backend in QSpectrumAnalyzer.
It is based on `SoapySDR <https://github.com/pothosware/SoapySDR>`_ and supports
nearly all SDR platforms (RTL-SDR, HackRF, Airspy, SDRplay, LimeSDR, bladeRF,
USRP and some other SDR devices). It is highly configurable (see additional parameters
help in *Settings* menu) and supports short acquisition time for
near real-time continuous measurement.
Other backends
**************
- **hackrf_sweep** (https://github.com/mossmann/hackrf)
``hackrf_sweep`` backend enables wideband spectrum monitoring by rapidly retuning the radio
without requiring individual tuning requests from the host computer. This allows unprecedented
sweep rate of 8 GHz per second. Only HackRF is supported.
- **rtl_power_fftw** (https://github.com/AD-Vega/rtl-power-fftw)
``rtl_power_fftw`` is alternative backend for RTL-SDR devices and has various
benefits over ``rtl_power``. E.g. better FFT performance (thanks to
use of ``fftw`` library) and possibility to use short acquisition time
for near real-time continuous measurement (minimum interval in original
``rtl_power`` is 1 second).
- **rtl_power** (https://github.com/keenerd/rtl-sdr)
``rtl_power`` is original backend for RTL-SDR devices. There are better alternatives now, but
if you want to use it, you should use `Keenerds fork of rtl-sdr <https://github.com/keenerd/rtl-sdr>`_
(latest Git revision), because ``rtl_power`` in original rtl-sdr package (from osmocom.org)
is broken (especially when used with cropping).
Another alternative is
`rtl_power_fftw <https://github.com/AD-Vega/rtl-power-fftw>`_ which has various
benefits over ``rtl_power``. E.g. better FFT performance (thanks to
use of ``fftw`` library) and possibility to use much shorter acquisition time
for more real-time continuous measurement (minimum interval in original
``rtl_power`` is 1 second, but in ``rtl_power_fftw`` you are only limited
by number of frequency hops).
- **rx_power** (https://github.com/rxseger/rx_tools) *[unsupported]*
``rx_power`` (part of ``rx_tools``) is also based on SoapySDR (like default ``soapy_power`` backend)
and therefore supports nearly all SDR platforms. But it is much slower than soapy_power, doesn't support
near real-time continuous measurement (minimum interval is 1 second, same as ``rtl_power``)
and is buggy. Backend is currently unsupported, if you want to fix it, patches are welcome.
Usage
-----
Start QSpectrumAnalyzer by running ``qspectrumanalyzer``.
You can choose if you want to use ``rtl_power`` or ``rtl_power_fftw`` backend in
*File* -> *Settings* (default is ``rtl_power``). Path to ``rtl_power``
(or ``rtl_power_fftw``) executable can be also manually specified there. You can also
set waterfall plot history size in there. Default is 100 lines, be aware that
really large sweeps (with a lot of bins) would require a lot of system memory, so
don't make this number too big.
You can choose which backend you want to use in *File* -> *Settings*
(or *Application menu* -> *Preferences* on Mac OS X), default is
``soapy_power``. Device, sample rate, bandwidth, LNB LO, path to backend executable
and additional backend parameters can be also manually specified there. You can
also set waterfall plot history size. Default is 100 lines, be aware that
really large sweeps (with a lot of bins) would require a lot of system
memory, so don't make this number too big.
Controls should be intuitive, but if you want consistent results, you should
turn off automatic gain control (set it to some fixed number) and also set
turn off automatic gain control (set gain to some fixed number) and also set
crop to 20% or more. For finding out ppm correction factor for your rtl-sdr
stick, use `kalibrate-rtl <https://github.com/steve-m/kalibrate-rtl>`_.
@ -55,39 +92,93 @@ table can be changed in mini-histogram widget (on *Levels* tab).
Installation
------------
**Arch Linux:**
Arch Linux:
***********
Stable version:
::
git clone https://aur.archlinux.org/qspectrumanalyzer.git
cd qspectrumanalyzer
makepkg -sri
Or simply use `pacaur <https://aur.archlinux.org/packages/pacaur>`_ (or any other AUR helper):
Git master branch:
::
git clone https://aur.archlinux.org/qspectrumanalyzer-git.git
cd qspectrumanalyzer-git
makepkg -sri
Or simply use `pacaur <https://aur.archlinux.org/packages/pacaur>`_ (or any other AUR helper)
which will also automatically install all QSpectrumAnalyzer dependencies:
::
pacaur -S qspectrumanalyzer
pacaur -S qspectrumanalyzer-git
**Debian / Ubuntu:**
Ubuntu:
*******
::
sudo apt-get install python3-pip python3-pyqt4 python3-numpy
sudo pip3 install qspectrumanalyzer
# Add SoapySDR PPA to your system
sudo add-apt-repository -y ppa:myriadrf/drivers
Warning! ``pip`` will install packages system-wide by default, but you
should always use your distribution package manager for this.
# Update list of packages
sudo apt-get update
You can install it locally only for your current user by running this (without ``sudo``):
::
# Install basic dependencies
sudo apt-get install python3-pip python3-pyqt5 python3-numpy python3-scipy soapysdr python3-soapysdr
# Install SoapySDR drivers for your hardware (e.g. RTL-SDR, Airspy, HackRF, LimeSDR, etc.)
sudo apt-get install soapysdr-module-rtlsdr soapysdr-module-airspy soapysdr-module-hackrf soapysdr-module-lms7
# Install QSpectrumAnalyzer locally for your current user
pip3 install --user qspectrumanalyzer
Executables will be then placed in ``~/.local/bin`` directory, you can add it to your
PATH in ``~/.bashrc``.
``qspectrumanalyzer`` and ``soapy_power`` executables will be then placed in
``~/.local/bin`` directory, you can add it to your PATH in ``~/.bashrc``.
If you want to install QSpectrumAnalyzer directly from Git master branch, you can use this procedure:
::
git clone https://github.com/xmikos/qspectrumanalyzer.git
cd qspectrumanalyzer
pip3 install --user .
Windows:
********
*Only 64-bit Windows are supported (there are no public 32-bit builds of SoapySDR
libraries and drivers).*
1. install `SoapySDR <https://github.com/pothosware/SoapySDR/wiki>`_ libraries and drivers
(bundled as part of Pothos SDR installer: `download <http://downloads.myriadrf.org/builds/PothosSDR/?C=M;O=D>`_).
This bundle also includes other great SDR apps like `CubicSDR <http://cubicsdr.com>`_, `GQRX <http://gqrx.dk>`_,
`GNU Radio Companion <https://gnuradio.org>`_, `Pothos GUI <https://github.com/pothosware/pothos/wiki>`_,
`Lime Suite <https://github.com/myriadrf/LimeSuite>`_ and `Zadig <http://zadig.akeo.ie>`_.
Utilities like ``hackrf_sweep`` and ``rtl_power`` are also included.
2. download QSpectrumAnalyzer installer or portable zip archive from GitHub
`releases <https://github.com/xmikos/qspectrumanalyzer/releases>`_ page
3. after you connect your SDR device, you have to run `Zadig <http://zadig.akeo.ie>`_ to install USB drivers
You can also install QSpectrumAnalyzer manually from `PyPI <https://pypi.python.org>`_:
1. install Python 3.6.x (64-bit) from `python.org <https://www.python.org>`_ and add Python to PATH
2. install `SoapySDR <https://github.com/pothosware/SoapySDR/wiki>`_ libraries and drivers
(bundled as part of Pothos SDR installer: `download <http://downloads.myriadrf.org/builds/PothosSDR/?C=M;O=D>`_)
3. Open ``cmd.exe`` and run::
pip install PyQt5
pip install QSpectrumAnalyzer
You should then be able to run it with ``qspectrumanalyzer`` (or ``python -m qspectrumanalyzer``
if it doesn't work for you).
Todo:
-----
- save FFT history (allow big waterfall plot saved to file)
- automatic peak detection / highlighting
- display average noise level
- frequency markers / bookmarks with notes (even importing / exporting .csv file with
- frequency markers / bookmarks with notes (even importing and exporting .csv file with
predefined channels, etc.)

View File

@ -1,7 +1,7 @@
[Desktop Entry]
Name=QSpectrumAnalyzer
GenericName=Spectrum analyzer
Comment=Spectrum analyzer for RTL-SDR (GUI for rtl_power based on PyQtGraph)
Comment=Spectrum analyzer for multiple SDR platforms (PyQtGraph based GUI for soapy_power, hackrf_sweep, rtl_power, rx_power and other backends)
Exec=qspectrumanalyzer
Icon=qspectrumanalyzer
StartupNotify=true

BIN
qspectrumanalyzer.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

457
qspectrumanalyzer/__main__.py Executable file → Normal file
View File

@ -1,167 +1,47 @@
#!/usr/bin/env python
import sys, signal, time
import sys, os, signal, time, argparse
from PyQt4 import QtCore, QtGui
from Qt import QtCore, QtGui, QtWidgets
from qspectrumanalyzer import backends
from qspectrumanalyzer.version import __version__
from qspectrumanalyzer.backend import RtlPowerThread, RtlPowerFftwThread, SoapyPowerThread, RxPowerThread, HackRFSweepThread
from qspectrumanalyzer.data import DataStorage
from qspectrumanalyzer.plot import SpectrumPlotWidget, WaterfallPlotWidget
from qspectrumanalyzer.utils import color_to_str, str_to_color
from qspectrumanalyzer.utils import str_to_color, human_time
from qspectrumanalyzer.settings import QSpectrumAnalyzerSettings
from qspectrumanalyzer.smoothing import QSpectrumAnalyzerSmoothing
from qspectrumanalyzer.persistence import QSpectrumAnalyzerPersistence
from qspectrumanalyzer.colors import QSpectrumAnalyzerColors
from qspectrumanalyzer.baseline import QSpectrumAnalyzerBaseline
from qspectrumanalyzer.ui_qspectrumanalyzer_settings import Ui_QSpectrumAnalyzerSettings
from qspectrumanalyzer.ui_qspectrumanalyzer_smooth import Ui_QSpectrumAnalyzerSmooth
from qspectrumanalyzer.ui_qspectrumanalyzer_persistence import Ui_QSpectrumAnalyzerPersistence
from qspectrumanalyzer.ui_qspectrumanalyzer_colors import Ui_QSpectrumAnalyzerColors
from qspectrumanalyzer.ui_qspectrumanalyzer import Ui_QSpectrumAnalyzerMainWindow
debug = False
# Allow CTRL+C and/or SIGTERM to kill us (PyQt blocks it otherwise)
signal.signal(signal.SIGINT, signal.SIG_DFL)
signal.signal(signal.SIGTERM, signal.SIG_DFL)
class QSpectrumAnalyzerSettings(QtGui.QDialog, Ui_QSpectrumAnalyzerSettings):
"""QSpectrumAnalyzer settings dialog"""
def __init__(self, parent=None):
# Initialize UI
super().__init__(parent)
self.setupUi(self)
# Load settings
settings = QtCore.QSettings()
self.executableEdit.setText(settings.value("executable", "soapy_power"))
self.waterfallHistorySizeSpinBox.setValue(settings.value("waterfall_history_size", 100, int))
self.deviceEdit.setText(settings.value("device", ""))
self.sampleRateSpinBox.setValue(settings.value("sample_rate", 2560000, int))
backend = settings.value("backend", "soapy_power")
self.backendComboBox.blockSignals(True)
i = self.backendComboBox.findText(backend)
if i == -1:
self.backendComboBox.setCurrentIndex(0)
else:
self.backendComboBox.setCurrentIndex(i)
self.backendComboBox.blockSignals(False)
@QtCore.pyqtSlot()
def on_executableButton_clicked(self):
"""Open file dialog when button is clicked"""
filename = QtGui.QFileDialog.getOpenFileName(self, self.tr("Select executable - QSpectrumAnalyzer"))
if filename:
self.executableEdit.setText(filename)
@QtCore.pyqtSlot(str)
def on_backendComboBox_currentIndexChanged(self, text):
"""Change executable when backend is changed"""
self.executableEdit.setText(text)
self.deviceEdit.setText("")
if text == "hackrf_sweep":
self.sampleRateSpinBox.setMinimum(20000000)
self.sampleRateSpinBox.setMaximum(20000000)
self.sampleRateSpinBox.setValue(20000000)
else:
self.sampleRateSpinBox.setMinimum(0)
self.sampleRateSpinBox.setMaximum(25000000)
self.sampleRateSpinBox.setValue(2560000)
def accept(self):
"""Save settings when dialog is accepted"""
settings = QtCore.QSettings()
settings.setValue("executable", self.executableEdit.text())
settings.setValue("waterfall_history_size", self.waterfallHistorySizeSpinBox.value())
settings.setValue("device", self.deviceEdit.text())
settings.setValue("sample_rate", self.sampleRateSpinBox.value())
settings.setValue("backend", self.backendComboBox.currentText())
QtGui.QDialog.accept(self)
class QSpectrumAnalyzerSmooth(QtGui.QDialog, Ui_QSpectrumAnalyzerSmooth):
"""QSpectrumAnalyzer spectrum smoothing dialog"""
def __init__(self, parent=None):
# Initialize UI
super().__init__(parent)
self.setupUi(self)
# Load settings
settings = QtCore.QSettings()
self.windowLengthSpinBox.setValue(settings.value("smooth_length", 11, int))
window_function = settings.value("smooth_window", "hanning")
i = self.windowFunctionComboBox.findText(window_function)
if i == -1:
self.windowFunctionComboBox.setCurrentIndex(0)
else:
self.windowFunctionComboBox.setCurrentIndex(i)
def accept(self):
"""Save settings when dialog is accepted"""
settings = QtCore.QSettings()
settings.setValue("smooth_length", self.windowLengthSpinBox.value())
settings.setValue("smooth_window", self.windowFunctionComboBox.currentText())
QtGui.QDialog.accept(self)
class QSpectrumAnalyzerPersistence(QtGui.QDialog, Ui_QSpectrumAnalyzerPersistence):
"""QSpectrumAnalyzer spectrum persistence dialog"""
def __init__(self, parent=None):
# Initialize UI
super().__init__(parent)
self.setupUi(self)
# Load settings
settings = QtCore.QSettings()
self.persistenceLengthSpinBox.setValue(settings.value("persistence_length", 5, int))
decay_function = settings.value("persistence_decay", "exponential")
i = self.decayFunctionComboBox.findText(decay_function)
if i == -1:
self.decayFunctionComboBox.setCurrentIndex(0)
else:
self.decayFunctionComboBox.setCurrentIndex(i)
def accept(self):
"""Save settings when dialog is accepted"""
settings = QtCore.QSettings()
settings.setValue("persistence_length", self.persistenceLengthSpinBox.value())
settings.setValue("persistence_decay", self.decayFunctionComboBox.currentText())
QtGui.QDialog.accept(self)
class QSpectrumAnalyzerColors(QtGui.QDialog, Ui_QSpectrumAnalyzerColors):
"""QSpectrumAnalyzer colors dialog"""
def __init__(self, parent=None):
# Initialize UI
super().__init__(parent)
self.setupUi(self)
# Load settings
settings = QtCore.QSettings()
self.mainColorButton.setColor(str_to_color(settings.value("main_color", "255, 255, 0, 255")))
self.peakHoldMaxColorButton.setColor(str_to_color(settings.value("peak_hold_max_color", "255, 0, 0, 255")))
self.peakHoldMinColorButton.setColor(str_to_color(settings.value("peak_hold_min_color", "0, 0, 255, 255")))
self.averageColorButton.setColor(str_to_color(settings.value("average_color", "0, 255, 255, 255")))
self.persistenceColorButton.setColor(str_to_color(settings.value("persistence_color", "0, 255, 0, 255")))
def accept(self):
"""Save settings when dialog is accepted"""
settings = QtCore.QSettings()
settings.setValue("main_color", color_to_str(self.mainColorButton.color()))
settings.setValue("peak_hold_max_color", color_to_str(self.peakHoldMaxColorButton.color()))
settings.setValue("peak_hold_min_color", color_to_str(self.peakHoldMinColorButton.color()))
settings.setValue("average_color", color_to_str(self.averageColorButton.color()))
settings.setValue("persistence_color", color_to_str(self.persistenceColorButton.color()))
QtGui.QDialog.accept(self)
class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWindow):
class QSpectrumAnalyzerMainWindow(QtWidgets.QMainWindow, Ui_QSpectrumAnalyzerMainWindow):
"""QSpectrumAnalyzer main window"""
def __init__(self, parent=None):
# Initialize UI
super().__init__(parent)
self.setupUi(self)
# Set window icon
icon_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "qspectrumanalyzer.svg")
self.setWindowIcon(QtGui.QIcon(icon_path))
# Create progress bar
self.progressbar = QtWidgets.QProgressBar()
self.progressbar.setMaximumWidth(250)
self.progressbar.setVisible(False)
self.statusbar.addPermanentWidget(self.progressbar)
# Create plot widgets and update UI
self.spectrumPlotWidget = SpectrumPlotWidget(self.mainPlotLayout)
self.waterfallPlotWidget = WaterfallPlotWidget(self.waterfallPlotLayout, self.histogramPlotLayout)
@ -170,9 +50,14 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
self.spectrumPlotWidget.plot.setXLink(self.waterfallPlotWidget.plot)
# Setup power thread and connect signals
self.update_status_timer = QtCore.QTimer()
self.update_status_timer.timeout.connect(self.update_status)
self.prev_sweep_time = None
self.prev_data_timestamp = None
self.start_timestamp = None
self.data_storage = None
self.power_thread = None
self.backend = None
self.setup_power_thread()
self.update_buttons()
@ -191,45 +76,66 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
self.data_storage.data_recalculated.connect(self.spectrumPlotWidget.recalculate_plot)
self.data_storage.data_recalculated.connect(self.spectrumPlotWidget.recalculate_persistence)
self.data_storage.history_updated.connect(self.waterfallPlotWidget.update_plot)
self.data_storage.history_recalculated.connect(self.waterfallPlotWidget.recalculate_plot)
self.data_storage.average_updated.connect(self.spectrumPlotWidget.update_average)
self.data_storage.baseline_updated.connect(self.spectrumPlotWidget.update_baseline)
self.data_storage.peak_hold_max_updated.connect(self.spectrumPlotWidget.update_peak_hold_max)
self.data_storage.peak_hold_min_updated.connect(self.spectrumPlotWidget.update_peak_hold_min)
# Setup default values and limits in case that backend is changed
backend = settings.value("backend", "soapy_power")
if backend == "soapy_power":
self.power_thread = SoapyPowerThread(self.data_storage)
elif backend == "rx_power":
self.power_thread = RxPowerThread(self.data_storage)
elif backend == "rtl_power_fftw":
self.power_thread = RtlPowerFftwThread(self.data_storage)
elif backend == "hackrf_sweep":
self.gainSpinBox.setMinimum(0)
self.gainSpinBox.setMaximum(102)
self.gainSpinBox.setValue(40)
self.startFreqSpinBox.setMinimum(0)
self.startFreqSpinBox.setMaximum(7230)
self.startFreqSpinBox.setValue(0)
self.stopFreqSpinBox.setMinimum(0)
self.stopFreqSpinBox.setMaximum(7250)
self.stopFreqSpinBox.setValue(6000)
self.binSizeSpinBox.setMinimum(40)
self.binSizeSpinBox.setMaximum(5000)
self.binSizeSpinBox.setValue(1000)
self.intervalSpinBox.setMinimum(0)
self.intervalSpinBox.setMaximum(0)
self.intervalSpinBox.setValue(0)
self.ppmSpinBox.setMinimum(0)
self.ppmSpinBox.setMaximum(0)
self.ppmSpinBox.setValue(0)
self.cropSpinBox.setMinimum(0)
self.cropSpinBox.setMaximum(0)
self.cropSpinBox.setValue(0)
self.power_thread = HackRFSweepThread(self.data_storage)
else:
self.power_thread = RtlPowerThread(self.data_storage)
try:
backend_module = getattr(backends, backend)
except AttributeError:
backend_module = backends.soapy_power
self.power_thread.powerThreadStarted.connect(self.update_buttons)
self.power_thread.powerThreadStopped.connect(self.update_buttons)
if self.backend is None or backend != self.backend:
self.backend = backend
self.gainSpinBox.setMinimum(backend_module.Info.gain_min)
self.gainSpinBox.setMaximum(backend_module.Info.gain_max)
self.gainSpinBox.setValue(backend_module.Info.gain)
self.startFreqSpinBox.setMinimum(backend_module.Info.start_freq_min)
self.startFreqSpinBox.setMaximum(backend_module.Info.start_freq_max)
self.startFreqSpinBox.setValue(backend_module.Info.start_freq)
self.stopFreqSpinBox.setMinimum(backend_module.Info.stop_freq_min)
self.stopFreqSpinBox.setMaximum(backend_module.Info.stop_freq_max)
self.stopFreqSpinBox.setValue(backend_module.Info.stop_freq)
self.binSizeSpinBox.setMinimum(backend_module.Info.bin_size_min)
self.binSizeSpinBox.setMaximum(backend_module.Info.bin_size_max)
self.binSizeSpinBox.setValue(backend_module.Info.bin_size)
self.intervalSpinBox.setMinimum(backend_module.Info.interval_min)
self.intervalSpinBox.setMaximum(backend_module.Info.interval_max)
self.intervalSpinBox.setValue(backend_module.Info.interval)
self.ppmSpinBox.setMinimum(backend_module.Info.ppm_min)
self.ppmSpinBox.setMaximum(backend_module.Info.ppm_max)
self.ppmSpinBox.setValue(backend_module.Info.ppm)
self.cropSpinBox.setMinimum(backend_module.Info.crop_min)
self.cropSpinBox.setMaximum(backend_module.Info.crop_max)
self.cropSpinBox.setValue(backend_module.Info.crop)
# Setup default values and limits in case that LNB LO is changed
lnb_lo = settings.value("lnb_lo", 0, float) / 1e6
start_freq_min = backend_module.Info.start_freq_min + lnb_lo
start_freq_max = backend_module.Info.start_freq_max + lnb_lo
start_freq = self.startFreqSpinBox.value()
stop_freq_min = backend_module.Info.stop_freq_min + lnb_lo
stop_freq_max = backend_module.Info.stop_freq_max + lnb_lo
stop_freq = self.stopFreqSpinBox.value()
self.startFreqSpinBox.setMinimum(start_freq_min if start_freq_min > 0 else 0)
self.startFreqSpinBox.setMaximum(start_freq_max)
if start_freq < start_freq_min or start_freq > start_freq_max:
self.startFreqSpinBox.setValue(start_freq_min)
self.stopFreqSpinBox.setMinimum(stop_freq_min if stop_freq_min > 0 else 0)
self.stopFreqSpinBox.setMaximum(stop_freq_max)
if stop_freq < stop_freq_min or stop_freq > stop_freq_max:
self.stopFreqSpinBox.setValue(stop_freq_max)
self.power_thread = backend_module.PowerThread(self.data_storage)
self.power_thread.powerThreadStarted.connect(self.on_power_thread_started)
self.power_thread.powerThreadStopped.connect(self.on_power_thread_stopped)
def set_dock_size(self, dock, width, height):
"""Ugly hack for resizing QDockWidget (because it doesn't respect minimumSize / sizePolicy set in Designer)
@ -263,7 +169,7 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
self.stopFreqSpinBox.setValue(settings.value("stop_freq", 108.0, float))
self.binSizeSpinBox.setValue(settings.value("bin_size", 10.0, float))
self.intervalSpinBox.setValue(settings.value("interval", 10.0, float))
self.gainSpinBox.setValue(settings.value("gain", 0, int))
self.gainSpinBox.setValue(settings.value("gain", 0, float))
self.ppmSpinBox.setValue(settings.value("ppm", 0, int))
self.cropSpinBox.setValue(settings.value("crop", 0, int))
self.mainCurveCheckBox.setChecked(settings.value("main_curve", 1, int))
@ -272,6 +178,8 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
self.averageCheckBox.setChecked(settings.value("average", 0, int))
self.smoothCheckBox.setChecked(settings.value("smooth", 0, int))
self.persistenceCheckBox.setChecked(settings.value("persistence", 0, int))
self.baselineCheckBox.setChecked(settings.value("baseline", 0, int))
self.subtractBaselineCheckBox.setChecked(settings.value("subtract_baseline", 0, int))
# Restore window state
if settings.value("window_state"):
@ -311,6 +219,8 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
settings.setValue("average", int(self.averageCheckBox.isChecked()))
settings.setValue("smooth", int(self.smoothCheckBox.isChecked()))
settings.setValue("persistence", int(self.persistenceCheckBox.isChecked()))
settings.setValue("baseline", int(self.baselineCheckBox.isChecked()))
settings.setValue("subtract_baseline", int(self.subtractBaselineCheckBox.isChecked()))
# Save window state and geometry
settings.setValue("window_geometry", self.saveGeometry())
@ -329,32 +239,72 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
def update_data(self, data_storage):
"""Update GUI when new data is received"""
# Show number of hops and how much time did the sweep really take
timestamp = time.time()
sweep_time = timestamp - self.prev_data_timestamp
self.prev_sweep_time = timestamp - self.prev_data_timestamp
self.prev_data_timestamp = timestamp
self.update_status()
self.show_status(
self.tr("Frequency hops: {} | Sweep time: {:.2f} s | FPS: {:.2f}").format(
self.power_thread.params["hops"] or self.tr("N/A"),
sweep_time,
1 / sweep_time
),
timeout=0
)
def update_status(self):
"""Update status bar"""
timestamp = time.time()
status = []
if self.power_thread.params["hops"]:
status.append(self.tr("Frequency hops: {}").format(self.power_thread.params["hops"]))
status.append(self.tr("Total time: {} | Sweep time: {:.2f} s ({:.2f} FPS)").format(
human_time(timestamp - self.start_timestamp),
self.prev_sweep_time,
(1 / self.prev_sweep_time) if self.prev_sweep_time else 0
))
self.show_status(" | ".join(status), timeout=0)
self.update_progress(timestamp - self.prev_data_timestamp)
def update_progress(self, value):
"""Update progress bar"""
value *= 1000
value_max = self.intervalSpinBox.value() * 1000
if value_max < 1000:
return
if value > value_max + 1000:
self.progressbar.setRange(0, 0)
value = value_max
elif value > value_max:
value = value_max
else:
self.progressbar.setRange(0, value_max)
self.progressbar.setValue(value)
def on_power_thread_started(self):
"""Update buttons state when power thread is started"""
self.update_buttons()
self.progressbar.setVisible(True)
def on_power_thread_stopped(self):
"""Update buttons state and status bar when power thread is stopped"""
self.update_buttons()
self.update_status_timer.stop()
self.update_status()
self.progressbar.setVisible(False)
def start(self, single_shot=False):
"""Start power thread"""
settings = QtCore.QSettings()
self.prev_data_timestamp = time.time()
self.data_storage.reset()
self.data_storage.set_smooth(
bool(self.smoothCheckBox.isChecked()),
settings.value("smooth_length", 11, int),
settings.value("smooth_window", "hanning"),
recalculate=False
)
self.prev_sweep_time = 0
self.prev_data_timestamp = time.time()
self.start_timestamp = self.prev_data_timestamp
if self.intervalSpinBox.value() >= 1:
self.progressbar.setRange(0, self.intervalSpinBox.value() * 1000)
else:
self.progressbar.setRange(0, 0)
self.update_progress(0)
self.update_status_timer.start(100)
self.waterfallPlotWidget.history_size = settings.value("waterfall_history_size", 100, int)
self.waterfallPlotWidget.clear_plot()
@ -367,6 +317,8 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
self.spectrumPlotWidget.peak_hold_min_color = str_to_color(settings.value("peak_hold_min_color", "0, 0, 255, 255"))
self.spectrumPlotWidget.average = bool(self.averageCheckBox.isChecked())
self.spectrumPlotWidget.average_color = str_to_color(settings.value("average_color", "0, 255, 255, 255"))
self.spectrumPlotWidget.baseline = bool(self.baselineCheckBox.isChecked())
self.spectrumPlotWidget.baseline_color = str_to_color(settings.value("baseline_color", "255, 0, 255, 255"))
self.spectrumPlotWidget.persistence = bool(self.persistenceCheckBox.isChecked())
self.spectrumPlotWidget.persistence_length = settings.value("persistence_length", 5, int)
self.spectrumPlotWidget.persistence_decay = settings.value("persistence_decay", "exponential")
@ -375,19 +327,35 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
self.spectrumPlotWidget.clear_peak_hold_max()
self.spectrumPlotWidget.clear_peak_hold_min()
self.spectrumPlotWidget.clear_average()
self.spectrumPlotWidget.clear_baseline()
self.spectrumPlotWidget.clear_persistence()
self.data_storage.reset()
self.data_storage.set_smooth(
bool(self.smoothCheckBox.isChecked()),
settings.value("smooth_length", 11, int),
settings.value("smooth_window", "hanning")
)
self.data_storage.set_subtract_baseline(
bool(self.subtractBaselineCheckBox.isChecked()),
settings.value("baseline_file", None)
)
if not self.power_thread.alive:
self.power_thread.setup(float(self.startFreqSpinBox.value()),
self.power_thread.setup(
float(self.startFreqSpinBox.value()),
float(self.stopFreqSpinBox.value()),
float(self.binSizeSpinBox.value()),
interval=float(self.intervalSpinBox.value()),
gain=int(self.gainSpinBox.value()),
gain=float(self.gainSpinBox.value()),
ppm=int(self.ppmSpinBox.value()),
crop=int(self.cropSpinBox.value()) / 100.0,
single_shot=single_shot,
device=settings.value("device", ""),
sample_rate=settings.value("sample_rate", 2560000, int))
sample_rate=settings.value("sample_rate", 2560000, float),
bandwidth=settings.value("bandwidth", 0, float),
lnb_lo=settings.value("lnb_lo", 0, float)
)
self.power_thread.start()
def stop(self):
@ -395,47 +363,47 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
if self.power_thread.alive:
self.power_thread.stop()
@QtCore.pyqtSlot()
@QtCore.Slot()
def on_startButton_clicked(self):
self.start()
@QtCore.pyqtSlot()
@QtCore.Slot()
def on_singleShotButton_clicked(self):
self.start(single_shot=True)
@QtCore.pyqtSlot()
@QtCore.Slot()
def on_stopButton_clicked(self):
self.stop()
@QtCore.pyqtSlot(bool)
@QtCore.Slot(bool)
def on_mainCurveCheckBox_toggled(self, checked):
self.spectrumPlotWidget.main_curve = checked
if self.spectrumPlotWidget.curve.xData is None:
self.spectrumPlotWidget.update_plot(self.data_storage)
self.spectrumPlotWidget.curve.setVisible(checked)
@QtCore.pyqtSlot(bool)
@QtCore.Slot(bool)
def on_peakHoldMaxCheckBox_toggled(self, checked):
self.spectrumPlotWidget.peak_hold_max = checked
if self.spectrumPlotWidget.curve_peak_hold_max.xData is None:
self.spectrumPlotWidget.update_peak_hold_max(self.data_storage)
self.spectrumPlotWidget.curve_peak_hold_max.setVisible(checked)
@QtCore.pyqtSlot(bool)
@QtCore.Slot(bool)
def on_peakHoldMinCheckBox_toggled(self, checked):
self.spectrumPlotWidget.peak_hold_min = checked
if self.spectrumPlotWidget.curve_peak_hold_min.xData is None:
self.spectrumPlotWidget.update_peak_hold_min(self.data_storage)
self.spectrumPlotWidget.curve_peak_hold_min.setVisible(checked)
@QtCore.pyqtSlot(bool)
@QtCore.Slot(bool)
def on_averageCheckBox_toggled(self, checked):
self.spectrumPlotWidget.average = checked
if self.spectrumPlotWidget.curve_average.xData is None:
self.spectrumPlotWidget.update_average(self.data_storage)
self.spectrumPlotWidget.curve_average.setVisible(checked)
@QtCore.pyqtSlot(bool)
@QtCore.Slot(bool)
def on_persistenceCheckBox_toggled(self, checked):
self.spectrumPlotWidget.persistence = checked
if self.spectrumPlotWidget.persistence_curves[0].xData is None:
@ -443,29 +411,52 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
for curve in self.spectrumPlotWidget.persistence_curves:
curve.setVisible(checked)
@QtCore.pyqtSlot(bool)
@QtCore.Slot(bool)
def on_smoothCheckBox_toggled(self, checked):
settings = QtCore.QSettings()
self.data_storage.set_smooth(
checked,
settings.value("smooth_length", 11, int),
settings.value("smooth_window", "hanning"),
recalculate=True
settings.value("smooth_window", "hanning")
)
@QtCore.pyqtSlot()
@QtCore.Slot(bool)
def on_baselineCheckBox_toggled(self, checked):
self.spectrumPlotWidget.baseline = checked
if self.spectrumPlotWidget.curve_baseline.xData is None:
self.spectrumPlotWidget.update_baseline(self.data_storage)
self.spectrumPlotWidget.curve_baseline.setVisible(checked)
@QtCore.Slot(bool)
def on_subtractBaselineCheckBox_toggled(self, checked):
settings = QtCore.QSettings()
self.data_storage.set_subtract_baseline(
checked,
settings.value("baseline_file", None)
)
@QtCore.Slot()
def on_baselineButton_clicked(self):
dialog = QSpectrumAnalyzerBaseline(self)
if dialog.exec_():
settings = QtCore.QSettings()
self.data_storage.set_subtract_baseline(
bool(self.subtractBaselineCheckBox.isChecked()),
settings.value("baseline_file", None)
)
@QtCore.Slot()
def on_smoothButton_clicked(self):
dialog = QSpectrumAnalyzerSmooth(self)
dialog = QSpectrumAnalyzerSmoothing(self)
if dialog.exec_():
settings = QtCore.QSettings()
self.data_storage.set_smooth(
bool(self.smoothCheckBox.isChecked()),
settings.value("smooth_length", 11, int),
settings.value("smooth_window", "hanning"),
recalculate=True
settings.value("smooth_window", "hanning")
)
@QtCore.pyqtSlot()
@QtCore.Slot()
def on_persistenceButton_clicked(self):
prev_persistence_length = self.spectrumPlotWidget.persistence_length
dialog = QSpectrumAnalyzerPersistence(self)
@ -481,7 +472,7 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
else:
self.spectrumPlotWidget.recalculate_persistence(self.data_storage)
@QtCore.pyqtSlot()
@QtCore.Slot()
def on_colorsButton_clicked(self):
dialog = QSpectrumAnalyzerColors(self)
if dialog.exec_():
@ -491,20 +482,21 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
self.spectrumPlotWidget.peak_hold_min_color = str_to_color(settings.value("peak_hold_min_color", "0, 0, 255, 255"))
self.spectrumPlotWidget.average_color = str_to_color(settings.value("average_color", "0, 255, 255, 255"))
self.spectrumPlotWidget.persistence_color = str_to_color(settings.value("persistence_color", "0, 255, 0, 255"))
self.spectrumPlotWidget.baseline_color = str_to_color(settings.value("baseline_color", "255, 0, 255, 255"))
self.spectrumPlotWidget.set_colors()
@QtCore.pyqtSlot()
@QtCore.Slot()
def on_action_Settings_triggered(self):
dialog = QSpectrumAnalyzerSettings(self)
if dialog.exec_():
self.setup_power_thread()
@QtCore.pyqtSlot()
@QtCore.Slot()
def on_action_About_triggered(self):
QtGui.QMessageBox.information(self, self.tr("About - QSpectrumAnalyzer"),
QtWidgets.QMessageBox.information(self, self.tr("About - QSpectrumAnalyzer"),
self.tr("QSpectrumAnalyzer {}").format(__version__))
@QtCore.pyqtSlot()
@QtCore.Slot()
def on_action_Quit_triggered(self):
self.close()
@ -515,12 +507,37 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
def main():
app = QtGui.QApplication(sys.argv)
global debug
# Parse command line arguments
parser = argparse.ArgumentParser(
prog="qspectrumanalyzer",
description="Spectrum analyzer for multiple SDR platforms",
)
parser.add_argument("--debug", action="store_true",
help="detailed debugging messages")
parser.add_argument("--version", action="version",
version="%(prog)s {}".format(__version__))
args, unparsed_args = parser.parse_known_args()
debug = args.debug
try:
# Hide console window on Windows
if sys.platform == 'win32' and not debug:
from qspectrumanalyzer import windows
windows.set_attached_console_visible(False)
# Start PyQt application
app = QtWidgets.QApplication(sys.argv[:1] + unparsed_args)
app.setOrganizationName("QSpectrumAnalyzer")
app.setOrganizationDomain("qspectrumanalyzer.eutopia.cz")
app.setApplicationName("QSpectrumAnalyzer")
window = QSpectrumAnalyzerMainWindow()
sys.exit(app.exec_())
finally:
# Unhide console window on Windows (we don't want to leave zombies behind)
if sys.platform == 'win32' and not debug:
windows.set_attached_console_visible(True)
if __name__ == "__main__":

View File

@ -1,566 +0,0 @@
import subprocess, math, pprint, re
import numpy as np
from PyQt4 import QtCore
import struct
class BasePowerThread(QtCore.QThread):
"""Thread which runs Power Spectral Density acquisition and calculation process"""
powerThreadStarted = QtCore.pyqtSignal()
powerThreadStopped = QtCore.pyqtSignal()
def __init__(self, data_storage, parent=None):
super().__init__(parent)
self.data_storage = data_storage
self.alive = False
self.process = None
def stop(self):
"""Stop power process thread"""
self.process_stop()
self.alive = False
self.wait()
def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1,
ppm=0, crop=0, single_shot=False, device=0, sample_rate=2560000):
"""Setup power process params"""
raise NotImplementedError
def process_start(self):
"""Start power process"""
raise NotImplementedError
def process_stop(self):
"""Terminate power process"""
if self.process:
try:
self.process.terminate()
except ProcessLookupError:
pass
self.process.wait()
self.process = None
def parse_output(self, line):
"""Parse one line of output from power process"""
raise NotImplementedError
def run(self):
"""Power process thread main loop"""
self.process_start()
self.alive = True
self.powerThreadStarted.emit()
for line in self.process.stdout:
if not self.alive:
break
self.parse_output(line)
self.process_stop()
self.alive = False
self.powerThreadStopped.emit()
class RxPowerThread(BasePowerThread):
"""Thread which runs rx_power process"""
def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1,
ppm=0, crop=0, single_shot=False, device=0, sample_rate=2560000):
"""Setup rx_power params"""
self.params = {
"start_freq": start_freq,
"stop_freq": stop_freq,
"bin_size": bin_size,
"interval": interval,
"device": device,
"hops": 0,
"gain": gain,
"ppm": ppm,
"crop": crop,
"single_shot": single_shot
}
self.databuffer = {}
self.last_timestamp = ""
print("rx_power params:")
pprint.pprint(self.params)
print()
def process_start(self):
"""Start rx_power process"""
if not self.process and self.params:
settings = QtCore.QSettings()
cmdline = [
settings.value("executable", "rx_power"),
"-f", "{}M:{}M:{}k".format(self.params["start_freq"],
self.params["stop_freq"],
self.params["bin_size"]),
"-i", "{}".format(self.params["interval"]),
"-d", "{}".format(self.params["device"]),
"-p", "{}".format(self.params["ppm"]),
"-c", "{}".format(self.params["crop"])
]
if self.params["gain"] >= 0:
cmdline.extend(["-g", "{}".format(self.params["gain"])])
if self.params["single_shot"]:
cmdline.append("-1")
self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
universal_newlines=True)
def parse_output(self, line):
"""Parse one line of output from rx_power"""
line = [col.strip() for col in line.split(",")]
timestamp = " ".join(line[:2])
start_freq = int(line[2])
stop_freq = int(line[3])
step = float(line[4])
samples = float(line[5])
x_axis = list(np.arange(start_freq, stop_freq, step))
y_axis = [float(y) for y in line[6:]]
if len(x_axis) != len(y_axis):
print("ERROR: len(x_axis) != len(y_axis), use newer version of rx_power!")
if len(x_axis) > len(y_axis):
print("Trimming x_axis...")
x_axis = x_axis[:len(y_axis)]
else:
print("Trimming y_axis...")
y_axis = y_axis[:len(x_axis)]
if timestamp != self.last_timestamp:
self.last_timestamp = timestamp
self.databuffer = {"timestamp": timestamp,
"x": x_axis,
"y": y_axis}
else:
self.databuffer["x"].extend(x_axis)
self.databuffer["y"].extend(y_axis)
# This have to be stupid like this to be compatible with old broken version of rx_power. Right way is:
# if stop_freq == self.params["stop_freq"] * 1e6:
if stop_freq > (self.params["stop_freq"] * 1e6) - step:
self.data_storage.update(self.databuffer)
class RtlPowerThread(BasePowerThread):
"""Thread which runs rtl_power process"""
def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1,
ppm=0, crop=0, single_shot=False, device=0, sample_rate=2560000):
"""Setup rtl_power params"""
if bin_size > 2800:
bin_size = 2800
self.params = {
"start_freq": start_freq,
"stop_freq": stop_freq,
"bin_size": bin_size,
"interval": interval,
"device": device,
"hops": 0,
"gain": gain,
"ppm": ppm,
"crop": crop,
"single_shot": single_shot
}
self.databuffer = {}
self.last_timestamp = ""
print("rtl_power params:")
pprint.pprint(self.params)
print()
def process_start(self):
"""Start rtl_power process"""
if not self.process and self.params:
settings = QtCore.QSettings()
cmdline = [
settings.value("executable", "rtl_power"),
"-f", "{}M:{}M:{}k".format(self.params["start_freq"],
self.params["stop_freq"],
self.params["bin_size"]),
"-i", "{}".format(self.params["interval"]),
"-d", "{}".format(self.params["device"]),
"-p", "{}".format(self.params["ppm"]),
"-c", "{}".format(self.params["crop"])
]
if self.params["gain"] >= 0:
cmdline.extend(["-g", "{}".format(self.params["gain"])])
if self.params["single_shot"]:
cmdline.append("-1")
self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
universal_newlines=True)
def parse_output(self, line):
"""Parse one line of output from rtl_power"""
line = [col.strip() for col in line.split(",")]
timestamp = " ".join(line[:2])
start_freq = int(line[2])
stop_freq = int(line[3])
step = float(line[4])
samples = float(line[5])
x_axis = list(np.arange(start_freq, stop_freq, step))
y_axis = [float(y) for y in line[6:]]
if len(x_axis) != len(y_axis):
print("ERROR: len(x_axis) != len(y_axis), use newer version of rtl_power!")
if len(x_axis) > len(y_axis):
print("Trimming x_axis...")
x_axis = x_axis[:len(y_axis)]
else:
print("Trimming y_axis...")
y_axis = y_axis[:len(x_axis)]
if timestamp != self.last_timestamp:
self.last_timestamp = timestamp
self.databuffer = {"timestamp": timestamp,
"x": x_axis,
"y": y_axis}
else:
self.databuffer["x"].extend(x_axis)
self.databuffer["y"].extend(y_axis)
# This have to be stupid like this to be compatible with old broken version of rtl_power. Right way is:
# if stop_freq == self.params["stop_freq"] * 1e6:
if stop_freq > (self.params["stop_freq"] * 1e6) - step:
self.data_storage.update(self.databuffer)
class RtlPowerFftwThread(BasePowerThread):
"""Thread which runs rtl_power_fftw process"""
def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1,
ppm=0, crop=0, single_shot=False, device=0, sample_rate=2560000):
"""Setup rtl_power_fftw params"""
crop = crop * 100
overlap = crop * 2
freq_range = stop_freq * 1e6 - start_freq * 1e6
min_overhang = sample_rate * overlap * 0.01
hops = math.ceil((freq_range - min_overhang) / (sample_rate - min_overhang))
overhang = (hops * sample_rate - freq_range) / (hops - 1) if hops > 1 else 0
if bin_size > 2800:
bin_size = 2800
bins = math.ceil(sample_rate / (bin_size * 1e3))
crop_freq = sample_rate * crop * 0.01
self.params = {
"start_freq": start_freq,
"stop_freq": stop_freq,
"freq_range": freq_range,
"device": device,
"sample_rate": sample_rate,
"bin_size": bin_size,
"bins": bins,
"interval": interval,
"hops": hops,
"time": interval / hops,
"gain": gain * 10,
"ppm": ppm,
"crop": crop,
"overlap": overlap,
"min_overhang": min_overhang,
"overhang": overhang,
"single_shot": single_shot
}
self.freqs = [self.get_hop_freq(hop) for hop in range(hops)]
self.freqs_crop = [(f[0] + crop_freq, f[1] - crop_freq) for f in self.freqs]
self.databuffer = {"timestamp": [], "x": [], "y": []}
self.databuffer_hop = {"timestamp": [], "x": [], "y": []}
self.hop = 0
self.prev_line = ""
print("rtl_power_fftw params:")
pprint.pprint(self.params)
print()
def get_hop_freq(self, hop):
"""Get start and stop frequency for particular hop"""
start_freq = self.params["start_freq"] * 1e6 + (self.params["sample_rate"] - self.params["overhang"]) * hop
stop_freq = start_freq + self.params["sample_rate"] - (self.params["sample_rate"] / self.params["bins"])
return (start_freq, stop_freq)
def process_start(self):
"""Start rtl_power_fftw process"""
if not self.process and self.params:
settings = QtCore.QSettings()
cmdline = [
settings.value("executable", "rtl_power_fftw"),
"-f", "{}M:{}M".format(self.params["start_freq"],
self.params["stop_freq"]),
"-b", "{}".format(self.params["bins"]),
"-t", "{}".format(self.params["time"]),
"-d", "{}".format(self.params["device"]),
"-r", "{}".format(self.params["sample_rate"]),
"-p", "{}".format(self.params["ppm"]),
]
if self.params["gain"] >= 0:
cmdline.extend(["-g", "{}".format(self.params["gain"])])
if self.params["overlap"] > 0:
cmdline.extend(["-o", "{}".format(self.params["overlap"])])
if not self.params["single_shot"]:
cmdline.append("-c")
self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
universal_newlines=True)
def parse_output(self, line):
"""Parse one line of output from rtl_power_fftw"""
line = line.strip()
# One empty line => new hop
if not line and self.prev_line:
self.hop += 1
self.databuffer["x"].extend(self.databuffer_hop["x"])
self.databuffer["y"].extend(self.databuffer_hop["y"])
self.databuffer_hop = {"timestamp": [], "x": [], "y": []}
# Two empty lines => new set
elif not line and not self.prev_line:
self.hop = 0
self.data_storage.update(self.databuffer)
self.databuffer = {"timestamp": [], "x": [], "y": []}
# Get timestamp for new hop and set
elif line.startswith("# Acquisition start:"):
timestamp = line.split(":", 1)[1].strip()
if not self.databuffer_hop["timestamp"]:
self.databuffer_hop["timestamp"] = timestamp
if not self.databuffer["timestamp"]:
self.databuffer["timestamp"] = timestamp
# Skip other comments
elif line.startswith("#"):
pass
# Parse frequency and power
elif line[0].isdigit():
freq, power = line.split()
freq, power = float(freq), float(power)
start_freq, stop_freq = self.freqs_crop[self.hop]
# Apply cropping
if freq >= start_freq and freq <= stop_freq:
# Skip overlapping frequencies
if not self.databuffer["x"] or freq > self.databuffer["x"][-1]:
#print(" {:.3f} MHz".format(freq / 1e6))
self.databuffer_hop["x"].append(freq)
self.databuffer_hop["y"].append(power)
else:
#print(" Overlapping {:.3f} MHz".format(freq / 1e6))
pass
else:
#print(" Cropping {:.3f} MHz".format(freq / 1e6))
pass
self.prev_line = line
class SoapyPowerThread(BasePowerThread):
"""Thread which runs soapy_power process"""
re_two_floats = re.compile(r'^[-+\d.eE]+\s+[-+\d.eE]+$')
def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1,
ppm=0, crop=0, single_shot=False, device="", sample_rate=2560000):
"""Setup soapy_power params"""
self.params = {
"start_freq": start_freq,
"stop_freq": stop_freq,
"device": device,
"sample_rate": sample_rate,
"bin_size": bin_size,
"interval": interval,
"hops": 0,
"gain": gain * 10,
"ppm": ppm,
"crop": crop * 100,
"single_shot": single_shot
}
self.databuffer = {"timestamp": [], "x": [], "y": []}
self.databuffer_hop = {"timestamp": [], "x": [], "y": []}
self.hop = 0
self.run = 0
self.prev_line = ""
print("soapy_power params:")
pprint.pprint(self.params)
print()
def process_start(self):
"""Start soapy_power process"""
if not self.process and self.params:
settings = QtCore.QSettings()
cmdline = [
settings.value("executable", "soapy_power"),
"-f", "{}M:{}M".format(self.params["start_freq"],
self.params["stop_freq"]),
"-B", "{}k".format(self.params["bin_size"]),
"-T", "{}".format(self.params["interval"]),
"-d", "{}".format(self.params["device"]),
"-r", "{}".format(self.params["sample_rate"]),
"-p", "{}".format(self.params["ppm"]),
]
if self.params["gain"] >= 0:
cmdline.extend(["-g", "{}".format(self.params["gain"])])
if self.params["crop"] > 0:
cmdline.extend(["-k", "{}".format(self.params["crop"])])
if not self.params["single_shot"]:
cmdline.append("-c")
self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
universal_newlines=True)
def parse_output(self, line):
"""Parse one line of output from soapy_power"""
line = line.strip()
# One empty line => new hop
if not line and self.prev_line:
self.hop += 1
print(' => HOP:', self.hop)
self.databuffer["x"].extend(self.databuffer_hop["x"])
self.databuffer["y"].extend(self.databuffer_hop["y"])
self.databuffer_hop = {"timestamp": [], "x": [], "y": []}
# Two empty lines => new set
elif not line and not self.prev_line:
self.hop = 0
self.run += 1
print(' * RUN:', self.run)
self.data_storage.update(self.databuffer)
self.databuffer = {"timestamp": [], "x": [], "y": []}
# Get timestamp for new hop and set
elif line.startswith("# Acquisition start:"):
timestamp = line.split(":", 1)[1].strip()
if not self.databuffer_hop["timestamp"]:
self.databuffer_hop["timestamp"] = timestamp
if not self.databuffer["timestamp"]:
self.databuffer["timestamp"] = timestamp
# Skip other comments
elif line.startswith("#"):
pass
# Parse frequency and power
elif self.re_two_floats.match(line):
try:
freq, power = line.split()
except ValueError:
return
freq, power = float(freq), float(power)
self.databuffer_hop["x"].append(freq)
self.databuffer_hop["y"].append(power)
self.prev_line = line
class HackRFSweepThread(BasePowerThread):
"""Thread which runs hackrf_sweep process"""
def setup(self, start_freq=0, stop_freq=6000, bin_size=1000,
interval=0.0, gain=40, ppm=0, crop=0, single_shot=False,
device_index=0, sample_rate=20000000):
"""Setup hackrf_sweep params"""
# theoretically we can support bins smaller than 40 kHz, but it is
# unlikely to result in acceptable performance
if bin_size < 40:
bin_size = 40
if bin_size > 5000:
bin_size = 5000
# We only support whole numbers of steps with bandwidth equal to the
# sample rate.
step_bandwidth = sample_rate / 1000000
total_bandwidth = stop_freq - start_freq
step_count = 1 + (total_bandwidth - 1) // step_bandwidth
total_bandwidth = step_count * step_bandwidth
stop_freq = start_freq + total_bandwidth
# distribute gain between two analog gain stages
if gain < 0:
gain = 0
if gain > 102:
gain = 102
lna_gain = 8 * (gain // 18)
vga_gain = 2 * ((gain - lna_gain) // 2)
self.params = {
"start_freq": start_freq, # MHz
"stop_freq": stop_freq, # MHz
"hops": 0,
"device_index": 0,
"sample_rate": 20e6, # sps
"bin_size": bin_size, # kHz
"interval": 0, # seconds
"gain": gain,
"lna_gain": lna_gain,
"vga_gain": vga_gain,
"ppm": 0,
"crop": 0,
"single_shot": single_shot
}
self.databuffer = {"timestamp": [], "x": [], "y": []}
print("hackrf_sweep params:")
pprint.pprint(self.params)
print()
def process_start(self):
"""Start hackrf_sweep process"""
if not self.process and self.params:
settings = QtCore.QSettings()
cmdline = [
settings.value("executable", "hackrf_sweep"),
"-f", "{}:{}".format(int(self.params["start_freq"]),
int(self.params["stop_freq"])),
"-B",
"-w", "{}".format(int(self.params["bin_size"]*1000)),
"-l", "{}".format(int(self.params["lna_gain"])),
"-g", "{}".format(int(self.params["vga_gain"])),
]
if self.params["single_shot"]:
cmdline.append("-1")
self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
universal_newlines=False)
def parse_output(self, buf):
"""Parse one buf of output from hackrf_sweep"""
(low_edge, high_edge) = struct.unpack('QQ', buf[:16])
data = np.fromstring(buf[16:], dtype='<f4')
step = (high_edge - low_edge) / len(data)
if (low_edge//1000000) <= (self.params["start_freq"]):
# Reset databuffer at the start of each sweep even if we somehow
# did not complete the previous sweep.
self.databuffer = {"timestamp": [], "x": [], "y": []}
x_axis = list(np.arange(low_edge + step/2, high_edge, step))
self.databuffer["x"].extend(x_axis)
for i in range(len(data)):
self.databuffer["y"].append(data[i])
if (high_edge / 1e6) >= (self.params["stop_freq"]):
# We've reached the end of a pass, so sort and display it.
sorted_data = sorted(zip(self.databuffer["x"], self.databuffer["y"]))
self.databuffer["x"], self.databuffer["y"] = [list(x) for x in zip(*sorted_data)]
self.data_storage.update(self.databuffer)
def run(self):
"""hackrf_sweep thread main loop"""
self.process_start()
self.alive = True
self.powerThreadStarted.emit()
while self.alive:
buf = self.process.stdout.read(4)
if buf:
(record_length,) = struct.unpack('I', buf)
buf = self.process.stdout.read(record_length)
if buf:
self.parse_output(buf)
self.process_stop()
self.alive = False
self.powerThreadStopped.emit()

View File

@ -0,0 +1,117 @@
import os, threading, shlex
from Qt import QtCore
from qspectrumanalyzer import subprocess
class BaseInfo:
"""Default device metadata"""
sample_rate_min = 0
sample_rate_max = 3200000
sample_rate = 2560000
bandwidth_min = 0
bandwidth_max = 0
bandwidth = 0
gain_min = -1
gain_max = 49.6
gain = 37
start_freq_min = 0
start_freq_max = 2200
start_freq = 87
stop_freq_min = 0
stop_freq_max = 2200
stop_freq = 108
bin_size_min = 0
bin_size_max = 2800
bin_size = 10
interval_min = 0
interval_max = 3600
interval = 1
ppm_min = -999
ppm_max = 999
ppm = 0
crop_min = 0
crop_max = 99
crop = 0
additional_params = ''
help_device = None
@classmethod
def help_params(cls, executable):
cmdline = shlex.split(executable)
try:
text = subprocess.check_output(cmdline + ['-h'], universal_newlines=True,
stderr=subprocess.STDOUT, env=dict(os.environ, COLUMNS='125'),
console=False)
except subprocess.CalledProcessError as e:
text = e.output
except OSError:
text = '{} executable not found!'.format(executable)
return text
class BasePowerThread(QtCore.QThread):
"""Thread which runs Power Spectral Density acquisition and calculation process"""
powerThreadStarted = QtCore.Signal()
powerThreadStopped = QtCore.Signal()
def __init__(self, data_storage, parent=None):
super().__init__(parent)
self.data_storage = data_storage
self.alive = False
self.process = None
self._shutdown_lock = threading.Lock()
def stop(self):
"""Stop power process thread"""
self.process_stop()
self.alive = False
self.wait()
def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1, ppm=0, crop=0,
single_shot=False, device=0, sample_rate=2560000, bandwidth=0, lnb_lo=0):
"""Setup power process params"""
raise NotImplementedError
def process_start(self):
"""Start power process"""
raise NotImplementedError
def process_stop(self):
"""Terminate power process"""
with self._shutdown_lock:
if self.process:
if self.process.poll() is None:
try:
self.process.terminate()
except ProcessLookupError:
pass
self.process.wait()
self.process = None
def parse_output(self, line):
"""Parse one line of output from power process"""
raise NotImplementedError
def run(self):
"""Power process thread main loop"""
self.process_start()
self.alive = True
self.powerThreadStarted.emit()
for line in self.process.stdout:
if not self.alive:
break
self.parse_output(line)
self.process_stop()
self.alive = False
self.powerThreadStopped.emit()
# Build list of all backends
__all__ = ['soapy_power', 'hackrf_sweep', 'rtl_power', 'rtl_power_fftw', 'rx_power']
# Import all backends
from qspectrumanalyzer.backends import soapy_power, hackrf_sweep, rtl_power, rtl_power_fftw, rx_power

View File

@ -0,0 +1,171 @@
import struct, shlex, sys, time
import numpy as np
from Qt import QtCore
from qspectrumanalyzer import subprocess
from qspectrumanalyzer.backends import BaseInfo, BasePowerThread
class Info(BaseInfo):
"""hackrf_sweep device metadata"""
sample_rate_min = 20000000
sample_rate_max = 20000000
sample_rate = 20000000
gain_min = -1
gain_max = 102
gain = 40
start_freq_min = 0
start_freq_max = 7230
start_freq = 0
stop_freq_min = 0
stop_freq_max = 7250
stop_freq = 6000
bin_size_min = 3
bin_size_max = 5000
bin_size = 1000
interval = 0
ppm_min = 0
ppm_max = 0
ppm = 0
crop_min = 0
crop_max = 0
crop = 0
class PowerThread(BasePowerThread):
"""Thread which runs hackrf_sweep process"""
def setup(self, start_freq=0, stop_freq=6000, bin_size=1000,
interval=0.0, gain=40, ppm=0, crop=0, single_shot=False,
device=0, sample_rate=20000000, bandwidth=0, lnb_lo=0):
"""Setup hackrf_sweep params"""
# Small bin sizes (<40 kHz) are only suitable with an arbitrarily
# reduced sweep interval. Bin sizes smaller than 3 kHz showed to be
# infeasible also in these cases.
if bin_size < 3:
bin_size = 3
if bin_size > 5000:
bin_size = 5000
# We only support whole numbers of steps with bandwidth equal to the
# sample rate.
step_bandwidth = sample_rate / 1000000
total_bandwidth = stop_freq - start_freq
step_count = 1 + (total_bandwidth - 1) // step_bandwidth
total_bandwidth = step_count * step_bandwidth
stop_freq = start_freq + total_bandwidth
# distribute gain between two analog gain stages
if gain > 102:
gain = 102
lna_gain = 8 * (gain // 18) if gain >= 0 else 0
vga_gain = 2 * ((gain - lna_gain) // 2) if gain >= 0 else 0
self.params = {
"start_freq": start_freq, # MHz
"stop_freq": stop_freq, # MHz
"hops": 0,
"device": 0,
"sample_rate": 20e6, # sps
"bin_size": bin_size, # kHz
"interval": interval, # seconds
"gain": gain,
"lna_gain": lna_gain,
"vga_gain": vga_gain,
"ppm": 0,
"crop": 0,
"single_shot": single_shot
}
self.lnb_lo = lnb_lo
self.databuffer = {"timestamp": [], "x": [], "y": []}
self.lastsweep = 0
self.interval = interval
def process_start(self):
"""Start hackrf_sweep process"""
if not self.process and self.params:
settings = QtCore.QSettings()
cmdline = shlex.split(settings.value("executable", "hackrf_sweep"))
cmdline.extend([
"-f", "{}:{}".format(int(self.params["start_freq"] - self.lnb_lo / 1e6),
int(self.params["stop_freq"] - self.lnb_lo / 1e6)),
"-B",
"-w", "{}".format(int(self.params["bin_size"] * 1000)),
])
if self.params["gain"] >= 0:
cmdline.extend([
"-l", "{}".format(int(self.params["lna_gain"])),
"-g", "{}".format(int(self.params["vga_gain"])),
])
if self.params["single_shot"]:
cmdline.append("-1")
additional_params = settings.value("params", Info.additional_params)
if additional_params:
cmdline.extend(shlex.split(additional_params))
print('Starting backend:')
print(' '.join(cmdline))
print()
self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
universal_newlines=False, console=False)
def parse_output(self, buf):
"""Parse one buf of output from hackrf_sweep"""
(low_edge, high_edge) = struct.unpack('QQ', buf[:16])
data = np.fromstring(buf[16:], dtype='<f4')
step = (high_edge - low_edge) / len(data)
if (low_edge // 1000000) <= (self.params["start_freq"] - self.lnb_lo / 1e6):
# Reset databuffer at the start of each sweep even if we somehow
# did not complete the previous sweep.
self.databuffer = {"timestamp": [], "x": [], "y": []}
x_axis = list(np.arange(low_edge + self.lnb_lo + step / 2, high_edge + self.lnb_lo, step))
self.databuffer["x"].extend(x_axis)
for i in range(len(data)):
self.databuffer["y"].append(data[i])
if (high_edge / 1e6) >= (self.params["stop_freq"] - self.lnb_lo / 1e6):
# We've reached the end of a pass. If it went too fast for our sweep interval, ignore it
t_finish = time.time()
if (t_finish < self.lastsweep + self.interval):
return
self.lastsweep = t_finish
# otherwise sort and display the data.
sorted_data = sorted(zip(self.databuffer["x"], self.databuffer["y"]))
self.databuffer["x"], self.databuffer["y"] = [list(x) for x in zip(*sorted_data)]
self.data_storage.update(self.databuffer)
def run(self):
"""hackrf_sweep thread main loop"""
self.process_start()
self.alive = True
self.powerThreadStarted.emit()
while self.alive:
try:
buf = self.process.stdout.read(4)
except AttributeError as e:
print(e, file=sys.stderr)
continue
if buf:
(record_length,) = struct.unpack('I', buf)
try:
buf = self.process.stdout.read(record_length)
except AttributeError as e:
print(e, file=sys.stderr)
continue
if buf:
self.parse_output(buf)
else:
break
else:
break
self.process_stop()
self.alive = False
self.powerThreadStopped.emit()

View File

@ -0,0 +1,104 @@
import shlex
import numpy as np
from Qt import QtCore
from qspectrumanalyzer import subprocess
from qspectrumanalyzer.backends import BaseInfo, BasePowerThread
class Info(BaseInfo):
"""rtl_power device metadata"""
pass
class PowerThread(BasePowerThread):
"""Thread which runs rtl_power process"""
def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1, ppm=0, crop=0,
single_shot=False, device=0, sample_rate=2560000, bandwidth=0, lnb_lo=0):
"""Setup rtl_power params"""
if bin_size > 2800:
bin_size = 2800
self.params = {
"start_freq": start_freq,
"stop_freq": stop_freq,
"bin_size": bin_size,
"interval": interval,
"device": device,
"sample_rate": sample_rate,
"hops": 0,
"gain": gain,
"ppm": ppm,
"crop": crop,
"single_shot": single_shot
}
self.lnb_lo = lnb_lo
self.databuffer = {}
self.last_timestamp = ""
def process_start(self):
"""Start rtl_power process"""
if not self.process and self.params:
settings = QtCore.QSettings()
cmdline = shlex.split(settings.value("executable", "rtl_power"))
cmdline.extend([
"-f", "{}M:{}M:{}k".format(self.params["start_freq"] - self.lnb_lo / 1e6,
self.params["stop_freq"] - self.lnb_lo / 1e6,
self.params["bin_size"]),
"-i", "{}".format(self.params["interval"]),
"-d", "{}".format(self.params["device"]),
"-p", "{}".format(self.params["ppm"]),
"-c", "{}".format(self.params["crop"])
])
if self.params["sample_rate"] > 0:
cmdline.extend(["-r", "{}M".format(self.params["sample_rate"] / 1e6)])
if self.params["gain"] >= 0:
cmdline.extend(["-g", "{}".format(self.params["gain"])])
if self.params["single_shot"]:
cmdline.append("-1")
additional_params = settings.value("params", Info.additional_params)
if additional_params:
cmdline.extend(shlex.split(additional_params))
print('Starting backend:')
print(' '.join(cmdline))
print()
self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
universal_newlines=True, console=False)
def parse_output(self, line):
"""Parse one line of output from rtl_power"""
line = [col.strip() for col in line.split(",")]
timestamp = " ".join(line[:2])
start_freq = int(line[2])
stop_freq = int(line[3])
step = float(line[4])
samples = float(line[5])
x_axis = list(np.linspace(start_freq + self.lnb_lo, stop_freq + self.lnb_lo,
round((stop_freq - start_freq) / step)))
y_axis = [float(y) for y in line[6:]]
if len(x_axis) != len(y_axis):
print("ERROR: len(x_axis) != len(y_axis), use newer version of rtl_power!")
if len(x_axis) > len(y_axis):
print("Trimming x_axis...")
x_axis = x_axis[:len(y_axis)]
else:
print("Trimming y_axis...")
y_axis = y_axis[:len(x_axis)]
if timestamp != self.last_timestamp:
self.last_timestamp = timestamp
self.databuffer = {"timestamp": timestamp,
"x": x_axis,
"y": y_axis}
else:
self.databuffer["x"].extend(x_axis)
self.databuffer["y"].extend(y_axis)
# This have to be stupid like this to be compatible with old broken version of rtl_power. Right way is:
# if stop_freq == (self.params["stop_freq"] - self.lnb_lo / 1e6) * 1e6:
if stop_freq > ((self.params["stop_freq"] - self.lnb_lo / 1e6) * 1e6) - step:
self.data_storage.update(self.databuffer)

View File

@ -0,0 +1,145 @@
import math, shlex
from Qt import QtCore
from qspectrumanalyzer import subprocess
from qspectrumanalyzer.backends import BaseInfo, BasePowerThread
class Info(BaseInfo):
"""rtl_power_fftw device metadata"""
pass
class PowerThread(BasePowerThread):
"""Thread which runs rtl_power_fftw process"""
def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1, ppm=0, crop=0,
single_shot=False, device=0, sample_rate=2560000, bandwidth=0, lnb_lo=0):
"""Setup rtl_power_fftw params"""
crop = crop * 100
overlap = crop * 2
freq_range = stop_freq * 1e6 - start_freq * 1e6
min_overhang = sample_rate * overlap * 0.01
hops = math.ceil((freq_range - min_overhang) / (sample_rate - min_overhang))
overhang = (hops * sample_rate - freq_range) / (hops - 1) if hops > 1 else 0
if bin_size > 2800:
bin_size = 2800
bins = math.ceil(sample_rate / (bin_size * 1e3))
crop_freq = sample_rate * crop * 0.01
self.params = {
"start_freq": start_freq,
"stop_freq": stop_freq,
"freq_range": freq_range,
"device": device,
"sample_rate": int(sample_rate),
"bin_size": bin_size,
"bins": bins,
"interval": interval,
"hops": hops,
"time": interval / hops,
"gain": int(gain * 10),
"ppm": ppm,
"crop": crop,
"overlap": overlap,
"min_overhang": min_overhang,
"overhang": overhang,
"single_shot": single_shot
}
self.lnb_lo = lnb_lo
self.freqs = [self.get_hop_freq(hop) for hop in range(hops)]
self.freqs_crop = [(f[0] + crop_freq, f[1] - crop_freq) for f in self.freqs]
self.databuffer = {"timestamp": [], "x": [], "y": []}
self.databuffer_hop = {"timestamp": [], "x": [], "y": []}
self.hop = 0
self.prev_line = ""
def get_hop_freq(self, hop):
"""Get start and stop frequency for particular hop"""
start_freq = self.params["start_freq"] * 1e6 + (self.params["sample_rate"] - self.params["overhang"]) * hop
stop_freq = start_freq + self.params["sample_rate"] - (self.params["sample_rate"] / self.params["bins"])
return (start_freq, stop_freq)
def process_start(self):
"""Start rtl_power_fftw process"""
if not self.process and self.params:
settings = QtCore.QSettings()
cmdline = shlex.split(settings.value("executable", "rtl_power_fftw"))
cmdline.extend([
"-f", "{}M:{}M".format(self.params["start_freq"] - self.lnb_lo / 1e6,
self.params["stop_freq"] - self.lnb_lo / 1e6),
"-b", "{}".format(self.params["bins"]),
"-t", "{}".format(self.params["time"]),
"-d", "{}".format(self.params["device"]),
"-r", "{}".format(self.params["sample_rate"]),
"-p", "{}".format(self.params["ppm"]),
"-q",
])
if self.params["gain"] >= 0:
cmdline.extend(["-g", "{}".format(self.params["gain"])])
if self.params["overlap"] > 0:
cmdline.extend(["-o", "{}".format(self.params["overlap"])])
if not self.params["single_shot"]:
cmdline.append("-c")
additional_params = settings.value("params", Info.additional_params)
if additional_params:
cmdline.extend(shlex.split(additional_params))
print('Starting backend:')
print(' '.join(cmdline))
print()
self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
universal_newlines=True, console=False)
def parse_output(self, line):
"""Parse one line of output from rtl_power_fftw"""
line = line.strip()
# One empty line => new hop
if not line and self.prev_line:
self.hop += 1
self.databuffer["x"].extend(self.databuffer_hop["x"])
self.databuffer["y"].extend(self.databuffer_hop["y"])
self.databuffer_hop = {"timestamp": [], "x": [], "y": []}
# Two empty lines => new set
elif not line and not self.prev_line:
self.hop = 0
self.data_storage.update(self.databuffer)
self.databuffer = {"timestamp": [], "x": [], "y": []}
# Get timestamp for new hop and set
elif line.startswith("# Acquisition start:"):
timestamp = line.split(":", 1)[1].strip()
if not self.databuffer_hop["timestamp"]:
self.databuffer_hop["timestamp"] = timestamp
if not self.databuffer["timestamp"]:
self.databuffer["timestamp"] = timestamp
# Skip other comments
elif line.startswith("#"):
pass
# Parse frequency and power
elif line[0].isdigit():
freq, power = line.split()
freq, power = float(freq) + self.lnb_lo, float(power)
start_freq, stop_freq = self.freqs_crop[self.hop]
# Apply cropping
if freq >= start_freq and freq <= stop_freq:
# Skip overlapping frequencies
if not self.databuffer["x"] or freq > self.databuffer["x"][-1]:
#print(" {:.3f} MHz".format(freq / 1e6))
self.databuffer_hop["x"].append(freq)
self.databuffer_hop["y"].append(power)
else:
#print(" Overlapping {:.3f} MHz".format(freq / 1e6))
pass
else:
#print(" Cropping {:.3f} MHz".format(freq / 1e6))
pass
self.prev_line = line

View File

@ -0,0 +1,109 @@
import shlex
import numpy as np
from Qt import QtCore
from qspectrumanalyzer import subprocess
from qspectrumanalyzer.backends import BaseInfo, BasePowerThread
class Info(BaseInfo):
"""rx_power device metadata"""
sample_rate_min = 0
sample_rate_max = 0
sample_rate = 0
start_freq_min = 0
start_freq_max = 7250
stop_freq_min = 0
stop_freq_max = 7250
gain_min = -1
gain_max = 999
bin_size_min = 0
bin_size_max = 2800
class PowerThread(BasePowerThread):
"""Thread which runs rx_power process"""
def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1, ppm=0, crop=0,
single_shot=False, device=0, sample_rate=2560000, bandwidth=0, lnb_lo=0):
"""Setup rx_power params"""
self.params = {
"start_freq": start_freq,
"stop_freq": stop_freq,
"bin_size": bin_size,
"interval": interval,
"device": device,
"hops": 0,
"gain": gain,
"ppm": ppm,
"crop": crop,
"single_shot": single_shot
}
self.lnb_lo = lnb_lo
self.databuffer = {}
self.last_timestamp = ""
def process_start(self):
"""Start rx_power process"""
if not self.process and self.params:
settings = QtCore.QSettings()
cmdline = shlex.split(settings.value("executable", "rx_power"))
cmdline.extend([
"-f", "{}M:{}M:{}k".format(self.params["start_freq"] - self.lnb_lo / 1e6,
self.params["stop_freq"] - self.lnb_lo / 1e6,
self.params["bin_size"]),
"-i", "{}".format(self.params["interval"]),
"-d", "{}".format(self.params["device"]),
"-p", "{}".format(self.params["ppm"]),
"-c", "{}".format(self.params["crop"])
])
if self.params["gain"] >= 0:
cmdline.extend(["-g", "{}".format(self.params["gain"])])
if self.params["single_shot"]:
cmdline.append("-1")
additional_params = settings.value("params", Info.additional_params)
if additional_params:
cmdline.extend(shlex.split(additional_params))
print('Starting backend:')
print(' '.join(cmdline))
print()
self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
universal_newlines=True, console=False)
def parse_output(self, line):
"""Parse one line of output from rx_power"""
line = [col.strip() for col in line.split(",")]
timestamp = " ".join(line[:2])
start_freq = int(line[2])
stop_freq = int(line[3])
step = float(line[4])
samples = float(line[5])
x_axis = list(np.linspace(start_freq + self.lnb_lo, stop_freq + self.lnb_lo,
round((stop_freq - start_freq) / step)))
y_axis = [float(y) for y in line[6:]]
if len(x_axis) != len(y_axis):
print("ERROR: len(x_axis) != len(y_axis)!")
if len(x_axis) > len(y_axis):
print("Trimming x_axis...")
x_axis = x_axis[:len(y_axis)]
else:
print("Trimming y_axis...")
y_axis = y_axis[:len(x_axis)]
if timestamp != self.last_timestamp:
self.last_timestamp = timestamp
self.databuffer = {"timestamp": timestamp,
"x": x_axis,
"y": y_axis}
else:
self.databuffer["x"].extend(x_axis)
self.databuffer["y"].extend(y_axis)
# This have to be stupid like this to be compatible with old broken version of rtl_power. Right way is:
# if stop_freq == (self.params["stop_freq"] - self.lnb_lo / 1e6) * 1e6:
if stop_freq > ((self.params["stop_freq"] - self.lnb_lo / 1e6) * 1e6) - step:
self.data_storage.update(self.databuffer)

View File

@ -0,0 +1,254 @@
import os, sys, shlex, signal
import numpy as np
from Qt import QtCore
from qspectrumanalyzer import subprocess
from qspectrumanalyzer.backends import BaseInfo, BasePowerThread
try:
from soapypower.writer import SoapyPowerBinFormat
formatter = SoapyPowerBinFormat()
except ImportError:
print('soapy_power module not found!')
formatter = None
class Info(BaseInfo):
"""soapy_power device metadata"""
sample_rate_min = 0
sample_rate_max = 61440000
bandwidth_min = 0
bandwidth_max = 61440000
start_freq_min = 0
start_freq_max = 7250
stop_freq_min = 0
stop_freq_max = 7250
gain_min = -1
gain_max = 999
bin_size_min = 0
bin_size_max = 10000
additional_params = '--even --fft-window boxcar --remove-dc'
@classmethod
def help_device(cls, executable, device):
cmdline = shlex.split(executable)
try:
text = subprocess.check_output(cmdline + ['--detect'], universal_newlines=True,
stderr=subprocess.DEVNULL, env=dict(os.environ, COLUMNS='125'),
console=False)
text += '\n'
text += subprocess.check_output(cmdline + ['--device', device, '--info'], universal_newlines=True,
stderr=subprocess.DEVNULL, env=dict(os.environ, COLUMNS='125'),
console=False)
except subprocess.CalledProcessError as e:
text = e.output
except OSError:
text = '{} executable not found!'.format(executable)
return text
class PowerThread(BasePowerThread):
"""Thread which runs soapy_power process"""
def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1, ppm=0, crop=0,
single_shot=False, device="", sample_rate=2560000, bandwidth=0, lnb_lo=0):
"""Setup soapy_power params"""
self.params = {
"start_freq": start_freq,
"stop_freq": stop_freq,
"device": device,
"sample_rate": sample_rate,
"bandwidth": bandwidth,
"bin_size": bin_size,
"interval": interval,
"hops": 0,
"gain": gain,
"ppm": ppm,
"crop": crop * 100,
"single_shot": single_shot
}
self.lnb_lo = lnb_lo
self.databuffer = {"timestamp": [], "x": [], "y": []}
self.min_freq = None
self.pipe_read = None
self.pipe_read_fd = None
self.pipe_write_fd = None
self.pipe_write_handle = None
def process_start(self):
"""Start soapy_power process"""
if not self.process and self.params:
# Create pipe used for communication with soapy_power process
self.pipe_read_fd, self.pipe_write_fd = os.pipe()
self.pipe_read = open(self.pipe_read_fd, 'rb')
os.set_inheritable(self.pipe_write_fd, True)
if sys.platform == 'win32':
self.pipe_write_handle = subprocess.make_inheritable_handle(self.pipe_write_fd)
# Prepare soapy_power cmdline parameters
settings = QtCore.QSettings()
cmdline = shlex.split(settings.value("executable", "soapy_power"))
cmdline.extend([
"-f", "{}M:{}M".format(self.params["start_freq"],
self.params["stop_freq"]),
"-B", "{}k".format(self.params["bin_size"]),
"-T", "{}".format(self.params["interval"]),
"-d", "{}".format(self.params["device"]),
"-r", "{}".format(self.params["sample_rate"]),
"-p", "{}".format(self.params["ppm"]),
"-F", "soapy_power_bin",
"--output-fd", "{}".format(
int(self.pipe_write_handle) if sys.platform == 'win32' else self.pipe_write_fd
),
])
if self.lnb_lo != 0:
cmdline.extend(["--lnb-lo", "{}".format(self.lnb_lo)])
if self.params["bandwidth"] > 0:
cmdline.extend(["-w", "{}".format(self.params["bandwidth"])])
if self.params["gain"] >= 0:
cmdline.extend(["-g", "{}".format(self.params["gain"])])
if self.params["crop"] > 0:
cmdline.extend(["-k", "{}".format(self.params["crop"])])
if not self.params["single_shot"]:
cmdline.append("-c")
additional_params = settings.value("params", Info.additional_params)
if additional_params:
cmdline.extend(shlex.split(additional_params))
# Start soapy_power process and close write part of pipe
if sys.platform == 'win32':
creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
else:
creationflags = 0
print('Starting backend:')
print(' '.join(cmdline))
print()
self.process = subprocess.Popen(cmdline, close_fds=False, universal_newlines=False,
creationflags=creationflags, console=False)
os.close(self.pipe_write_fd)
if sys.platform == 'win32':
self.pipe_write_handle.Close()
def process_stop(self):
"""Stop soapy_power process"""
with self._shutdown_lock:
if self.process:
if self.process.poll() is None:
try:
if sys.platform == 'win32':
self.process.send_signal(signal.CTRL_BREAK_EVENT)
else:
self.process.terminate()
except ProcessLookupError:
pass
self.process.wait()
self.process = None
# Close pipe used for communication with soapy_power process
self.pipe_read.close()
self.pipe_read = None
self.pipe_read_fd = None
self.pipe_write_fd = None
self.pipe_write_handle = None
def parse_output(self, data):
"""Parse data from soapy_power"""
header, y_axis = data
time_start = header.time_start
time_stop = header.time_stop
start_freq = header.start
stop_freq = header.stop
step = header.step
samples = header.samples
x_axis = np.linspace(start_freq, stop_freq, round((stop_freq - start_freq) / step))
if len(x_axis) != len(y_axis):
print("ERROR: len(x_axis) != len(y_axis)")
return
if self.min_freq is None:
self.min_freq = start_freq
if start_freq == self.min_freq:
self.databuffer = {"timestamp": time_stop,
"x": list(x_axis),
"y": list(y_axis)}
else:
self.databuffer["x"].extend(x_axis)
self.databuffer["y"].extend(y_axis)
if stop_freq > (self.params["stop_freq"] * 1e6) - step:
self.data_storage.update(self.databuffer)
def run(self):
"""soapy_power thread main loop"""
if not formatter:
return
self.process_start()
self.alive = True
self.powerThreadStarted.emit()
while self.alive:
try:
data = formatter.read(self.pipe_read)
except ValueError as e:
print(e, file=sys.stderr)
continue
if data:
self.parse_output(data)
else:
break
self.process_stop()
self.alive = False
self.powerThreadStopped.emit()
def read_from_file(f):
"""Generator for reading data from soapy_power binary files"""
if not formatter:
return
min_freq = None
databuffer = None
while True:
try:
data = formatter.read(f)
except ValueError as e:
print(e, file=sys.stderr)
continue
if not data:
if min_freq is not None:
yield databuffer
return
header, y_axis = data
x_axis = np.linspace(header.start, header.stop, round((header.stop - header.start) / header.step))
if len(x_axis) != len(y_axis):
print("ERROR: len(x_axis) != len(y_axis)")
continue
if min_freq is None:
min_freq = header.start
elif header.start == min_freq:
yield databuffer
if header.start == min_freq:
databuffer = {"timestamp": header.time_stop,
"x": list(x_axis),
"y": list(y_axis)}
else:
databuffer["x"].extend(x_axis)
databuffer["y"].extend(y_axis)

View File

@ -0,0 +1,28 @@
from Qt import QtCore, QtWidgets
from qspectrumanalyzer.ui_qspectrumanalyzer_baseline import Ui_QSpectrumAnalyzerBaseline
class QSpectrumAnalyzerBaseline(QtWidgets.QDialog, Ui_QSpectrumAnalyzerBaseline):
"""QSpectrumAnalyzer baseline dialog"""
def __init__(self, parent=None):
# Initialize UI
super().__init__(parent)
self.setupUi(self)
# Load settings
settings = QtCore.QSettings()
self.baselineFileEdit.setText(settings.value("baseline_file", ""))
@QtCore.Slot()
def on_baselineFileButton_clicked(self):
"""Open file dialog when button is clicked"""
filename = QtWidgets.QFileDialog.getOpenFileName(self, self.tr("Select baseline file - QSpectrumAnalyzer"))[0]
if filename:
self.baselineFileEdit.setText(filename)
def accept(self):
"""Save settings when dialog is accepted"""
settings = QtCore.QSettings()
settings.setValue("baseline_file", self.baselineFileEdit.text())
QtWidgets.QDialog.accept(self)

View File

@ -0,0 +1,33 @@
from Qt import QtCore, QtWidgets
from qspectrumanalyzer.utils import color_to_str, str_to_color
from qspectrumanalyzer.ui_qspectrumanalyzer_colors import Ui_QSpectrumAnalyzerColors
class QSpectrumAnalyzerColors(QtWidgets.QDialog, Ui_QSpectrumAnalyzerColors):
"""QSpectrumAnalyzer colors dialog"""
def __init__(self, parent=None):
# Initialize UI
super().__init__(parent)
self.setupUi(self)
# Load settings
settings = QtCore.QSettings()
self.mainColorButton.setColor(str_to_color(settings.value("main_color", "255, 255, 0, 255")))
self.peakHoldMaxColorButton.setColor(str_to_color(settings.value("peak_hold_max_color", "255, 0, 0, 255")))
self.peakHoldMinColorButton.setColor(str_to_color(settings.value("peak_hold_min_color", "0, 0, 255, 255")))
self.averageColorButton.setColor(str_to_color(settings.value("average_color", "0, 255, 255, 255")))
self.persistenceColorButton.setColor(str_to_color(settings.value("persistence_color", "0, 255, 0, 255")))
self.baselineColorButton.setColor(str_to_color(settings.value("baseline_color", "255, 0, 255, 255")))
def accept(self):
"""Save settings when dialog is accepted"""
settings = QtCore.QSettings()
settings.setValue("main_color", color_to_str(self.mainColorButton.color()))
settings.setValue("peak_hold_max_color", color_to_str(self.peakHoldMaxColorButton.color()))
settings.setValue("peak_hold_min_color", color_to_str(self.peakHoldMinColorButton.color()))
settings.setValue("average_color", color_to_str(self.averageColorButton.color()))
settings.setValue("persistence_color", color_to_str(self.persistenceColorButton.color()))
settings.setValue("baseline_color", color_to_str(self.baselineColorButton.color()))
QtWidgets.QDialog.accept(self)

View File

@ -1,9 +1,10 @@
import time, sys
import time, sys, os
from PyQt4 import QtCore
from Qt import QtCore
import numpy as np
from qspectrumanalyzer.utils import smooth
from qspectrumanalyzer.backends import soapy_power
class HistoryBuffer:
@ -36,7 +37,7 @@ class HistoryBuffer:
class TaskSignals(QtCore.QObject):
"""Task signals emitter"""
result = QtCore.pyqtSignal(object)
result = QtCore.Signal(object)
class Task(QtCore.QRunnable):
@ -57,12 +58,14 @@ class Task(QtCore.QRunnable):
class DataStorage(QtCore.QObject):
"""Data storage for spectrum measurements"""
history_updated = QtCore.pyqtSignal(object)
data_updated = QtCore.pyqtSignal(object)
data_recalculated = QtCore.pyqtSignal(object)
average_updated = QtCore.pyqtSignal(object)
peak_hold_max_updated = QtCore.pyqtSignal(object)
peak_hold_min_updated = QtCore.pyqtSignal(object)
history_updated = QtCore.Signal(object)
data_updated = QtCore.Signal(object)
history_recalculated = QtCore.Signal(object)
data_recalculated = QtCore.Signal(object)
average_updated = QtCore.Signal(object)
baseline_updated = QtCore.Signal(object)
peak_hold_max_updated = QtCore.Signal(object)
peak_hold_min_updated = QtCore.Signal(object)
def __init__(self, max_history_size=100, parent=None):
super().__init__(parent)
@ -70,6 +73,10 @@ class DataStorage(QtCore.QObject):
self.smooth = False
self.smooth_length = 11
self.smooth_window = "hanning"
self.subtract_baseline = False
self.prev_baseline = None
self.baseline = None
self.baseline_x = None
# Use only one worker thread because it is not faster
# with more threads (and memory consumption is much higher)
@ -105,11 +112,20 @@ class DataStorage(QtCore.QObject):
def update(self, data):
"""Update data storage"""
if self.y is not None and len(data["y"]) != len(self.y):
print("{:d} bins coming from backend, expected {:d}".format(len(data["y"]), len(self.y)))
return
self.average_counter += 1
if self.x is None:
self.x = data["x"]
# Subtract baseline from data
data["y"] = np.asarray(data["y"])
if self.subtract_baseline and self.baseline is not None and len(data["y"]) == len(self.baseline):
data["y"] -= self.baseline
self.start_task(self.update_history, data.copy())
self.start_task(self.update_data, data)
@ -161,16 +177,63 @@ class DataStorage(QtCore.QObject):
"""Apply smoothing function to data"""
return smooth(y, window_len=self.smooth_length, window=self.smooth_window)
def set_smooth(self, toggle, length=11, window="hanning", recalculate=False):
def set_smooth(self, toggle, length=11, window="hanning"):
"""Toggle smoothing and set smoothing params"""
if toggle != self.smooth or length != self.smooth_length or window != self.smooth_window:
self.smooth = toggle
self.smooth_length = length
self.smooth_window = window
if recalculate:
self.start_task(self.recalculate_data)
def set_subtract_baseline(self, toggle, baseline_file=None):
"""Toggle baseline subtraction and set baseline"""
baseline = None
baseline_x = None
# Load baseline from file (compute average if there are multiple PSD data in file)
if baseline_file and os.path.isfile(baseline_file):
average_counter = 0
with open(baseline_file, 'rb') as f:
for data in soapy_power.read_from_file(f):
average_counter += 1
if baseline is None:
baseline = data['y'].copy()
baseline_x = data['x'].copy()
else:
self.reset_data()
baseline = np.average((baseline, data['y']), axis=0, weights=(average_counter - 1, 1))
# Don't subtract baseline if number of bins in baseline differs from number of bins in data
if self.y is not None and baseline is not None and len(self.y) != len(baseline):
print("Can't subtract baseline (expected {:d} bins, but baseline has {:d} bins)".format(
len(self.y), len(baseline)
))
#baseline = None
if self.subtract_baseline:
self.prev_baseline = self.baseline
#if not np.array_equal(baseline, self.baseline):
self.baseline = baseline
self.baseline_x = baseline_x
self.baseline_updated.emit(self)
self.subtract_baseline = toggle
self.start_task(self.recalculate_history)
self.start_task(self.recalculate_data)
def recalculate_history(self):
"""Recalculate spectrum measurements history"""
if self.history is None:
return
history = self.history.get_buffer()
if self.prev_baseline is not None and len(history[-1]) == len(self.prev_baseline):
history += self.prev_baseline
self.prev_baseline = None
if self.subtract_baseline and self.baseline is not None and len(history[-1]) == len(self.baseline):
history -= self.baseline
self.history_recalculated.emit(self)
def recalculate_data(self):
"""Recalculate current data from history"""

View File

@ -1,250 +1,288 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="2.0">
<context>
<name>QSpectrumAnalyzerBaseline</name>
<message>
<location filename="../baseline.py" line="20"/>
<source>Select baseline file - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_baseline.py" line="50"/>
<source>Baseline - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_baseline.py" line="51"/>
<source>Baseline &amp;file:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_baseline.py" line="52"/>
<source>...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QSpectrumAnalyzerColors</name>
<message>
<location filename="../ui_qspectrumanalyzer_colors.py" line="113"/>
<location filename="../ui_qspectrumanalyzer_colors.py" line="112"/>
<source>Colors - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_colors.py" line="114"/>
<source>Main curve color:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_colors.py" line="123"/>
<location filename="../ui_qspectrumanalyzer_colors.py" line="124"/>
<source>...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_colors.py" line="120"/>
<source>Average color:</source>
<location filename="../ui_qspectrumanalyzer_colors.py" line="113"/>
<source>&amp;Main curve color:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_colors.py" line="122"/>
<source>Persistence color:</source>
<location filename="../ui_qspectrumanalyzer_colors.py" line="115"/>
<source>Max. peak &amp;hold color:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_colors.py" line="116"/>
<source>Max. peak hold color:</source>
<location filename="../ui_qspectrumanalyzer_colors.py" line="117"/>
<source>M&amp;in. peak hold color:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_colors.py" line="118"/>
<source>Min. peak hold color:</source>
<location filename="../ui_qspectrumanalyzer_colors.py" line="119"/>
<source>Average &amp;color:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_colors.py" line="121"/>
<source>Persistence co&amp;lor:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_colors.py" line="123"/>
<source>&amp;Baseline color:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QSpectrumAnalyzerMainWindow</name>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="311"/>
<location filename="../ui_qspectrumanalyzer.py" line="317"/>
<source>QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="325"/>
<source>Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="322"/>
<source> MHz</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="324"/>
<source> kHz</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="328"/>
<source>auto</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="318"/>
<source>Frequency</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="314"/>
<source>Controls</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="340"/>
<source>Levels</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../__main__.py" line="337"/>
<source>Frequency hops: {} | Sweep time: {:.2f} s | FPS: {:.2f}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../__main__.py" line="337"/>
<source>N/A</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../__main__.py" line="504"/>
<location filename="../__main__.py" line="496"/>
<source>About - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../__main__.py" line="504"/>
<location filename="../__main__.py" line="496"/>
<source>QSpectrumAnalyzer {}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="312"/>
<location filename="../ui_qspectrumanalyzer.py" line="318"/>
<source>&amp;File</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="313"/>
<location filename="../ui_qspectrumanalyzer.py" line="319"/>
<source>&amp;Help</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="315"/>
<source>&amp;Start</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="316"/>
<source>S&amp;top</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="317"/>
<source>Si&amp;ngle shot</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="319"/>
<source>Start:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="321"/>
<source>Stop:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="323"/>
<source>Bin size:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="326"/>
<source>Interval [s]:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="327"/>
<source>Gain [dB]:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="329"/>
<source>Corr. [ppm]:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="330"/>
<source>Crop [%]:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="331"/>
<source>Main curve</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="332"/>
<source>Colors...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="333"/>
<source>Max. hold</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="334"/>
<source>Min. hold</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="335"/>
<source>Average</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="336"/>
<source>Smoothing</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="339"/>
<source>...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="338"/>
<source>Persistence</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="341"/>
<location filename="../ui_qspectrumanalyzer.py" line="350"/>
<source>&amp;Settings...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="342"/>
<location filename="../ui_qspectrumanalyzer.py" line="351"/>
<source>&amp;Quit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="343"/>
<location filename="../ui_qspectrumanalyzer.py" line="352"/>
<source>Ctrl+Q</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="344"/>
<location filename="../ui_qspectrumanalyzer.py" line="353"/>
<source>&amp;About</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../__main__.py" line="253"/>
<source>Frequency hops: {}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../__main__.py" line="255"/>
<source>Total time: {} | Sweep time: {:.2f} s ({:.2f} FPS)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="321"/>
<source>&amp;Start</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="322"/>
<source>S&amp;top</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="323"/>
<source>Si&amp;ngle shot</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="325"/>
<source>Start:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="328"/>
<source> MHz</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="327"/>
<source>Stop:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="329"/>
<source>&amp;Bin size:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="330"/>
<source> kHz</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="332"/>
<source>&amp;Interval [s]:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="333"/>
<source>&amp;Gain [dB]:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="334"/>
<source>Corr. [ppm]:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="335"/>
<source>Crop [%]:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="336"/>
<source>Main curve</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="337"/>
<source>Colors...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="338"/>
<source>Max. hold</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="339"/>
<source>Min. hold</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="340"/>
<source>Average</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="341"/>
<source>Smoothing</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="347"/>
<source>...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="343"/>
<source>Persistence</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="345"/>
<source>auto</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="348"/>
<source>Subtract baseline</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="346"/>
<source>Baseline</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="320"/>
<source>Controls</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="324"/>
<source>Frequency</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="331"/>
<source>Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="349"/>
<source>Levels</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QSpectrumAnalyzerPersistence</name>
<message>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="68"/>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="55"/>
<source>Persistence - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="69"/>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="56"/>
<source>Decay function:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="70"/>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="57"/>
<source>linear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="71"/>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="58"/>
<source>exponential</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="72"/>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="59"/>
<source>Persistence length:</source>
<translation type="unfinished"></translation>
</message>
@ -252,110 +290,148 @@
<context>
<name>QSpectrumAnalyzerSettings</name>
<message>
<location filename="../__main__.py" line="50"/>
<location filename="../settings.py" line="57"/>
<source>Select executable - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="107"/>
<location filename="../ui_qspectrumanalyzer_settings.py" line="148"/>
<source>Settings - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="108"/>
<location filename="../ui_qspectrumanalyzer_settings.py" line="149"/>
<source>&amp;Backend:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="115"/>
<location filename="../ui_qspectrumanalyzer_settings.py" line="156"/>
<source>soapy_power</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="110"/>
<location filename="../ui_qspectrumanalyzer_settings.py" line="151"/>
<source>rx_power</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="111"/>
<location filename="../ui_qspectrumanalyzer_settings.py" line="152"/>
<source>rtl_power_fftw</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="112"/>
<location filename="../ui_qspectrumanalyzer_settings.py" line="153"/>
<source>rtl_power</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="113"/>
<location filename="../ui_qspectrumanalyzer_settings.py" line="154"/>
<source>hackrf_sweep</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="114"/>
<location filename="../ui_qspectrumanalyzer_settings.py" line="155"/>
<source>E&amp;xecutable:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="116"/>
<location filename="../ui_qspectrumanalyzer_settings.py" line="157"/>
<source>...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="117"/>
<source>Device:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="118"/>
<location filename="../ui_qspectrumanalyzer_settings.py" line="159"/>
<source>Sa&amp;mple rate:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="119"/>
<location filename="../ui_qspectrumanalyzer_settings.py" line="160"/>
<source>&amp;Waterfall history size:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="158"/>
<source>&amp;Device:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="161"/>
<source>Bandwidt&amp;h:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="163"/>
<source>&amp;LNB LO:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="166"/>
<source> ? </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="169"/>
<source>Negative frequency for upconverters, positive frequency for downconverters.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="164"/>
<source>Add&amp;itional parameters:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="170"/>
<source> MHz</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QSpectrumAnalyzerSmooth</name>
<name>QSpectrumAnalyzerSettingsHelp</name>
<message>
<location filename="../ui_qspectrumanalyzer_smooth.py" line="73"/>
<location filename="../ui_qspectrumanalyzer_settings_help.py" line="36"/>
<source>Help - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QSpectrumAnalyzerSmoothing</name>
<message>
<location filename="../ui_qspectrumanalyzer_smoothing.py" line="60"/>
<source>Smoothing - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_smooth.py" line="74"/>
<location filename="../ui_qspectrumanalyzer_smoothing.py" line="61"/>
<source>&amp;Window function:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_smooth.py" line="75"/>
<location filename="../ui_qspectrumanalyzer_smoothing.py" line="62"/>
<source>rectangular</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_smooth.py" line="76"/>
<location filename="../ui_qspectrumanalyzer_smoothing.py" line="63"/>
<source>hanning</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_smooth.py" line="77"/>
<location filename="../ui_qspectrumanalyzer_smoothing.py" line="64"/>
<source>hamming</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_smooth.py" line="78"/>
<location filename="../ui_qspectrumanalyzer_smoothing.py" line="65"/>
<source>bartlett</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_smooth.py" line="79"/>
<location filename="../ui_qspectrumanalyzer_smoothing.py" line="66"/>
<source>blackman</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_smooth.py" line="80"/>
<location filename="../ui_qspectrumanalyzer_smoothing.py" line="67"/>
<source>Window len&amp;gth:</source>
<translation type="unfinished"></translation>
</message>

View File

@ -0,0 +1,29 @@
from Qt import QtCore, QtWidgets
from qspectrumanalyzer.ui_qspectrumanalyzer_persistence import Ui_QSpectrumAnalyzerPersistence
class QSpectrumAnalyzerPersistence(QtWidgets.QDialog, Ui_QSpectrumAnalyzerPersistence):
"""QSpectrumAnalyzer spectrum persistence dialog"""
def __init__(self, parent=None):
# Initialize UI
super().__init__(parent)
self.setupUi(self)
# Load settings
settings = QtCore.QSettings()
self.persistenceLengthSpinBox.setValue(settings.value("persistence_length", 5, int))
decay_function = settings.value("persistence_decay", "exponential")
i = self.decayFunctionComboBox.findText(decay_function)
if i == -1:
self.decayFunctionComboBox.setCurrentIndex(0)
else:
self.decayFunctionComboBox.setCurrentIndex(i)
def accept(self):
"""Save settings when dialog is accepted"""
settings = QtCore.QSettings()
settings.setValue("persistence_length", self.persistenceLengthSpinBox.value())
settings.setValue("persistence_decay", self.decayFunctionComboBox.currentText())
QtWidgets.QDialog.accept(self)

View File

@ -1,6 +1,6 @@
import collections, math
from PyQt4 import QtCore
from Qt import QtCore
import pyqtgraph as pg
# Basic PyQtGraph settings
@ -29,6 +29,8 @@ class SpectrumPlotWidget:
self.peak_hold_min_color = pg.mkColor("b")
self.average = False
self.average_color = pg.mkColor("c")
self.baseline = False
self.baseline_color = pg.mkColor("m")
self.create_plot()
@ -42,6 +44,10 @@ class SpectrumPlotWidget:
self.plot.setLimits(xMin=0)
self.plot.showButtons()
#self.plot.setDownsampling(mode="peak")
#self.plot.setClipToView(True)
self.create_baseline_curve()
self.create_persistence_curves()
self.create_average_curve()
self.create_peak_hold_min_curve()
@ -78,6 +84,11 @@ class SpectrumPlotWidget:
self.curve_average = self.plot.plot(pen=self.average_color)
self.curve_average.setZValue(700)
def create_baseline_curve(self):
"""Create baseline curve"""
self.curve_baseline = self.plot.plot(pen=self.baseline_color)
self.curve_baseline.setZValue(500)
def create_persistence_curves(self):
"""Create spectrum persistence curves"""
z_index_base = 600
@ -96,6 +107,7 @@ class SpectrumPlotWidget:
self.curve_peak_hold_max.setPen(self.peak_hold_max_color)
self.curve_peak_hold_min.setPen(self.peak_hold_min_color)
self.curve_average.setPen(self.average_color)
self.curve_baseline.setPen(self.baseline_color)
decay = self.get_decay()
for i, curve in enumerate(self.persistence_curves):
@ -158,6 +170,17 @@ class SpectrumPlotWidget:
if force:
self.curve_average.setVisible(self.average)
def update_baseline(self, data_storage, force=False):
"""Update baseline curve"""
if data_storage.baseline_x is None or data_storage.baseline is None:
self.curve_baseline.clear()
return
if self.baseline or force:
self.curve_baseline.setData(data_storage.baseline_x, data_storage.baseline)
if force:
self.curve_baseline.setVisible(self.baseline)
def update_persistence(self, data_storage, force=False):
"""Update persistence curves"""
if data_storage.x is None:
@ -181,6 +204,7 @@ class SpectrumPlotWidget:
QtCore.QTimer.singleShot(0, lambda: self.update_plot(data_storage, force=True))
QtCore.QTimer.singleShot(0, lambda: self.update_average(data_storage, force=True))
QtCore.QTimer.singleShot(0, lambda: self.update_baseline(data_storage, force=True))
QtCore.QTimer.singleShot(0, lambda: self.update_peak_hold_max(data_storage, force=True))
QtCore.QTimer.singleShot(0, lambda: self.update_peak_hold_min(data_storage, force=True))
@ -228,6 +252,10 @@ class SpectrumPlotWidget:
"""Clear average curve"""
self.curve_average.clear()
def clear_baseline(self):
"""Clear baseline curve"""
self.curve_baseline.clear()
def clear_persistence(self):
"""Clear spectrum persistence curves"""
self.persistence_data = None
@ -264,6 +292,7 @@ class WaterfallPlotWidget:
self.plot.setLimits(xMin=0, yMax=0)
self.plot.showButtons()
#self.plot.setAspectLocked(True)
#self.plot.setDownsampling(mode="peak")
#self.plot.setClipToView(True)
@ -304,3 +333,16 @@ class WaterfallPlotWidget:
def clear_plot(self):
"""Clear waterfall plot"""
self.counter = 0
def recalculate_plot(self, data_storage):
"""Recalculate waterfall plot"""
if data_storage.x is None:
return
self.waterfallImg.setImage(data_storage.history.buffer[-self.counter:].T,
autoLevels=False, autoRange=False)
self.waterfallImg.setPos(
data_storage.x[0],
-self.counter if self.counter < self.history_size else -self.history_size
)
self.histogram.setImageItem(self.waterfallImg)

View File

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
id="svg4290"
version="1.1"
inkscape:version="0.91 r13725"
viewBox="0 0 32 32"
inkscape:export-filename="qspectrumanalyzer.png"
inkscape:export-xdpi="135"
inkscape:export-ydpi="135"
sodipodi:docname="qspectrumanalyzer.svg">
<defs
id="defs4292" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6568543"
inkscape:cx="0.51171273"
inkscape:cy="17.429759"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:snap-intersection-paths="true"
inkscape:snap-bbox="false"
inkscape:snap-nodes="true"
inkscape:object-nodes="true"
inkscape:snap-global="false"
inkscape:window-width="1366"
inkscape:window-height="709"
inkscape:window-x="-4"
inkscape:window-y="0"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid4298" />
</sodipodi:namedview>
<metadata
id="metadata4295">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="fill:#000000;stroke:#666666;stroke-opacity:1"
id="rect4300"
width="30"
height="30"
x="1"
y="1"
rx="3"
ry="3" />
<path
style="fill:none;fill-rule:evenodd;stroke:#666666;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 16,1 0,30"
id="path4302"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#666666;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 31,16 1,16"
id="path4302-5"
inkscape:connector-curvature="0" />
<use
x="0"
y="0"
xlink:href="#path4302"
id="use4369"
transform="translate(-7.5,0)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#path4302"
id="use4371"
transform="translate(7.5,0)"
width="100%"
height="100%" />
<path
inkscape:connector-curvature="0"
id="path4373"
d="M 31,8.5 1,8.5"
style="fill:none;fill-rule:evenodd;stroke:#666666;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<use
x="0"
y="0"
xlink:href="#path4302-5"
id="use4375"
transform="translate(0,7.5)"
width="100%"
height="100%" />
<path
style="fill:none;fill-rule:evenodd;stroke:#00ff00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 1.0625,25.25 c 0,0 2.5545099,5.432144 4.3470654,0.903939 0.6415968,-1.620748 1.1843693,-10.505075 2.3294368,-10.451145 1.124605,0.05297 1.2928473,9.79388 3.0231028,11.399454 0.836834,0.735662 2.019611,-2.789575 2.96484,-2.691821 1.178304,0.121857 2.125956,3.657855 3.181223,2.383073 2.07617,-2.508052 1.527752,-21.6078848 2.588897,-21.5746877 1.200441,0.037555 1.333727,19.4989257 3.765313,21.7522057 1.533625,1.421166 1.791478,-2.106359 3.115303,-2.461012 0.660576,-0.176968 2.034012,3.420657 2.702589,3.52241 0.772278,0.117536 1.624099,-1.153766 2.01348,-0.813666"
id="path4394"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csscssssssc" />
<rect
style="fill:none;stroke:#666666;stroke-opacity:1"
id="rect4300-8"
width="30"
height="30"
x="1"
y="1"
rx="3"
ry="3" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>1200</width>
<height>810</height>
<height>892</height>
</rect>
</property>
<property name="windowTitle">
@ -52,7 +52,7 @@
<x>0</x>
<y>0</y>
<width>1200</width>
<height>30</height>
<height>32</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
@ -82,8 +82,8 @@
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>10</height>
<width>190</width>
<height>130</height>
</size>
</property>
<property name="features">
@ -143,8 +143,8 @@
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>10</height>
<width>208</width>
<height>166</height>
</size>
</property>
<property name="features">
@ -182,6 +182,9 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="showGroupSeparator" stdset="0">
<bool>true</bool>
</property>
<property name="suffix">
<string> MHz</string>
</property>
@ -189,10 +192,10 @@
<number>3</number>
</property>
<property name="minimum">
<double>24.000000000000000</double>
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1766.000000000000000</double>
<double>2200.000000000000000</double>
</property>
<property name="value">
<double>87.000000000000000</double>
@ -220,6 +223,9 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="showGroupSeparator" stdset="0">
<bool>true</bool>
</property>
<property name="suffix">
<string> MHz</string>
</property>
@ -227,10 +233,10 @@
<number>3</number>
</property>
<property name="minimum">
<double>24.000000000000000</double>
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1766.000000000000000</double>
<double>2200.000000000000000</double>
</property>
<property name="value">
<double>108.000000000000000</double>
@ -240,7 +246,7 @@
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Bin size:</string>
<string>&amp;Bin size:</string>
</property>
<property name="buddy">
<cstring>binSizeSpinBox</cstring>
@ -258,14 +264,20 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="showGroupSeparator" stdset="0">
<bool>true</bool>
</property>
<property name="suffix">
<string> kHz</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>2800.000000000000000</double>
<double>10000.000000000000000</double>
</property>
<property name="value">
<double>10.000000000000000</double>
@ -309,7 +321,7 @@
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Interval [s]:</string>
<string>&amp;Interval [s]:</string>
</property>
<property name="buddy">
<cstring>intervalSpinBox</cstring>
@ -319,7 +331,7 @@
<item row="0" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Gain [dB]:</string>
<string>&amp;Gain [dB]:</string>
</property>
<property name="buddy">
<cstring>gainSpinBox</cstring>
@ -339,25 +351,6 @@
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QSpinBox" name="gainSpinBox">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="specialValueText">
<string>auto</string>
</property>
<property name="minimum">
<number>-1</number>
</property>
<property name="maximum">
<number>49</number>
</property>
<property name="value">
<number>-1</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
@ -463,7 +456,7 @@
</property>
</widget>
</item>
<item row="9" column="0">
<item row="11" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -483,6 +476,55 @@
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QDoubleSpinBox" name="gainSpinBox">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="specialValueText">
<string>auto</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>-1.000000000000000</double>
</property>
<property name="maximum">
<double>999.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
<property name="value">
<double>-1.000000000000000</double>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="baselineCheckBox">
<property name="text">
<string>Baseline</string>
</property>
</widget>
</item>
<item row="9" column="2">
<widget class="QToolButton" name="baselineButton">
<property name="text">
<string>...</string>
</property>
<property name="autoRaise">
<bool>false</bool>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QCheckBox" name="subtractBaselineCheckBox">
<property name="text">
<string>Subtract baseline</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
@ -563,6 +605,9 @@
<tabstop>smoothButton</tabstop>
<tabstop>persistenceCheckBox</tabstop>
<tabstop>persistenceButton</tabstop>
<tabstop>baselineCheckBox</tabstop>
<tabstop>baselineButton</tabstop>
<tabstop>subtractBaselineCheckBox</tabstop>
<tabstop>histogramPlotLayout</tabstop>
<tabstop>mainPlotLayout</tabstop>
<tabstop>waterfallPlotLayout</tabstop>

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QSpectrumAnalyzerBaseline</class>
<widget class="QDialog" name="QSpectrumAnalyzerBaseline">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>100</height>
</rect>
</property>
<property name="windowTitle">
<string>Baseline - QSpectrumAnalyzer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Baseline &amp;file:</string>
</property>
<property name="buddy">
<cstring>baselineFileEdit</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="baselineFileEdit"/>
</item>
<item>
<widget class="QToolButton" name="baselineFileButton">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>baselineFileEdit</tabstop>
<tabstop>baselineFileButton</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QSpectrumAnalyzerBaseline</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>224</x>
<y>72</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>99</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QSpectrumAnalyzerBaseline</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>292</x>
<y>78</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>99</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>232</width>
<height>260</height>
<width>253</width>
<height>266</height>
</rect>
</property>
<property name="windowTitle">
@ -19,7 +19,7 @@
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Main curve color:</string>
<string>&amp;Main curve color:</string>
</property>
<property name="buddy">
<cstring>mainColorButton</cstring>
@ -42,7 +42,7 @@
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Max. peak hold color:</string>
<string>Max. peak &amp;hold color:</string>
</property>
<property name="buddy">
<cstring>peakHoldMaxColorButton</cstring>
@ -65,7 +65,7 @@
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Min. peak hold color:</string>
<string>M&amp;in. peak hold color:</string>
</property>
<property name="buddy">
<cstring>peakHoldMinColorButton</cstring>
@ -88,7 +88,7 @@
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Average color:</string>
<string>Average &amp;color:</string>
</property>
<property name="buddy">
<cstring>averageColorButton</cstring>
@ -111,7 +111,7 @@
<item row="4" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Persistence color:</string>
<string>Persistence co&amp;lor:</string>
</property>
<property name="buddy">
<cstring>persistenceColorButton</cstring>
@ -131,6 +131,29 @@
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>&amp;Baseline color:</string>
</property>
<property name="buddy">
<cstring>baselineColorButton</cstring>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="ColorButton" name="baselineColorButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
@ -171,7 +194,7 @@
<tabstop>peakHoldMinColorButton</tabstop>
<tabstop>averageColorButton</tabstop>
<tabstop>persistenceColorButton</tabstop>
<tabstop>buttonBox</tabstop>
<tabstop>baselineColorButton</tabstop>
</tabstops>
<resources/>
<connections>
@ -182,12 +205,12 @@
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
<x>246</x>
<y>259</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
<y>265</y>
</hint>
</hints>
</connection>
@ -198,12 +221,12 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
<x>246</x>
<y>259</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
<x>252</x>
<y>265</y>
</hint>
</hints>
</connection>

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>420</width>
<height>255</height>
<width>600</width>
<height>388</height>
</rect>
</property>
<property name="windowTitle">
@ -76,6 +76,12 @@
</item>
<item>
<widget class="QToolButton" name="executableButton">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
@ -83,20 +89,17 @@
</item>
</layout>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Device:</string>
<string>&amp;Device:</string>
</property>
<property name="buddy">
<cstring>deviceEdit</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="deviceEdit"/>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Sa&amp;mple rate:</string>
@ -106,23 +109,7 @@
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="sampleRateSpinBox">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>25000000</number>
</property>
<property name="singleStep">
<number>10000</number>
</property>
<property name="value">
<number>2560000</number>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Waterfall history size:</string>
@ -132,7 +119,7 @@
</property>
</widget>
</item>
<item row="4" column="1">
<item row="7" column="1">
<widget class="QSpinBox" name="waterfallHistorySizeSpinBox">
<property name="minimum">
<number>1</number>
@ -145,6 +132,157 @@
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Bandwidt&amp;h:</string>
</property>
<property name="buddy">
<cstring>bandwidthSpinBox</cstring>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_8">
<property name="toolTip">
<string>Negative frequency for upconverters, positive frequency for downconverters.</string>
</property>
<property name="text">
<string>&amp;LNB LO:</string>
</property>
<property name="buddy">
<cstring>lnbSpinBox</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Add&amp;itional parameters:</string>
</property>
<property name="buddy">
<cstring>paramsEdit</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="paramsEdit"/>
</item>
<item>
<widget class="QToolButton" name="paramsHelpButton">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string> ? </string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="deviceEdit"/>
</item>
<item>
<widget class="QToolButton" name="deviceHelpButton">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string> ? </string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="sampleRateSpinBox">
<property name="showGroupSeparator" stdset="0">
<bool>true</bool>
</property>
<property name="suffix">
<string> MHz</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>999999.989999999990687</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>61.439999999999998</double>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="bandwidthSpinBox">
<property name="showGroupSeparator" stdset="0">
<bool>true</bool>
</property>
<property name="suffix">
<string> MHz</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>999999.989999999990687</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="lnbSpinBox">
<property name="toolTip">
<string>Negative frequency for upconverters, positive frequency for downconverters.</string>
</property>
<property name="showGroupSeparator" stdset="0">
<bool>true</bool>
</property>
<property name="suffix">
<string> MHz</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>-999999.998999999952503</double>
</property>
<property name="maximum">
<double>999999.998999999952503</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
@ -176,10 +314,14 @@
<tabstop>backendComboBox</tabstop>
<tabstop>executableEdit</tabstop>
<tabstop>executableButton</tabstop>
<tabstop>paramsEdit</tabstop>
<tabstop>paramsHelpButton</tabstop>
<tabstop>deviceEdit</tabstop>
<tabstop>deviceHelpButton</tabstop>
<tabstop>sampleRateSpinBox</tabstop>
<tabstop>bandwidthSpinBox</tabstop>
<tabstop>lnbSpinBox</tabstop>
<tabstop>waterfallHistorySizeSpinBox</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections>
@ -190,8 +332,8 @@
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>242</x>
<y>248</y>
<x>254</x>
<y>377</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
@ -206,8 +348,8 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>310</x>
<y>248</y>
<x>322</x>
<y>377</y>
</hint>
<hint type="destinationlabel">
<x>286</x>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QSpectrumAnalyzerSettingsHelp</class>
<widget class="QDialog" name="QSpectrumAnalyzerSettingsHelp">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1200</width>
<height>700</height>
</rect>
</property>
<property name="windowTitle">
<string>Help - QSpectrumAnalyzer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPlainTextEdit" name="helpTextEdit">
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>helpTextEdit</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QSpectrumAnalyzerSettingsHelp</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>224</x>
<y>672</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QSpectrumAnalyzerSettingsHelp</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>292</x>
<y>678</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QSpectrumAnalyzerSmooth</class>
<widget class="QDialog" name="QSpectrumAnalyzerSmooth">
<class>QSpectrumAnalyzerSmoothing</class>
<widget class="QDialog" name="QSpectrumAnalyzerSmoothing">
<property name="geometry">
<rect>
<x>0</x>
@ -118,7 +118,7 @@
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QSpectrumAnalyzerSmooth</receiver>
<receiver>QSpectrumAnalyzerSmoothing</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
@ -134,7 +134,7 @@
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QSpectrumAnalyzerSmooth</receiver>
<receiver>QSpectrumAnalyzerSmoothing</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">

View File

@ -0,0 +1,139 @@
from Qt import QtCore, QtGui, QtWidgets
from qspectrumanalyzer import backends
from qspectrumanalyzer.ui_qspectrumanalyzer_settings import Ui_QSpectrumAnalyzerSettings
from qspectrumanalyzer.ui_qspectrumanalyzer_settings_help import Ui_QSpectrumAnalyzerSettingsHelp
class QSpectrumAnalyzerSettings(QtWidgets.QDialog, Ui_QSpectrumAnalyzerSettings):
"""QSpectrumAnalyzer settings dialog"""
def __init__(self, parent=None):
# Initialize UI
super().__init__(parent)
self.setupUi(self)
self.params_help_dialog = None
self.device_help_dialog = None
# Load settings
settings = QtCore.QSettings()
self.executableEdit.setText(settings.value("executable", "soapy_power"))
self.deviceEdit.setText(settings.value("device", ""))
self.lnbSpinBox.setValue(settings.value("lnb_lo", 0, float) / 1e6)
self.waterfallHistorySizeSpinBox.setValue(settings.value("waterfall_history_size", 100, int))
backend = settings.value("backend", "soapy_power")
try:
backend_module = getattr(backends, backend)
except AttributeError:
backend_module = backends.soapy_power
self.paramsEdit.setText(settings.value("params", backend_module.Info.additional_params))
self.deviceHelpButton.setEnabled(bool(backend_module.Info.help_device))
self.sampleRateSpinBox.setMinimum(backend_module.Info.sample_rate_min / 1e6)
self.sampleRateSpinBox.setMaximum(backend_module.Info.sample_rate_max / 1e6)
self.sampleRateSpinBox.setValue(settings.value("sample_rate", backend_module.Info.sample_rate, float) / 1e6)
self.bandwidthSpinBox.setMinimum(backend_module.Info.bandwidth_min / 1e6)
self.bandwidthSpinBox.setMaximum(backend_module.Info.bandwidth_max / 1e6)
self.bandwidthSpinBox.setValue(settings.value("bandwidth", backend_module.Info.bandwidth, float) / 1e6)
self.backendComboBox.blockSignals(True)
self.backendComboBox.clear()
for b in sorted(backends.__all__):
self.backendComboBox.addItem(b)
i = self.backendComboBox.findText(backend)
if i == -1:
self.backendComboBox.setCurrentIndex(0)
else:
self.backendComboBox.setCurrentIndex(i)
self.backendComboBox.blockSignals(False)
@QtCore.Slot()
def on_executableButton_clicked(self):
"""Open file dialog when button is clicked"""
filename = QtWidgets.QFileDialog.getOpenFileName(self, self.tr("Select executable - QSpectrumAnalyzer"))[0]
if filename:
self.executableEdit.setText(filename)
@QtCore.Slot()
def on_paramsHelpButton_clicked(self):
"""Open additional parameters help dialog when button is clicked"""
try:
backend_module = getattr(backends, self.backendComboBox.currentText())
except AttributeError:
backend_module = backends.soapy_power
self.params_help_dialog = QSpectrumAnalyzerSettingsHelp(
backend_module.Info.help_params(self.executableEdit.text()),
parent=self
)
self.params_help_dialog.show()
self.params_help_dialog.raise_()
self.params_help_dialog.activateWindow()
@QtCore.Slot()
def on_deviceHelpButton_clicked(self):
"""Open device help dialog when button is clicked"""
try:
backend_module = getattr(backends, self.backendComboBox.currentText())
except AttributeError:
backend_module = backends.soapy_power
self.device_help_dialog = QSpectrumAnalyzerSettingsHelp(
backend_module.Info.help_device(self.executableEdit.text(), self.deviceEdit.text()),
parent=self
)
self.device_help_dialog.show()
self.device_help_dialog.raise_()
self.device_help_dialog.activateWindow()
@QtCore.Slot(str)
def on_backendComboBox_currentIndexChanged(self, text):
"""Change executable when backend is changed"""
self.executableEdit.setText(text)
self.deviceEdit.setText("")
try:
backend_module = getattr(backends, text)
except AttributeError:
backend_module = backends.soapy_power
self.paramsEdit.setText(backend_module.Info.additional_params)
self.deviceHelpButton.setEnabled(bool(backend_module.Info.help_device))
self.sampleRateSpinBox.setMinimum(backend_module.Info.sample_rate_min / 1e6)
self.sampleRateSpinBox.setMaximum(backend_module.Info.sample_rate_max / 1e6)
self.sampleRateSpinBox.setValue(backend_module.Info.sample_rate / 1e6)
self.bandwidthSpinBox.setMinimum(backend_module.Info.bandwidth_min / 1e6)
self.bandwidthSpinBox.setMaximum(backend_module.Info.bandwidth_max / 1e6)
self.bandwidthSpinBox.setValue(backend_module.Info.bandwidth / 1e6)
def accept(self):
"""Save settings when dialog is accepted"""
settings = QtCore.QSettings()
settings.setValue("backend", self.backendComboBox.currentText())
settings.setValue("executable", self.executableEdit.text())
settings.setValue("params", self.paramsEdit.text())
settings.setValue("device", self.deviceEdit.text())
settings.setValue("sample_rate", self.sampleRateSpinBox.value() * 1e6)
settings.setValue("bandwidth", self.bandwidthSpinBox.value() * 1e6)
settings.setValue("lnb_lo", self.lnbSpinBox.value() * 1e6)
settings.setValue("waterfall_history_size", self.waterfallHistorySizeSpinBox.value())
QtWidgets.QDialog.accept(self)
class QSpectrumAnalyzerSettingsHelp(QtWidgets.QDialog, Ui_QSpectrumAnalyzerSettingsHelp):
"""QSpectrumAnalyzer settings help dialog"""
def __init__(self, text, parent=None):
# Initialize UI
super().__init__(parent)
self.setupUi(self)
monospace_font = QtGui.QFont('monospace')
monospace_font.setStyleHint(QtGui.QFont.Monospace)
self.helpTextEdit.setFont(monospace_font)
self.helpTextEdit.setPlainText(text)

View File

@ -0,0 +1,29 @@
from Qt import QtCore, QtWidgets
from qspectrumanalyzer.ui_qspectrumanalyzer_smoothing import Ui_QSpectrumAnalyzerSmoothing
class QSpectrumAnalyzerSmoothing(QtWidgets.QDialog, Ui_QSpectrumAnalyzerSmoothing):
"""QSpectrumAnalyzer spectrum smoothing dialog"""
def __init__(self, parent=None):
# Initialize UI
super().__init__(parent)
self.setupUi(self)
# Load settings
settings = QtCore.QSettings()
self.windowLengthSpinBox.setValue(settings.value("smooth_length", 11, int))
window_function = settings.value("smooth_window", "hanning")
i = self.windowFunctionComboBox.findText(window_function)
if i == -1:
self.windowFunctionComboBox.setCurrentIndex(0)
else:
self.windowFunctionComboBox.setCurrentIndex(i)
def accept(self):
"""Save settings when dialog is accepted"""
settings = QtCore.QSettings()
settings.setValue("smooth_length", self.windowLengthSpinBox.value())
settings.setValue("smooth_window", self.windowFunctionComboBox.currentText())
QtWidgets.QDialog.accept(self)

View File

@ -0,0 +1,81 @@
import sys, subprocess
# Basic attributes and exceptions
PIPE = subprocess.PIPE
STDOUT = subprocess.STDOUT
DEVNULL = subprocess.DEVNULL
SubprocessError = subprocess.SubprocessError
TimeoutExpired = subprocess.TimeoutExpired
CalledProcessError = subprocess.CalledProcessError
# Windows-only attributes and functions
if sys.platform == 'win32':
import msvcrt
import _winapi
# creationflags
CREATE_NEW_CONSOLE = subprocess.CREATE_NEW_CONSOLE
CREATE_NEW_PROCESS_GROUP = subprocess.CREATE_NEW_PROCESS_GROUP
# startupinfo
STARTUPINFO = subprocess.STARTUPINFO
STARTF_USESTDHANDLES = subprocess.STARTF_USESTDHANDLES
STARTF_USESHOWWINDOW = subprocess.STARTF_USESHOWWINDOW
SW_HIDE = subprocess.SW_HIDE
# file handles
Handle = subprocess.Handle
STD_INPUT_HANDLE = subprocess.STD_INPUT_HANDLE
STD_OUTPUT_HANDLE = subprocess.STD_OUTPUT_HANDLE
STD_ERROR_HANDLE = subprocess.STD_ERROR_HANDLE
def make_inheritable_handle(fd):
"""Create inheritable duplicate of handle from file descriptor"""
h = _winapi.DuplicateHandle(
_winapi.GetCurrentProcess(),
msvcrt.get_osfhandle(fd),
_winapi.GetCurrentProcess(), 0, 1,
_winapi.DUPLICATE_SAME_ACCESS
)
return subprocess.Handle(h)
def hide_console_window(startupinfo=None):
"""Returns altered startupinfo to hide console window on Windows"""
if sys.platform != 'win32':
return None
if not startupinfo:
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
return startupinfo
class Popen(subprocess.Popen):
"""subprocess.Popen with ability to hide console window on Windows"""
def __init__(self, *pargs, console=True, **kwargs):
if not console:
kwargs['startupinfo'] = hide_console_window(kwargs.get('startupinfo'))
super().__init__(*pargs, **kwargs)
def call(*pargs, console=True, **kwargs):
"""subprocess.call with ability to hide console window on Windows"""
if not console:
kwargs['startupinfo'] = hide_console_window(kwargs.get('startupinfo'))
return subprocess.call(*pargs, **kwargs)
def check_call(*pargs, console=True, **kwargs):
"""subprocess.check_call with ability to hide console window on Windows"""
if not console:
kwargs['startupinfo'] = hide_console_window(kwargs.get('startupinfo'))
return subprocess.check_call(*pargs, **kwargs)
def check_output(*pargs, console=True, **kwargs):
"""subprocess.check_output with ability to hide console window on Windows"""
if not console:
kwargs['startupinfo'] = hide_console_window(kwargs.get('startupinfo'))
return subprocess.check_output(*pargs, **kwargs)

View File

@ -2,273 +2,275 @@
# Form implementation generated from reading ui file 'qspectrumanalyzer/qspectrumanalyzer.ui'
#
# Created by: PyQt4 UI code generator 4.12
# Created by: PyQt5 UI code generator 5.8
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
from Qt import QtCore, QtGui, QtWidgets
class Ui_QSpectrumAnalyzerMainWindow(object):
def setupUi(self, QSpectrumAnalyzerMainWindow):
QSpectrumAnalyzerMainWindow.setObjectName(_fromUtf8("QSpectrumAnalyzerMainWindow"))
QSpectrumAnalyzerMainWindow.resize(1200, 810)
self.centralwidget = QtGui.QWidget(QSpectrumAnalyzerMainWindow)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
self.plotSplitter = QtGui.QSplitter(self.centralwidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
QSpectrumAnalyzerMainWindow.setObjectName("QSpectrumAnalyzerMainWindow")
QSpectrumAnalyzerMainWindow.resize(1200, 892)
self.centralwidget = QtWidgets.QWidget(QSpectrumAnalyzerMainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.plotSplitter = QtWidgets.QSplitter(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.plotSplitter.sizePolicy().hasHeightForWidth())
self.plotSplitter.setSizePolicy(sizePolicy)
self.plotSplitter.setOrientation(QtCore.Qt.Vertical)
self.plotSplitter.setObjectName(_fromUtf8("plotSplitter"))
self.plotSplitter.setObjectName("plotSplitter")
self.mainPlotLayout = GraphicsLayoutWidget(self.plotSplitter)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.mainPlotLayout.sizePolicy().hasHeightForWidth())
self.mainPlotLayout.setSizePolicy(sizePolicy)
self.mainPlotLayout.setObjectName(_fromUtf8("mainPlotLayout"))
self.mainPlotLayout.setObjectName("mainPlotLayout")
self.waterfallPlotLayout = GraphicsLayoutWidget(self.plotSplitter)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.waterfallPlotLayout.sizePolicy().hasHeightForWidth())
self.waterfallPlotLayout.setSizePolicy(sizePolicy)
self.waterfallPlotLayout.setObjectName(_fromUtf8("waterfallPlotLayout"))
self.waterfallPlotLayout.setObjectName("waterfallPlotLayout")
self.horizontalLayout.addWidget(self.plotSplitter)
QSpectrumAnalyzerMainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(QSpectrumAnalyzerMainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1200, 30))
self.menubar.setObjectName(_fromUtf8("menubar"))
self.menu_File = QtGui.QMenu(self.menubar)
self.menu_File.setObjectName(_fromUtf8("menu_File"))
self.menu_Help = QtGui.QMenu(self.menubar)
self.menu_Help.setObjectName(_fromUtf8("menu_Help"))
self.menubar = QtWidgets.QMenuBar(QSpectrumAnalyzerMainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1200, 32))
self.menubar.setObjectName("menubar")
self.menu_File = QtWidgets.QMenu(self.menubar)
self.menu_File.setObjectName("menu_File")
self.menu_Help = QtWidgets.QMenu(self.menubar)
self.menu_Help.setObjectName("menu_Help")
QSpectrumAnalyzerMainWindow.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(QSpectrumAnalyzerMainWindow)
self.statusbar.setObjectName(_fromUtf8("statusbar"))
self.statusbar = QtWidgets.QStatusBar(QSpectrumAnalyzerMainWindow)
self.statusbar.setObjectName("statusbar")
QSpectrumAnalyzerMainWindow.setStatusBar(self.statusbar)
self.controlsDockWidget = QtGui.QDockWidget(QSpectrumAnalyzerMainWindow)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)
self.controlsDockWidget = QtWidgets.QDockWidget(QSpectrumAnalyzerMainWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.controlsDockWidget.sizePolicy().hasHeightForWidth())
self.controlsDockWidget.setSizePolicy(sizePolicy)
self.controlsDockWidget.setMinimumSize(QtCore.QSize(10, 10))
self.controlsDockWidget.setFeatures(QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetMovable)
self.controlsDockWidget.setObjectName(_fromUtf8("controlsDockWidget"))
self.controlsDockWidgetContents = QtGui.QWidget()
self.controlsDockWidgetContents.setObjectName(_fromUtf8("controlsDockWidgetContents"))
self.gridLayout_2 = QtGui.QGridLayout(self.controlsDockWidgetContents)
self.gridLayout_2.setMargin(0)
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
self.startButton = QtGui.QPushButton(self.controlsDockWidgetContents)
self.startButton.setObjectName(_fromUtf8("startButton"))
self.controlsDockWidget.setMinimumSize(QtCore.QSize(190, 130))
self.controlsDockWidget.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable|QtWidgets.QDockWidget.DockWidgetMovable)
self.controlsDockWidget.setObjectName("controlsDockWidget")
self.controlsDockWidgetContents = QtWidgets.QWidget()
self.controlsDockWidgetContents.setObjectName("controlsDockWidgetContents")
self.gridLayout_2 = QtWidgets.QGridLayout(self.controlsDockWidgetContents)
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.gridLayout_2.setObjectName("gridLayout_2")
self.startButton = QtWidgets.QPushButton(self.controlsDockWidgetContents)
self.startButton.setObjectName("startButton")
self.gridLayout_2.addWidget(self.startButton, 0, 0, 1, 1)
self.stopButton = QtGui.QPushButton(self.controlsDockWidgetContents)
self.stopButton.setObjectName(_fromUtf8("stopButton"))
self.stopButton = QtWidgets.QPushButton(self.controlsDockWidgetContents)
self.stopButton.setObjectName("stopButton")
self.gridLayout_2.addWidget(self.stopButton, 0, 1, 1, 1)
self.singleShotButton = QtGui.QPushButton(self.controlsDockWidgetContents)
self.singleShotButton.setObjectName(_fromUtf8("singleShotButton"))
self.singleShotButton = QtWidgets.QPushButton(self.controlsDockWidgetContents)
self.singleShotButton.setObjectName("singleShotButton")
self.gridLayout_2.addWidget(self.singleShotButton, 1, 0, 1, 2)
spacerItem = QtGui.QSpacerItem(20, 561, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
spacerItem = QtWidgets.QSpacerItem(20, 561, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_2.addItem(spacerItem, 2, 0, 1, 1)
self.controlsDockWidget.setWidget(self.controlsDockWidgetContents)
QSpectrumAnalyzerMainWindow.addDockWidget(QtCore.Qt.DockWidgetArea(2), self.controlsDockWidget)
self.frequencyDockWidget = QtGui.QDockWidget(QSpectrumAnalyzerMainWindow)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)
self.frequencyDockWidget = QtWidgets.QDockWidget(QSpectrumAnalyzerMainWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.frequencyDockWidget.sizePolicy().hasHeightForWidth())
self.frequencyDockWidget.setSizePolicy(sizePolicy)
self.frequencyDockWidget.setMinimumSize(QtCore.QSize(10, 10))
self.frequencyDockWidget.setFeatures(QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetMovable)
self.frequencyDockWidget.setObjectName(_fromUtf8("frequencyDockWidget"))
self.frequencyDockWidgetContents = QtGui.QWidget()
self.frequencyDockWidgetContents.setObjectName(_fromUtf8("frequencyDockWidgetContents"))
self.formLayout = QtGui.QFormLayout(self.frequencyDockWidgetContents)
self.formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.ExpandingFieldsGrow)
self.formLayout.setMargin(0)
self.formLayout.setObjectName(_fromUtf8("formLayout"))
self.label_2 = QtGui.QLabel(self.frequencyDockWidgetContents)
self.label_2.setObjectName(_fromUtf8("label_2"))
self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label_2)
self.startFreqSpinBox = QtGui.QDoubleSpinBox(self.frequencyDockWidgetContents)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.frequencyDockWidget.setMinimumSize(QtCore.QSize(208, 166))
self.frequencyDockWidget.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable|QtWidgets.QDockWidget.DockWidgetMovable)
self.frequencyDockWidget.setObjectName("frequencyDockWidget")
self.frequencyDockWidgetContents = QtWidgets.QWidget()
self.frequencyDockWidgetContents.setObjectName("frequencyDockWidgetContents")
self.formLayout = QtWidgets.QFormLayout(self.frequencyDockWidgetContents)
self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow)
self.formLayout.setContentsMargins(0, 0, 0, 0)
self.formLayout.setObjectName("formLayout")
self.label_2 = QtWidgets.QLabel(self.frequencyDockWidgetContents)
self.label_2.setObjectName("label_2")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_2)
self.startFreqSpinBox = QtWidgets.QDoubleSpinBox(self.frequencyDockWidgetContents)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.startFreqSpinBox.sizePolicy().hasHeightForWidth())
self.startFreqSpinBox.setSizePolicy(sizePolicy)
self.startFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.startFreqSpinBox.setProperty("showGroupSeparator", True)
self.startFreqSpinBox.setDecimals(3)
self.startFreqSpinBox.setMinimum(24.0)
self.startFreqSpinBox.setMaximum(1766.0)
self.startFreqSpinBox.setMinimum(0.0)
self.startFreqSpinBox.setMaximum(2200.0)
self.startFreqSpinBox.setProperty("value", 87.0)
self.startFreqSpinBox.setObjectName(_fromUtf8("startFreqSpinBox"))
self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.startFreqSpinBox)
self.label_3 = QtGui.QLabel(self.frequencyDockWidgetContents)
self.label_3.setObjectName(_fromUtf8("label_3"))
self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.label_3)
self.stopFreqSpinBox = QtGui.QDoubleSpinBox(self.frequencyDockWidgetContents)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.startFreqSpinBox.setObjectName("startFreqSpinBox")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.startFreqSpinBox)
self.label_3 = QtWidgets.QLabel(self.frequencyDockWidgetContents)
self.label_3.setObjectName("label_3")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_3)
self.stopFreqSpinBox = QtWidgets.QDoubleSpinBox(self.frequencyDockWidgetContents)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.stopFreqSpinBox.sizePolicy().hasHeightForWidth())
self.stopFreqSpinBox.setSizePolicy(sizePolicy)
self.stopFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.stopFreqSpinBox.setProperty("showGroupSeparator", True)
self.stopFreqSpinBox.setDecimals(3)
self.stopFreqSpinBox.setMinimum(24.0)
self.stopFreqSpinBox.setMaximum(1766.0)
self.stopFreqSpinBox.setMinimum(0.0)
self.stopFreqSpinBox.setMaximum(2200.0)
self.stopFreqSpinBox.setProperty("value", 108.0)
self.stopFreqSpinBox.setObjectName(_fromUtf8("stopFreqSpinBox"))
self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.stopFreqSpinBox)
self.label = QtGui.QLabel(self.frequencyDockWidgetContents)
self.label.setObjectName(_fromUtf8("label"))
self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.label)
self.binSizeSpinBox = QtGui.QDoubleSpinBox(self.frequencyDockWidgetContents)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.stopFreqSpinBox.setObjectName("stopFreqSpinBox")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.stopFreqSpinBox)
self.label = QtWidgets.QLabel(self.frequencyDockWidgetContents)
self.label.setObjectName("label")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label)
self.binSizeSpinBox = QtWidgets.QDoubleSpinBox(self.frequencyDockWidgetContents)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.binSizeSpinBox.sizePolicy().hasHeightForWidth())
self.binSizeSpinBox.setSizePolicy(sizePolicy)
self.binSizeSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.binSizeSpinBox.setProperty("showGroupSeparator", True)
self.binSizeSpinBox.setDecimals(3)
self.binSizeSpinBox.setMaximum(2800.0)
self.binSizeSpinBox.setMinimum(0.0)
self.binSizeSpinBox.setMaximum(10000.0)
self.binSizeSpinBox.setProperty("value", 10.0)
self.binSizeSpinBox.setObjectName(_fromUtf8("binSizeSpinBox"))
self.formLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.binSizeSpinBox)
spacerItem1 = QtGui.QSpacerItem(20, 0, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.formLayout.setItem(3, QtGui.QFormLayout.SpanningRole, spacerItem1)
self.binSizeSpinBox.setObjectName("binSizeSpinBox")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.binSizeSpinBox)
spacerItem1 = QtWidgets.QSpacerItem(20, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.formLayout.setItem(3, QtWidgets.QFormLayout.SpanningRole, spacerItem1)
self.frequencyDockWidget.setWidget(self.frequencyDockWidgetContents)
QSpectrumAnalyzerMainWindow.addDockWidget(QtCore.Qt.DockWidgetArea(2), self.frequencyDockWidget)
self.settingsDockWidget = QtGui.QDockWidget(QSpectrumAnalyzerMainWindow)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
self.settingsDockWidget = QtWidgets.QDockWidget(QSpectrumAnalyzerMainWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.settingsDockWidget.sizePolicy().hasHeightForWidth())
self.settingsDockWidget.setSizePolicy(sizePolicy)
self.settingsDockWidget.setFeatures(QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetMovable)
self.settingsDockWidget.setObjectName(_fromUtf8("settingsDockWidget"))
self.settingsDockWidgetContents = QtGui.QWidget()
self.settingsDockWidgetContents.setObjectName(_fromUtf8("settingsDockWidgetContents"))
self.gridLayout = QtGui.QGridLayout(self.settingsDockWidgetContents)
self.gridLayout.setMargin(0)
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.label_4 = QtGui.QLabel(self.settingsDockWidgetContents)
self.label_4.setObjectName(_fromUtf8("label_4"))
self.settingsDockWidget.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable|QtWidgets.QDockWidget.DockWidgetMovable)
self.settingsDockWidget.setObjectName("settingsDockWidget")
self.settingsDockWidgetContents = QtWidgets.QWidget()
self.settingsDockWidgetContents.setObjectName("settingsDockWidgetContents")
self.gridLayout = QtWidgets.QGridLayout(self.settingsDockWidgetContents)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.label_4 = QtWidgets.QLabel(self.settingsDockWidgetContents)
self.label_4.setObjectName("label_4")
self.gridLayout.addWidget(self.label_4, 0, 0, 1, 1)
self.label_6 = QtGui.QLabel(self.settingsDockWidgetContents)
self.label_6.setObjectName(_fromUtf8("label_6"))
self.label_6 = QtWidgets.QLabel(self.settingsDockWidgetContents)
self.label_6.setObjectName("label_6")
self.gridLayout.addWidget(self.label_6, 0, 1, 1, 1)
self.intervalSpinBox = QtGui.QDoubleSpinBox(self.settingsDockWidgetContents)
self.intervalSpinBox = QtWidgets.QDoubleSpinBox(self.settingsDockWidgetContents)
self.intervalSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.intervalSpinBox.setMaximum(999.0)
self.intervalSpinBox.setProperty("value", 1.0)
self.intervalSpinBox.setObjectName(_fromUtf8("intervalSpinBox"))
self.intervalSpinBox.setObjectName("intervalSpinBox")
self.gridLayout.addWidget(self.intervalSpinBox, 1, 0, 1, 1)
self.gainSpinBox = QtGui.QSpinBox(self.settingsDockWidgetContents)
self.gainSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.gainSpinBox.setMinimum(-1)
self.gainSpinBox.setMaximum(49)
self.gainSpinBox.setProperty("value", -1)
self.gainSpinBox.setObjectName(_fromUtf8("gainSpinBox"))
self.gridLayout.addWidget(self.gainSpinBox, 1, 1, 1, 2)
self.label_5 = QtGui.QLabel(self.settingsDockWidgetContents)
self.label_5.setObjectName(_fromUtf8("label_5"))
self.label_5 = QtWidgets.QLabel(self.settingsDockWidgetContents)
self.label_5.setObjectName("label_5")
self.gridLayout.addWidget(self.label_5, 2, 0, 1, 1)
self.label_7 = QtGui.QLabel(self.settingsDockWidgetContents)
self.label_7.setObjectName(_fromUtf8("label_7"))
self.label_7 = QtWidgets.QLabel(self.settingsDockWidgetContents)
self.label_7.setObjectName("label_7")
self.gridLayout.addWidget(self.label_7, 2, 1, 1, 1)
self.ppmSpinBox = QtGui.QSpinBox(self.settingsDockWidgetContents)
self.ppmSpinBox = QtWidgets.QSpinBox(self.settingsDockWidgetContents)
self.ppmSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.ppmSpinBox.setMinimum(-999)
self.ppmSpinBox.setMaximum(999)
self.ppmSpinBox.setObjectName(_fromUtf8("ppmSpinBox"))
self.ppmSpinBox.setObjectName("ppmSpinBox")
self.gridLayout.addWidget(self.ppmSpinBox, 3, 0, 1, 1)
self.mainCurveCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents)
self.mainCurveCheckBox = QtWidgets.QCheckBox(self.settingsDockWidgetContents)
self.mainCurveCheckBox.setChecked(True)
self.mainCurveCheckBox.setObjectName(_fromUtf8("mainCurveCheckBox"))
self.mainCurveCheckBox.setObjectName("mainCurveCheckBox")
self.gridLayout.addWidget(self.mainCurveCheckBox, 4, 0, 1, 1)
self.colorsButton = QtGui.QPushButton(self.settingsDockWidgetContents)
self.colorsButton.setObjectName(_fromUtf8("colorsButton"))
self.colorsButton = QtWidgets.QPushButton(self.settingsDockWidgetContents)
self.colorsButton.setObjectName("colorsButton")
self.gridLayout.addWidget(self.colorsButton, 4, 1, 1, 2)
self.peakHoldMaxCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents)
self.peakHoldMaxCheckBox.setObjectName(_fromUtf8("peakHoldMaxCheckBox"))
self.peakHoldMaxCheckBox = QtWidgets.QCheckBox(self.settingsDockWidgetContents)
self.peakHoldMaxCheckBox.setObjectName("peakHoldMaxCheckBox")
self.gridLayout.addWidget(self.peakHoldMaxCheckBox, 5, 0, 1, 1)
self.peakHoldMinCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents)
self.peakHoldMinCheckBox.setObjectName(_fromUtf8("peakHoldMinCheckBox"))
self.peakHoldMinCheckBox = QtWidgets.QCheckBox(self.settingsDockWidgetContents)
self.peakHoldMinCheckBox.setObjectName("peakHoldMinCheckBox")
self.gridLayout.addWidget(self.peakHoldMinCheckBox, 5, 1, 1, 2)
self.averageCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents)
self.averageCheckBox.setObjectName(_fromUtf8("averageCheckBox"))
self.averageCheckBox = QtWidgets.QCheckBox(self.settingsDockWidgetContents)
self.averageCheckBox.setObjectName("averageCheckBox")
self.gridLayout.addWidget(self.averageCheckBox, 6, 0, 1, 1)
self.smoothCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents)
self.smoothCheckBox.setObjectName(_fromUtf8("smoothCheckBox"))
self.smoothCheckBox = QtWidgets.QCheckBox(self.settingsDockWidgetContents)
self.smoothCheckBox.setObjectName("smoothCheckBox")
self.gridLayout.addWidget(self.smoothCheckBox, 7, 0, 1, 1)
self.smoothButton = QtGui.QToolButton(self.settingsDockWidgetContents)
self.smoothButton = QtWidgets.QToolButton(self.settingsDockWidgetContents)
self.smoothButton.setAutoRaise(False)
self.smoothButton.setObjectName(_fromUtf8("smoothButton"))
self.smoothButton.setObjectName("smoothButton")
self.gridLayout.addWidget(self.smoothButton, 7, 2, 1, 1)
self.persistenceCheckBox = QtGui.QCheckBox(self.settingsDockWidgetContents)
self.persistenceCheckBox.setObjectName(_fromUtf8("persistenceCheckBox"))
self.persistenceCheckBox = QtWidgets.QCheckBox(self.settingsDockWidgetContents)
self.persistenceCheckBox.setObjectName("persistenceCheckBox")
self.gridLayout.addWidget(self.persistenceCheckBox, 8, 0, 1, 1)
self.persistenceButton = QtGui.QToolButton(self.settingsDockWidgetContents)
self.persistenceButton = QtWidgets.QToolButton(self.settingsDockWidgetContents)
self.persistenceButton.setAutoRaise(False)
self.persistenceButton.setObjectName(_fromUtf8("persistenceButton"))
self.persistenceButton.setObjectName("persistenceButton")
self.gridLayout.addWidget(self.persistenceButton, 8, 2, 1, 1)
spacerItem2 = QtGui.QSpacerItem(20, 1, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout.addItem(spacerItem2, 9, 0, 1, 1)
self.cropSpinBox = QtGui.QSpinBox(self.settingsDockWidgetContents)
spacerItem2 = QtWidgets.QSpacerItem(20, 1, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout.addItem(spacerItem2, 11, 0, 1, 1)
self.cropSpinBox = QtWidgets.QSpinBox(self.settingsDockWidgetContents)
self.cropSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.cropSpinBox.setObjectName(_fromUtf8("cropSpinBox"))
self.cropSpinBox.setObjectName("cropSpinBox")
self.gridLayout.addWidget(self.cropSpinBox, 3, 1, 1, 2)
self.gainSpinBox = QtWidgets.QDoubleSpinBox(self.settingsDockWidgetContents)
self.gainSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.gainSpinBox.setDecimals(1)
self.gainSpinBox.setMinimum(-1.0)
self.gainSpinBox.setMaximum(999.0)
self.gainSpinBox.setSingleStep(1.0)
self.gainSpinBox.setProperty("value", -1.0)
self.gainSpinBox.setObjectName("gainSpinBox")
self.gridLayout.addWidget(self.gainSpinBox, 1, 1, 1, 2)
self.baselineCheckBox = QtWidgets.QCheckBox(self.settingsDockWidgetContents)
self.baselineCheckBox.setObjectName("baselineCheckBox")
self.gridLayout.addWidget(self.baselineCheckBox, 9, 0, 1, 1)
self.baselineButton = QtWidgets.QToolButton(self.settingsDockWidgetContents)
self.baselineButton.setAutoRaise(False)
self.baselineButton.setObjectName("baselineButton")
self.gridLayout.addWidget(self.baselineButton, 9, 2, 1, 1)
self.subtractBaselineCheckBox = QtWidgets.QCheckBox(self.settingsDockWidgetContents)
self.subtractBaselineCheckBox.setObjectName("subtractBaselineCheckBox")
self.gridLayout.addWidget(self.subtractBaselineCheckBox, 10, 0, 1, 1)
self.settingsDockWidget.setWidget(self.settingsDockWidgetContents)
QSpectrumAnalyzerMainWindow.addDockWidget(QtCore.Qt.DockWidgetArea(2), self.settingsDockWidget)
self.levelsDockWidget = QtGui.QDockWidget(QSpectrumAnalyzerMainWindow)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
self.levelsDockWidget = QtWidgets.QDockWidget(QSpectrumAnalyzerMainWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.levelsDockWidget.sizePolicy().hasHeightForWidth())
self.levelsDockWidget.setSizePolicy(sizePolicy)
self.levelsDockWidget.setFeatures(QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetMovable)
self.levelsDockWidget.setObjectName(_fromUtf8("levelsDockWidget"))
self.levelsDockWidgetContents = QtGui.QWidget()
self.levelsDockWidgetContents.setObjectName(_fromUtf8("levelsDockWidgetContents"))
self.verticalLayout_6 = QtGui.QVBoxLayout(self.levelsDockWidgetContents)
self.verticalLayout_6.setMargin(0)
self.verticalLayout_6.setObjectName(_fromUtf8("verticalLayout_6"))
self.levelsDockWidget.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable|QtWidgets.QDockWidget.DockWidgetMovable)
self.levelsDockWidget.setObjectName("levelsDockWidget")
self.levelsDockWidgetContents = QtWidgets.QWidget()
self.levelsDockWidgetContents.setObjectName("levelsDockWidgetContents")
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.levelsDockWidgetContents)
self.verticalLayout_6.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_6.setObjectName("verticalLayout_6")
self.histogramPlotLayout = GraphicsLayoutWidget(self.levelsDockWidgetContents)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Expanding)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.histogramPlotLayout.sizePolicy().hasHeightForWidth())
self.histogramPlotLayout.setSizePolicy(sizePolicy)
self.histogramPlotLayout.setObjectName(_fromUtf8("histogramPlotLayout"))
self.histogramPlotLayout.setObjectName("histogramPlotLayout")
self.verticalLayout_6.addWidget(self.histogramPlotLayout)
self.levelsDockWidget.setWidget(self.levelsDockWidgetContents)
QSpectrumAnalyzerMainWindow.addDockWidget(QtCore.Qt.DockWidgetArea(2), self.levelsDockWidget)
self.action_Settings = QtGui.QAction(QSpectrumAnalyzerMainWindow)
self.action_Settings.setObjectName(_fromUtf8("action_Settings"))
self.action_Quit = QtGui.QAction(QSpectrumAnalyzerMainWindow)
self.action_Quit.setObjectName(_fromUtf8("action_Quit"))
self.action_About = QtGui.QAction(QSpectrumAnalyzerMainWindow)
self.action_About.setObjectName(_fromUtf8("action_About"))
self.action_Settings = QtWidgets.QAction(QSpectrumAnalyzerMainWindow)
self.action_Settings.setObjectName("action_Settings")
self.action_Quit = QtWidgets.QAction(QSpectrumAnalyzerMainWindow)
self.action_Quit.setObjectName("action_Quit")
self.action_About = QtWidgets.QAction(QSpectrumAnalyzerMainWindow)
self.action_About.setObjectName("action_About")
self.menu_File.addAction(self.action_Settings)
self.menu_File.addSeparator()
self.menu_File.addAction(self.action_Quit)
@ -303,44 +305,51 @@ class Ui_QSpectrumAnalyzerMainWindow(object):
QSpectrumAnalyzerMainWindow.setTabOrder(self.smoothCheckBox, self.smoothButton)
QSpectrumAnalyzerMainWindow.setTabOrder(self.smoothButton, self.persistenceCheckBox)
QSpectrumAnalyzerMainWindow.setTabOrder(self.persistenceCheckBox, self.persistenceButton)
QSpectrumAnalyzerMainWindow.setTabOrder(self.persistenceButton, self.histogramPlotLayout)
QSpectrumAnalyzerMainWindow.setTabOrder(self.persistenceButton, self.baselineCheckBox)
QSpectrumAnalyzerMainWindow.setTabOrder(self.baselineCheckBox, self.baselineButton)
QSpectrumAnalyzerMainWindow.setTabOrder(self.baselineButton, self.subtractBaselineCheckBox)
QSpectrumAnalyzerMainWindow.setTabOrder(self.subtractBaselineCheckBox, self.histogramPlotLayout)
QSpectrumAnalyzerMainWindow.setTabOrder(self.histogramPlotLayout, self.mainPlotLayout)
QSpectrumAnalyzerMainWindow.setTabOrder(self.mainPlotLayout, self.waterfallPlotLayout)
def retranslateUi(self, QSpectrumAnalyzerMainWindow):
QSpectrumAnalyzerMainWindow.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "QSpectrumAnalyzer", None))
self.menu_File.setTitle(_translate("QSpectrumAnalyzerMainWindow", "&File", None))
self.menu_Help.setTitle(_translate("QSpectrumAnalyzerMainWindow", "&Help", None))
self.controlsDockWidget.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "Controls", None))
self.startButton.setText(_translate("QSpectrumAnalyzerMainWindow", "&Start", None))
self.stopButton.setText(_translate("QSpectrumAnalyzerMainWindow", "S&top", None))
self.singleShotButton.setText(_translate("QSpectrumAnalyzerMainWindow", "Si&ngle shot", None))
self.frequencyDockWidget.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "Frequency", None))
self.label_2.setText(_translate("QSpectrumAnalyzerMainWindow", "Start:", None))
self.startFreqSpinBox.setSuffix(_translate("QSpectrumAnalyzerMainWindow", " MHz", None))
self.label_3.setText(_translate("QSpectrumAnalyzerMainWindow", "Stop:", None))
self.stopFreqSpinBox.setSuffix(_translate("QSpectrumAnalyzerMainWindow", " MHz", None))
self.label.setText(_translate("QSpectrumAnalyzerMainWindow", "Bin size:", None))
self.binSizeSpinBox.setSuffix(_translate("QSpectrumAnalyzerMainWindow", " kHz", None))
self.settingsDockWidget.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "Settings", None))
self.label_4.setText(_translate("QSpectrumAnalyzerMainWindow", "Interval [s]:", None))
self.label_6.setText(_translate("QSpectrumAnalyzerMainWindow", "Gain [dB]:", None))
self.gainSpinBox.setSpecialValueText(_translate("QSpectrumAnalyzerMainWindow", "auto", None))
self.label_5.setText(_translate("QSpectrumAnalyzerMainWindow", "Corr. [ppm]:", None))
self.label_7.setText(_translate("QSpectrumAnalyzerMainWindow", "Crop [%]:", None))
self.mainCurveCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Main curve", None))
self.colorsButton.setText(_translate("QSpectrumAnalyzerMainWindow", "Colors...", None))
self.peakHoldMaxCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Max. hold", None))
self.peakHoldMinCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Min. hold", None))
self.averageCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Average", None))
self.smoothCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Smoothing", None))
self.smoothButton.setText(_translate("QSpectrumAnalyzerMainWindow", "...", None))
self.persistenceCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Persistence", None))
self.persistenceButton.setText(_translate("QSpectrumAnalyzerMainWindow", "...", None))
self.levelsDockWidget.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "Levels", None))
self.action_Settings.setText(_translate("QSpectrumAnalyzerMainWindow", "&Settings...", None))
self.action_Quit.setText(_translate("QSpectrumAnalyzerMainWindow", "&Quit", None))
self.action_Quit.setShortcut(_translate("QSpectrumAnalyzerMainWindow", "Ctrl+Q", None))
self.action_About.setText(_translate("QSpectrumAnalyzerMainWindow", "&About", None))
_translate = QtCore.QCoreApplication.translate
QSpectrumAnalyzerMainWindow.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "QSpectrumAnalyzer"))
self.menu_File.setTitle(_translate("QSpectrumAnalyzerMainWindow", "&File"))
self.menu_Help.setTitle(_translate("QSpectrumAnalyzerMainWindow", "&Help"))
self.controlsDockWidget.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "Controls"))
self.startButton.setText(_translate("QSpectrumAnalyzerMainWindow", "&Start"))
self.stopButton.setText(_translate("QSpectrumAnalyzerMainWindow", "S&top"))
self.singleShotButton.setText(_translate("QSpectrumAnalyzerMainWindow", "Si&ngle shot"))
self.frequencyDockWidget.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "Frequency"))
self.label_2.setText(_translate("QSpectrumAnalyzerMainWindow", "Start:"))
self.startFreqSpinBox.setSuffix(_translate("QSpectrumAnalyzerMainWindow", " MHz"))
self.label_3.setText(_translate("QSpectrumAnalyzerMainWindow", "Stop:"))
self.stopFreqSpinBox.setSuffix(_translate("QSpectrumAnalyzerMainWindow", " MHz"))
self.label.setText(_translate("QSpectrumAnalyzerMainWindow", "&Bin size:"))
self.binSizeSpinBox.setSuffix(_translate("QSpectrumAnalyzerMainWindow", " kHz"))
self.settingsDockWidget.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "Settings"))
self.label_4.setText(_translate("QSpectrumAnalyzerMainWindow", "&Interval [s]:"))
self.label_6.setText(_translate("QSpectrumAnalyzerMainWindow", "&Gain [dB]:"))
self.label_5.setText(_translate("QSpectrumAnalyzerMainWindow", "Corr. [ppm]:"))
self.label_7.setText(_translate("QSpectrumAnalyzerMainWindow", "Crop [%]:"))
self.mainCurveCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Main curve"))
self.colorsButton.setText(_translate("QSpectrumAnalyzerMainWindow", "Colors..."))
self.peakHoldMaxCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Max. hold"))
self.peakHoldMinCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Min. hold"))
self.averageCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Average"))
self.smoothCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Smoothing"))
self.smoothButton.setText(_translate("QSpectrumAnalyzerMainWindow", "..."))
self.persistenceCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Persistence"))
self.persistenceButton.setText(_translate("QSpectrumAnalyzerMainWindow", "..."))
self.gainSpinBox.setSpecialValueText(_translate("QSpectrumAnalyzerMainWindow", "auto"))
self.baselineCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Baseline"))
self.baselineButton.setText(_translate("QSpectrumAnalyzerMainWindow", "..."))
self.subtractBaselineCheckBox.setText(_translate("QSpectrumAnalyzerMainWindow", "Subtract baseline"))
self.levelsDockWidget.setWindowTitle(_translate("QSpectrumAnalyzerMainWindow", "Levels"))
self.action_Settings.setText(_translate("QSpectrumAnalyzerMainWindow", "&Settings..."))
self.action_Quit.setText(_translate("QSpectrumAnalyzerMainWindow", "&Quit"))
self.action_Quit.setShortcut(_translate("QSpectrumAnalyzerMainWindow", "Ctrl+Q"))
self.action_About.setText(_translate("QSpectrumAnalyzerMainWindow", "&About"))
from pyqtgraph import GraphicsLayoutWidget

View File

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'qspectrumanalyzer/qspectrumanalyzer_baseline.ui'
#
# Created by: PyQt5 UI code generator 5.8
#
# WARNING! All changes made in this file will be lost!
from Qt import QtCore, QtGui, QtWidgets
class Ui_QSpectrumAnalyzerBaseline(object):
def setupUi(self, QSpectrumAnalyzerBaseline):
QSpectrumAnalyzerBaseline.setObjectName("QSpectrumAnalyzerBaseline")
QSpectrumAnalyzerBaseline.resize(500, 100)
self.verticalLayout = QtWidgets.QVBoxLayout(QSpectrumAnalyzerBaseline)
self.verticalLayout.setObjectName("verticalLayout")
self.formLayout = QtWidgets.QFormLayout()
self.formLayout.setObjectName("formLayout")
self.label = QtWidgets.QLabel(QSpectrumAnalyzerBaseline)
self.label.setObjectName("label")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.baselineFileEdit = QtWidgets.QLineEdit(QSpectrumAnalyzerBaseline)
self.baselineFileEdit.setObjectName("baselineFileEdit")
self.horizontalLayout.addWidget(self.baselineFileEdit)
self.baselineFileButton = QtWidgets.QToolButton(QSpectrumAnalyzerBaseline)
self.baselineFileButton.setMinimumSize(QtCore.QSize(50, 0))
self.baselineFileButton.setObjectName("baselineFileButton")
self.horizontalLayout.addWidget(self.baselineFileButton)
self.formLayout.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout)
self.verticalLayout.addLayout(self.formLayout)
spacerItem = QtWidgets.QSpacerItem(20, 1, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.buttonBox = QtWidgets.QDialogButtonBox(QSpectrumAnalyzerBaseline)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout.addWidget(self.buttonBox)
self.label.setBuddy(self.baselineFileEdit)
self.retranslateUi(QSpectrumAnalyzerBaseline)
self.buttonBox.accepted.connect(QSpectrumAnalyzerBaseline.accept)
self.buttonBox.rejected.connect(QSpectrumAnalyzerBaseline.reject)
QtCore.QMetaObject.connectSlotsByName(QSpectrumAnalyzerBaseline)
QSpectrumAnalyzerBaseline.setTabOrder(self.baselineFileEdit, self.baselineFileButton)
def retranslateUi(self, QSpectrumAnalyzerBaseline):
_translate = QtCore.QCoreApplication.translate
QSpectrumAnalyzerBaseline.setWindowTitle(_translate("QSpectrumAnalyzerBaseline", "Baseline - QSpectrumAnalyzer"))
self.label.setText(_translate("QSpectrumAnalyzerBaseline", "Baseline &file:"))
self.baselineFileButton.setText(_translate("QSpectrumAnalyzerBaseline", "..."))

View File

@ -2,124 +2,125 @@
# Form implementation generated from reading ui file 'qspectrumanalyzer/qspectrumanalyzer_colors.ui'
#
# Created by: PyQt4 UI code generator 4.12
# Created by: PyQt5 UI code generator 5.8
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
from Qt import QtCore, QtGui, QtWidgets
class Ui_QSpectrumAnalyzerColors(object):
def setupUi(self, QSpectrumAnalyzerColors):
QSpectrumAnalyzerColors.setObjectName(_fromUtf8("QSpectrumAnalyzerColors"))
QSpectrumAnalyzerColors.resize(232, 260)
self.verticalLayout = QtGui.QVBoxLayout(QSpectrumAnalyzerColors)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.formLayout = QtGui.QFormLayout()
self.formLayout.setObjectName(_fromUtf8("formLayout"))
self.label_2 = QtGui.QLabel(QSpectrumAnalyzerColors)
self.label_2.setObjectName(_fromUtf8("label_2"))
self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label_2)
QSpectrumAnalyzerColors.setObjectName("QSpectrumAnalyzerColors")
QSpectrumAnalyzerColors.resize(253, 266)
self.verticalLayout = QtWidgets.QVBoxLayout(QSpectrumAnalyzerColors)
self.verticalLayout.setObjectName("verticalLayout")
self.formLayout = QtWidgets.QFormLayout()
self.formLayout.setObjectName("formLayout")
self.label_2 = QtWidgets.QLabel(QSpectrumAnalyzerColors)
self.label_2.setObjectName("label_2")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_2)
self.mainColorButton = ColorButton(QSpectrumAnalyzerColors)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.mainColorButton.sizePolicy().hasHeightForWidth())
self.mainColorButton.setSizePolicy(sizePolicy)
self.mainColorButton.setObjectName(_fromUtf8("mainColorButton"))
self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.mainColorButton)
self.label_4 = QtGui.QLabel(QSpectrumAnalyzerColors)
self.label_4.setObjectName(_fromUtf8("label_4"))
self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.label_4)
self.mainColorButton.setObjectName("mainColorButton")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.mainColorButton)
self.label_4 = QtWidgets.QLabel(QSpectrumAnalyzerColors)
self.label_4.setObjectName("label_4")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_4)
self.peakHoldMaxColorButton = ColorButton(QSpectrumAnalyzerColors)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.peakHoldMaxColorButton.sizePolicy().hasHeightForWidth())
self.peakHoldMaxColorButton.setSizePolicy(sizePolicy)
self.peakHoldMaxColorButton.setObjectName(_fromUtf8("peakHoldMaxColorButton"))
self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.peakHoldMaxColorButton)
self.label_6 = QtGui.QLabel(QSpectrumAnalyzerColors)
self.label_6.setObjectName(_fromUtf8("label_6"))
self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.label_6)
self.peakHoldMaxColorButton.setObjectName("peakHoldMaxColorButton")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.peakHoldMaxColorButton)
self.label_6 = QtWidgets.QLabel(QSpectrumAnalyzerColors)
self.label_6.setObjectName("label_6")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_6)
self.peakHoldMinColorButton = ColorButton(QSpectrumAnalyzerColors)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.peakHoldMinColorButton.sizePolicy().hasHeightForWidth())
self.peakHoldMinColorButton.setSizePolicy(sizePolicy)
self.peakHoldMinColorButton.setObjectName(_fromUtf8("peakHoldMinColorButton"))
self.formLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.peakHoldMinColorButton)
self.label_5 = QtGui.QLabel(QSpectrumAnalyzerColors)
self.label_5.setObjectName(_fromUtf8("label_5"))
self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.label_5)
self.peakHoldMinColorButton.setObjectName("peakHoldMinColorButton")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.peakHoldMinColorButton)
self.label_5 = QtWidgets.QLabel(QSpectrumAnalyzerColors)
self.label_5.setObjectName("label_5")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_5)
self.averageColorButton = ColorButton(QSpectrumAnalyzerColors)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.averageColorButton.sizePolicy().hasHeightForWidth())
self.averageColorButton.setSizePolicy(sizePolicy)
self.averageColorButton.setObjectName(_fromUtf8("averageColorButton"))
self.formLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.averageColorButton)
self.label_3 = QtGui.QLabel(QSpectrumAnalyzerColors)
self.label_3.setObjectName(_fromUtf8("label_3"))
self.formLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.label_3)
self.averageColorButton.setObjectName("averageColorButton")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.averageColorButton)
self.label_3 = QtWidgets.QLabel(QSpectrumAnalyzerColors)
self.label_3.setObjectName("label_3")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_3)
self.persistenceColorButton = ColorButton(QSpectrumAnalyzerColors)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.persistenceColorButton.sizePolicy().hasHeightForWidth())
self.persistenceColorButton.setSizePolicy(sizePolicy)
self.persistenceColorButton.setObjectName(_fromUtf8("persistenceColorButton"))
self.formLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.persistenceColorButton)
self.persistenceColorButton.setObjectName("persistenceColorButton")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.persistenceColorButton)
self.label = QtWidgets.QLabel(QSpectrumAnalyzerColors)
self.label.setObjectName("label")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label)
self.baselineColorButton = ColorButton(QSpectrumAnalyzerColors)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.baselineColorButton.sizePolicy().hasHeightForWidth())
self.baselineColorButton.setSizePolicy(sizePolicy)
self.baselineColorButton.setObjectName("baselineColorButton")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.baselineColorButton)
self.verticalLayout.addLayout(self.formLayout)
spacerItem = QtGui.QSpacerItem(20, 2, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
spacerItem = QtWidgets.QSpacerItem(20, 2, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.buttonBox = QtGui.QDialogButtonBox(QSpectrumAnalyzerColors)
self.buttonBox = QtWidgets.QDialogButtonBox(QSpectrumAnalyzerColors)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout.addWidget(self.buttonBox)
self.label_2.setBuddy(self.mainColorButton)
self.label_4.setBuddy(self.peakHoldMaxColorButton)
self.label_6.setBuddy(self.peakHoldMinColorButton)
self.label_5.setBuddy(self.averageColorButton)
self.label_3.setBuddy(self.persistenceColorButton)
self.label.setBuddy(self.baselineColorButton)
self.retranslateUi(QSpectrumAnalyzerColors)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), QSpectrumAnalyzerColors.accept)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), QSpectrumAnalyzerColors.reject)
self.buttonBox.accepted.connect(QSpectrumAnalyzerColors.accept)
self.buttonBox.rejected.connect(QSpectrumAnalyzerColors.reject)
QtCore.QMetaObject.connectSlotsByName(QSpectrumAnalyzerColors)
QSpectrumAnalyzerColors.setTabOrder(self.mainColorButton, self.peakHoldMaxColorButton)
QSpectrumAnalyzerColors.setTabOrder(self.peakHoldMaxColorButton, self.peakHoldMinColorButton)
QSpectrumAnalyzerColors.setTabOrder(self.peakHoldMinColorButton, self.averageColorButton)
QSpectrumAnalyzerColors.setTabOrder(self.averageColorButton, self.persistenceColorButton)
QSpectrumAnalyzerColors.setTabOrder(self.persistenceColorButton, self.buttonBox)
QSpectrumAnalyzerColors.setTabOrder(self.persistenceColorButton, self.baselineColorButton)
def retranslateUi(self, QSpectrumAnalyzerColors):
QSpectrumAnalyzerColors.setWindowTitle(_translate("QSpectrumAnalyzerColors", "Colors - QSpectrumAnalyzer", None))
self.label_2.setText(_translate("QSpectrumAnalyzerColors", "Main curve color:", None))
self.mainColorButton.setText(_translate("QSpectrumAnalyzerColors", "...", None))
self.label_4.setText(_translate("QSpectrumAnalyzerColors", "Max. peak hold color:", None))
self.peakHoldMaxColorButton.setText(_translate("QSpectrumAnalyzerColors", "...", None))
self.label_6.setText(_translate("QSpectrumAnalyzerColors", "Min. peak hold color:", None))
self.peakHoldMinColorButton.setText(_translate("QSpectrumAnalyzerColors", "...", None))
self.label_5.setText(_translate("QSpectrumAnalyzerColors", "Average color:", None))
self.averageColorButton.setText(_translate("QSpectrumAnalyzerColors", "...", None))
self.label_3.setText(_translate("QSpectrumAnalyzerColors", "Persistence color:", None))
self.persistenceColorButton.setText(_translate("QSpectrumAnalyzerColors", "...", None))
_translate = QtCore.QCoreApplication.translate
QSpectrumAnalyzerColors.setWindowTitle(_translate("QSpectrumAnalyzerColors", "Colors - QSpectrumAnalyzer"))
self.label_2.setText(_translate("QSpectrumAnalyzerColors", "&Main curve color:"))
self.mainColorButton.setText(_translate("QSpectrumAnalyzerColors", "..."))
self.label_4.setText(_translate("QSpectrumAnalyzerColors", "Max. peak &hold color:"))
self.peakHoldMaxColorButton.setText(_translate("QSpectrumAnalyzerColors", "..."))
self.label_6.setText(_translate("QSpectrumAnalyzerColors", "M&in. peak hold color:"))
self.peakHoldMinColorButton.setText(_translate("QSpectrumAnalyzerColors", "..."))
self.label_5.setText(_translate("QSpectrumAnalyzerColors", "Average &color:"))
self.averageColorButton.setText(_translate("QSpectrumAnalyzerColors", "..."))
self.label_3.setText(_translate("QSpectrumAnalyzerColors", "Persistence co&lor:"))
self.persistenceColorButton.setText(_translate("QSpectrumAnalyzerColors", "..."))
self.label.setText(_translate("QSpectrumAnalyzerColors", "&Baseline color:"))
self.baselineColorButton.setText(_translate("QSpectrumAnalyzerColors", "..."))
from pyqtgraph import ColorButton

View File

@ -2,72 +2,59 @@
# Form implementation generated from reading ui file 'qspectrumanalyzer/qspectrumanalyzer_persistence.ui'
#
# Created by: PyQt4 UI code generator 4.12
# Created by: PyQt5 UI code generator 5.8
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
from Qt import QtCore, QtGui, QtWidgets
class Ui_QSpectrumAnalyzerPersistence(object):
def setupUi(self, QSpectrumAnalyzerPersistence):
QSpectrumAnalyzerPersistence.setObjectName(_fromUtf8("QSpectrumAnalyzerPersistence"))
QSpectrumAnalyzerPersistence.setObjectName("QSpectrumAnalyzerPersistence")
QSpectrumAnalyzerPersistence.resize(250, 130)
self.verticalLayout = QtGui.QVBoxLayout(QSpectrumAnalyzerPersistence)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.formLayout = QtGui.QFormLayout()
self.formLayout.setObjectName(_fromUtf8("formLayout"))
self.label_2 = QtGui.QLabel(QSpectrumAnalyzerPersistence)
self.label_2.setObjectName(_fromUtf8("label_2"))
self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label_2)
self.decayFunctionComboBox = QtGui.QComboBox(QSpectrumAnalyzerPersistence)
self.decayFunctionComboBox.setObjectName(_fromUtf8("decayFunctionComboBox"))
self.decayFunctionComboBox.addItem(_fromUtf8(""))
self.decayFunctionComboBox.addItem(_fromUtf8(""))
self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.decayFunctionComboBox)
self.label = QtGui.QLabel(QSpectrumAnalyzerPersistence)
self.label.setObjectName(_fromUtf8("label"))
self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.label)
self.persistenceLengthSpinBox = QtGui.QSpinBox(QSpectrumAnalyzerPersistence)
self.verticalLayout = QtWidgets.QVBoxLayout(QSpectrumAnalyzerPersistence)
self.verticalLayout.setObjectName("verticalLayout")
self.formLayout = QtWidgets.QFormLayout()
self.formLayout.setObjectName("formLayout")
self.label_2 = QtWidgets.QLabel(QSpectrumAnalyzerPersistence)
self.label_2.setObjectName("label_2")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_2)
self.decayFunctionComboBox = QtWidgets.QComboBox(QSpectrumAnalyzerPersistence)
self.decayFunctionComboBox.setObjectName("decayFunctionComboBox")
self.decayFunctionComboBox.addItem("")
self.decayFunctionComboBox.addItem("")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.decayFunctionComboBox)
self.label = QtWidgets.QLabel(QSpectrumAnalyzerPersistence)
self.label.setObjectName("label")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label)
self.persistenceLengthSpinBox = QtWidgets.QSpinBox(QSpectrumAnalyzerPersistence)
self.persistenceLengthSpinBox.setProperty("value", 5)
self.persistenceLengthSpinBox.setObjectName(_fromUtf8("persistenceLengthSpinBox"))
self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.persistenceLengthSpinBox)
self.persistenceLengthSpinBox.setObjectName("persistenceLengthSpinBox")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.persistenceLengthSpinBox)
self.verticalLayout.addLayout(self.formLayout)
spacerItem = QtGui.QSpacerItem(20, 5, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
spacerItem = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.buttonBox = QtGui.QDialogButtonBox(QSpectrumAnalyzerPersistence)
self.buttonBox = QtWidgets.QDialogButtonBox(QSpectrumAnalyzerPersistence)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout.addWidget(self.buttonBox)
self.label_2.setBuddy(self.decayFunctionComboBox)
self.label.setBuddy(self.persistenceLengthSpinBox)
self.retranslateUi(QSpectrumAnalyzerPersistence)
self.decayFunctionComboBox.setCurrentIndex(1)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), QSpectrumAnalyzerPersistence.accept)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), QSpectrumAnalyzerPersistence.reject)
self.buttonBox.accepted.connect(QSpectrumAnalyzerPersistence.accept)
self.buttonBox.rejected.connect(QSpectrumAnalyzerPersistence.reject)
QtCore.QMetaObject.connectSlotsByName(QSpectrumAnalyzerPersistence)
QSpectrumAnalyzerPersistence.setTabOrder(self.decayFunctionComboBox, self.persistenceLengthSpinBox)
QSpectrumAnalyzerPersistence.setTabOrder(self.persistenceLengthSpinBox, self.buttonBox)
def retranslateUi(self, QSpectrumAnalyzerPersistence):
QSpectrumAnalyzerPersistence.setWindowTitle(_translate("QSpectrumAnalyzerPersistence", "Persistence - QSpectrumAnalyzer", None))
self.label_2.setText(_translate("QSpectrumAnalyzerPersistence", "Decay function:", None))
self.decayFunctionComboBox.setItemText(0, _translate("QSpectrumAnalyzerPersistence", "linear", None))
self.decayFunctionComboBox.setItemText(1, _translate("QSpectrumAnalyzerPersistence", "exponential", None))
self.label.setText(_translate("QSpectrumAnalyzerPersistence", "Persistence length:", None))
_translate = QtCore.QCoreApplication.translate
QSpectrumAnalyzerPersistence.setWindowTitle(_translate("QSpectrumAnalyzerPersistence", "Persistence - QSpectrumAnalyzer"))
self.label_2.setText(_translate("QSpectrumAnalyzerPersistence", "Decay function:"))
self.decayFunctionComboBox.setItemText(0, _translate("QSpectrumAnalyzerPersistence", "linear"))
self.decayFunctionComboBox.setItemText(1, _translate("QSpectrumAnalyzerPersistence", "exponential"))
self.label.setText(_translate("QSpectrumAnalyzerPersistence", "Persistence length:"))

View File

@ -2,119 +2,170 @@
# Form implementation generated from reading ui file 'qspectrumanalyzer/qspectrumanalyzer_settings.ui'
#
# Created by: PyQt4 UI code generator 4.12
# Created by: PyQt5 UI code generator 5.8
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
from Qt import QtCore, QtGui, QtWidgets
class Ui_QSpectrumAnalyzerSettings(object):
def setupUi(self, QSpectrumAnalyzerSettings):
QSpectrumAnalyzerSettings.setObjectName(_fromUtf8("QSpectrumAnalyzerSettings"))
QSpectrumAnalyzerSettings.resize(420, 255)
self.verticalLayout = QtGui.QVBoxLayout(QSpectrumAnalyzerSettings)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.formLayout = QtGui.QFormLayout()
self.formLayout.setObjectName(_fromUtf8("formLayout"))
self.label_3 = QtGui.QLabel(QSpectrumAnalyzerSettings)
self.label_3.setObjectName(_fromUtf8("label_3"))
self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label_3)
self.backendComboBox = QtGui.QComboBox(QSpectrumAnalyzerSettings)
self.backendComboBox.setObjectName(_fromUtf8("backendComboBox"))
self.backendComboBox.addItem(_fromUtf8(""))
self.backendComboBox.addItem(_fromUtf8(""))
self.backendComboBox.addItem(_fromUtf8(""))
self.backendComboBox.addItem(_fromUtf8(""))
self.backendComboBox.addItem(_fromUtf8(""))
self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.backendComboBox)
self.label = QtGui.QLabel(QSpectrumAnalyzerSettings)
self.label.setObjectName(_fromUtf8("label"))
self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.label)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
self.executableEdit = QtGui.QLineEdit(QSpectrumAnalyzerSettings)
self.executableEdit.setObjectName(_fromUtf8("executableEdit"))
QSpectrumAnalyzerSettings.setObjectName("QSpectrumAnalyzerSettings")
QSpectrumAnalyzerSettings.resize(600, 388)
self.verticalLayout = QtWidgets.QVBoxLayout(QSpectrumAnalyzerSettings)
self.verticalLayout.setObjectName("verticalLayout")
self.formLayout = QtWidgets.QFormLayout()
self.formLayout.setObjectName("formLayout")
self.label_3 = QtWidgets.QLabel(QSpectrumAnalyzerSettings)
self.label_3.setObjectName("label_3")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_3)
self.backendComboBox = QtWidgets.QComboBox(QSpectrumAnalyzerSettings)
self.backendComboBox.setObjectName("backendComboBox")
self.backendComboBox.addItem("")
self.backendComboBox.addItem("")
self.backendComboBox.addItem("")
self.backendComboBox.addItem("")
self.backendComboBox.addItem("")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.backendComboBox)
self.label = QtWidgets.QLabel(QSpectrumAnalyzerSettings)
self.label.setObjectName("label")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.executableEdit = QtWidgets.QLineEdit(QSpectrumAnalyzerSettings)
self.executableEdit.setObjectName("executableEdit")
self.horizontalLayout.addWidget(self.executableEdit)
self.executableButton = QtGui.QToolButton(QSpectrumAnalyzerSettings)
self.executableButton.setObjectName(_fromUtf8("executableButton"))
self.executableButton = QtWidgets.QToolButton(QSpectrumAnalyzerSettings)
self.executableButton.setMinimumSize(QtCore.QSize(50, 0))
self.executableButton.setObjectName("executableButton")
self.horizontalLayout.addWidget(self.executableButton)
self.formLayout.setLayout(1, QtGui.QFormLayout.FieldRole, self.horizontalLayout)
self.label_5 = QtGui.QLabel(QSpectrumAnalyzerSettings)
self.label_5.setObjectName(_fromUtf8("label_5"))
self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.label_5)
self.deviceEdit = QtGui.QLineEdit(QSpectrumAnalyzerSettings)
self.deviceEdit.setObjectName(_fromUtf8("deviceEdit"))
self.formLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.deviceEdit)
self.label_4 = QtGui.QLabel(QSpectrumAnalyzerSettings)
self.label_4.setObjectName(_fromUtf8("label_4"))
self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.label_4)
self.sampleRateSpinBox = QtGui.QSpinBox(QSpectrumAnalyzerSettings)
self.sampleRateSpinBox.setMinimum(0)
self.sampleRateSpinBox.setMaximum(25000000)
self.sampleRateSpinBox.setSingleStep(10000)
self.sampleRateSpinBox.setProperty("value", 2560000)
self.sampleRateSpinBox.setObjectName(_fromUtf8("sampleRateSpinBox"))
self.formLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.sampleRateSpinBox)
self.label_2 = QtGui.QLabel(QSpectrumAnalyzerSettings)
self.label_2.setObjectName(_fromUtf8("label_2"))
self.formLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.label_2)
self.waterfallHistorySizeSpinBox = QtGui.QSpinBox(QSpectrumAnalyzerSettings)
self.formLayout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout)
self.label_5 = QtWidgets.QLabel(QSpectrumAnalyzerSettings)
self.label_5.setObjectName("label_5")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_5)
self.label_4 = QtWidgets.QLabel(QSpectrumAnalyzerSettings)
self.label_4.setObjectName("label_4")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_4)
self.label_2 = QtWidgets.QLabel(QSpectrumAnalyzerSettings)
self.label_2.setObjectName("label_2")
self.formLayout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.label_2)
self.waterfallHistorySizeSpinBox = QtWidgets.QSpinBox(QSpectrumAnalyzerSettings)
self.waterfallHistorySizeSpinBox.setMinimum(1)
self.waterfallHistorySizeSpinBox.setMaximum(10000000)
self.waterfallHistorySizeSpinBox.setProperty("value", 100)
self.waterfallHistorySizeSpinBox.setObjectName(_fromUtf8("waterfallHistorySizeSpinBox"))
self.formLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.waterfallHistorySizeSpinBox)
self.waterfallHistorySizeSpinBox.setObjectName("waterfallHistorySizeSpinBox")
self.formLayout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.waterfallHistorySizeSpinBox)
self.label_7 = QtWidgets.QLabel(QSpectrumAnalyzerSettings)
self.label_7.setObjectName("label_7")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_7)
self.label_8 = QtWidgets.QLabel(QSpectrumAnalyzerSettings)
self.label_8.setObjectName("label_8")
self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_8)
self.label_6 = QtWidgets.QLabel(QSpectrumAnalyzerSettings)
self.label_6.setObjectName("label_6")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_6)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.paramsEdit = QtWidgets.QLineEdit(QSpectrumAnalyzerSettings)
self.paramsEdit.setObjectName("paramsEdit")
self.horizontalLayout_2.addWidget(self.paramsEdit)
self.paramsHelpButton = QtWidgets.QToolButton(QSpectrumAnalyzerSettings)
self.paramsHelpButton.setMinimumSize(QtCore.QSize(50, 0))
self.paramsHelpButton.setObjectName("paramsHelpButton")
self.horizontalLayout_2.addWidget(self.paramsHelpButton)
self.formLayout.setLayout(2, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_2)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.deviceEdit = QtWidgets.QLineEdit(QSpectrumAnalyzerSettings)
self.deviceEdit.setObjectName("deviceEdit")
self.horizontalLayout_3.addWidget(self.deviceEdit)
self.deviceHelpButton = QtWidgets.QToolButton(QSpectrumAnalyzerSettings)
self.deviceHelpButton.setMinimumSize(QtCore.QSize(50, 0))
self.deviceHelpButton.setObjectName("deviceHelpButton")
self.horizontalLayout_3.addWidget(self.deviceHelpButton)
self.formLayout.setLayout(3, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_3)
self.sampleRateSpinBox = QtWidgets.QDoubleSpinBox(QSpectrumAnalyzerSettings)
self.sampleRateSpinBox.setProperty("showGroupSeparator", True)
self.sampleRateSpinBox.setDecimals(3)
self.sampleRateSpinBox.setMinimum(0.0)
self.sampleRateSpinBox.setMaximum(999999.99)
self.sampleRateSpinBox.setSingleStep(0.01)
self.sampleRateSpinBox.setProperty("value", 61.44)
self.sampleRateSpinBox.setObjectName("sampleRateSpinBox")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.sampleRateSpinBox)
self.bandwidthSpinBox = QtWidgets.QDoubleSpinBox(QSpectrumAnalyzerSettings)
self.bandwidthSpinBox.setProperty("showGroupSeparator", True)
self.bandwidthSpinBox.setDecimals(3)
self.bandwidthSpinBox.setMinimum(0.0)
self.bandwidthSpinBox.setMaximum(999999.99)
self.bandwidthSpinBox.setSingleStep(0.01)
self.bandwidthSpinBox.setProperty("value", 0.0)
self.bandwidthSpinBox.setObjectName("bandwidthSpinBox")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.bandwidthSpinBox)
self.lnbSpinBox = QtWidgets.QDoubleSpinBox(QSpectrumAnalyzerSettings)
self.lnbSpinBox.setProperty("showGroupSeparator", True)
self.lnbSpinBox.setDecimals(3)
self.lnbSpinBox.setMinimum(-999999.999)
self.lnbSpinBox.setMaximum(999999.999)
self.lnbSpinBox.setSingleStep(0.01)
self.lnbSpinBox.setProperty("value", 0.0)
self.lnbSpinBox.setObjectName("lnbSpinBox")
self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.lnbSpinBox)
self.verticalLayout.addLayout(self.formLayout)
spacerItem = QtGui.QSpacerItem(20, 21, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
spacerItem = QtWidgets.QSpacerItem(20, 21, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.buttonBox = QtGui.QDialogButtonBox(QSpectrumAnalyzerSettings)
self.buttonBox = QtWidgets.QDialogButtonBox(QSpectrumAnalyzerSettings)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout.addWidget(self.buttonBox)
self.label_3.setBuddy(self.backendComboBox)
self.label.setBuddy(self.executableEdit)
self.label_5.setBuddy(self.deviceEdit)
self.label_4.setBuddy(self.sampleRateSpinBox)
self.label_2.setBuddy(self.waterfallHistorySizeSpinBox)
self.label_7.setBuddy(self.bandwidthSpinBox)
self.label_8.setBuddy(self.lnbSpinBox)
self.label_6.setBuddy(self.paramsEdit)
self.retranslateUi(QSpectrumAnalyzerSettings)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), QSpectrumAnalyzerSettings.accept)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), QSpectrumAnalyzerSettings.reject)
self.buttonBox.accepted.connect(QSpectrumAnalyzerSettings.accept)
self.buttonBox.rejected.connect(QSpectrumAnalyzerSettings.reject)
QtCore.QMetaObject.connectSlotsByName(QSpectrumAnalyzerSettings)
QSpectrumAnalyzerSettings.setTabOrder(self.backendComboBox, self.executableEdit)
QSpectrumAnalyzerSettings.setTabOrder(self.executableEdit, self.executableButton)
QSpectrumAnalyzerSettings.setTabOrder(self.executableButton, self.deviceEdit)
QSpectrumAnalyzerSettings.setTabOrder(self.deviceEdit, self.sampleRateSpinBox)
QSpectrumAnalyzerSettings.setTabOrder(self.sampleRateSpinBox, self.waterfallHistorySizeSpinBox)
QSpectrumAnalyzerSettings.setTabOrder(self.waterfallHistorySizeSpinBox, self.buttonBox)
QSpectrumAnalyzerSettings.setTabOrder(self.executableButton, self.paramsEdit)
QSpectrumAnalyzerSettings.setTabOrder(self.paramsEdit, self.paramsHelpButton)
QSpectrumAnalyzerSettings.setTabOrder(self.paramsHelpButton, self.deviceEdit)
QSpectrumAnalyzerSettings.setTabOrder(self.deviceEdit, self.deviceHelpButton)
QSpectrumAnalyzerSettings.setTabOrder(self.deviceHelpButton, self.sampleRateSpinBox)
QSpectrumAnalyzerSettings.setTabOrder(self.sampleRateSpinBox, self.bandwidthSpinBox)
QSpectrumAnalyzerSettings.setTabOrder(self.bandwidthSpinBox, self.lnbSpinBox)
QSpectrumAnalyzerSettings.setTabOrder(self.lnbSpinBox, self.waterfallHistorySizeSpinBox)
def retranslateUi(self, QSpectrumAnalyzerSettings):
QSpectrumAnalyzerSettings.setWindowTitle(_translate("QSpectrumAnalyzerSettings", "Settings - QSpectrumAnalyzer", None))
self.label_3.setText(_translate("QSpectrumAnalyzerSettings", "&Backend:", None))
self.backendComboBox.setItemText(0, _translate("QSpectrumAnalyzerSettings", "soapy_power", None))
self.backendComboBox.setItemText(1, _translate("QSpectrumAnalyzerSettings", "rx_power", None))
self.backendComboBox.setItemText(2, _translate("QSpectrumAnalyzerSettings", "rtl_power_fftw", None))
self.backendComboBox.setItemText(3, _translate("QSpectrumAnalyzerSettings", "rtl_power", None))
self.backendComboBox.setItemText(4, _translate("QSpectrumAnalyzerSettings", "hackrf_sweep", None))
self.label.setText(_translate("QSpectrumAnalyzerSettings", "E&xecutable:", None))
self.executableEdit.setText(_translate("QSpectrumAnalyzerSettings", "soapy_power", None))
self.executableButton.setText(_translate("QSpectrumAnalyzerSettings", "...", None))
self.label_5.setText(_translate("QSpectrumAnalyzerSettings", "Device:", None))
self.label_4.setText(_translate("QSpectrumAnalyzerSettings", "Sa&mple rate:", None))
self.label_2.setText(_translate("QSpectrumAnalyzerSettings", "&Waterfall history size:", None))
_translate = QtCore.QCoreApplication.translate
QSpectrumAnalyzerSettings.setWindowTitle(_translate("QSpectrumAnalyzerSettings", "Settings - QSpectrumAnalyzer"))
self.label_3.setText(_translate("QSpectrumAnalyzerSettings", "&Backend:"))
self.backendComboBox.setItemText(0, _translate("QSpectrumAnalyzerSettings", "soapy_power"))
self.backendComboBox.setItemText(1, _translate("QSpectrumAnalyzerSettings", "rx_power"))
self.backendComboBox.setItemText(2, _translate("QSpectrumAnalyzerSettings", "rtl_power_fftw"))
self.backendComboBox.setItemText(3, _translate("QSpectrumAnalyzerSettings", "rtl_power"))
self.backendComboBox.setItemText(4, _translate("QSpectrumAnalyzerSettings", "hackrf_sweep"))
self.label.setText(_translate("QSpectrumAnalyzerSettings", "E&xecutable:"))
self.executableEdit.setText(_translate("QSpectrumAnalyzerSettings", "soapy_power"))
self.executableButton.setText(_translate("QSpectrumAnalyzerSettings", "..."))
self.label_5.setText(_translate("QSpectrumAnalyzerSettings", "&Device:"))
self.label_4.setText(_translate("QSpectrumAnalyzerSettings", "Sa&mple rate:"))
self.label_2.setText(_translate("QSpectrumAnalyzerSettings", "&Waterfall history size:"))
self.label_7.setText(_translate("QSpectrumAnalyzerSettings", "Bandwidt&h:"))
self.label_8.setToolTip(_translate("QSpectrumAnalyzerSettings", "Negative frequency for upconverters, positive frequency for downconverters."))
self.label_8.setText(_translate("QSpectrumAnalyzerSettings", "&LNB LO:"))
self.label_6.setText(_translate("QSpectrumAnalyzerSettings", "Add&itional parameters:"))
self.paramsHelpButton.setText(_translate("QSpectrumAnalyzerSettings", " ? "))
self.deviceHelpButton.setText(_translate("QSpectrumAnalyzerSettings", " ? "))
self.sampleRateSpinBox.setSuffix(_translate("QSpectrumAnalyzerSettings", " MHz"))
self.bandwidthSpinBox.setSuffix(_translate("QSpectrumAnalyzerSettings", " MHz"))
self.lnbSpinBox.setToolTip(_translate("QSpectrumAnalyzerSettings", "Negative frequency for upconverters, positive frequency for downconverters."))
self.lnbSpinBox.setSuffix(_translate("QSpectrumAnalyzerSettings", " MHz"))

View File

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'qspectrumanalyzer/qspectrumanalyzer_settings_help.ui'
#
# Created by: PyQt5 UI code generator 5.8
#
# WARNING! All changes made in this file will be lost!
from Qt import QtCore, QtGui, QtWidgets
class Ui_QSpectrumAnalyzerSettingsHelp(object):
def setupUi(self, QSpectrumAnalyzerSettingsHelp):
QSpectrumAnalyzerSettingsHelp.setObjectName("QSpectrumAnalyzerSettingsHelp")
QSpectrumAnalyzerSettingsHelp.resize(1200, 700)
self.verticalLayout = QtWidgets.QVBoxLayout(QSpectrumAnalyzerSettingsHelp)
self.verticalLayout.setObjectName("verticalLayout")
self.helpTextEdit = QtWidgets.QPlainTextEdit(QSpectrumAnalyzerSettingsHelp)
self.helpTextEdit.setUndoRedoEnabled(False)
self.helpTextEdit.setTextInteractionFlags(QtCore.Qt.TextSelectableByKeyboard|QtCore.Qt.TextSelectableByMouse)
self.helpTextEdit.setObjectName("helpTextEdit")
self.verticalLayout.addWidget(self.helpTextEdit)
self.buttonBox = QtWidgets.QDialogButtonBox(QSpectrumAnalyzerSettingsHelp)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Close)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout.addWidget(self.buttonBox)
self.retranslateUi(QSpectrumAnalyzerSettingsHelp)
self.buttonBox.accepted.connect(QSpectrumAnalyzerSettingsHelp.accept)
self.buttonBox.rejected.connect(QSpectrumAnalyzerSettingsHelp.reject)
QtCore.QMetaObject.connectSlotsByName(QSpectrumAnalyzerSettingsHelp)
QSpectrumAnalyzerSettingsHelp.setTabOrder(self.helpTextEdit, self.buttonBox)
def retranslateUi(self, QSpectrumAnalyzerSettingsHelp):
_translate = QtCore.QCoreApplication.translate
QSpectrumAnalyzerSettingsHelp.setWindowTitle(_translate("QSpectrumAnalyzerSettingsHelp", "Help - QSpectrumAnalyzer"))

View File

@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'qspectrumanalyzer/qspectrumanalyzer_smooth.ui'
#
# Created by: PyQt4 UI code generator 4.12
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_QSpectrumAnalyzerSmooth(object):
def setupUi(self, QSpectrumAnalyzerSmooth):
QSpectrumAnalyzerSmooth.setObjectName(_fromUtf8("QSpectrumAnalyzerSmooth"))
QSpectrumAnalyzerSmooth.resize(250, 130)
self.verticalLayout = QtGui.QVBoxLayout(QSpectrumAnalyzerSmooth)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.formLayout = QtGui.QFormLayout()
self.formLayout.setObjectName(_fromUtf8("formLayout"))
self.label = QtGui.QLabel(QSpectrumAnalyzerSmooth)
self.label.setObjectName(_fromUtf8("label"))
self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label)
self.windowFunctionComboBox = QtGui.QComboBox(QSpectrumAnalyzerSmooth)
self.windowFunctionComboBox.setObjectName(_fromUtf8("windowFunctionComboBox"))
self.windowFunctionComboBox.addItem(_fromUtf8(""))
self.windowFunctionComboBox.addItem(_fromUtf8(""))
self.windowFunctionComboBox.addItem(_fromUtf8(""))
self.windowFunctionComboBox.addItem(_fromUtf8(""))
self.windowFunctionComboBox.addItem(_fromUtf8(""))
self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.windowFunctionComboBox)
self.label_2 = QtGui.QLabel(QSpectrumAnalyzerSmooth)
self.label_2.setObjectName(_fromUtf8("label_2"))
self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.label_2)
self.windowLengthSpinBox = QtGui.QSpinBox(QSpectrumAnalyzerSmooth)
self.windowLengthSpinBox.setMinimum(3)
self.windowLengthSpinBox.setMaximum(1001)
self.windowLengthSpinBox.setProperty("value", 11)
self.windowLengthSpinBox.setObjectName(_fromUtf8("windowLengthSpinBox"))
self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.windowLengthSpinBox)
self.verticalLayout.addLayout(self.formLayout)
spacerItem = QtGui.QSpacerItem(20, 1, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.buttonBox = QtGui.QDialogButtonBox(QSpectrumAnalyzerSmooth)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
self.verticalLayout.addWidget(self.buttonBox)
self.label.setBuddy(self.windowFunctionComboBox)
self.label_2.setBuddy(self.windowLengthSpinBox)
self.retranslateUi(QSpectrumAnalyzerSmooth)
self.windowFunctionComboBox.setCurrentIndex(1)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), QSpectrumAnalyzerSmooth.accept)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), QSpectrumAnalyzerSmooth.reject)
QtCore.QMetaObject.connectSlotsByName(QSpectrumAnalyzerSmooth)
QSpectrumAnalyzerSmooth.setTabOrder(self.windowFunctionComboBox, self.windowLengthSpinBox)
QSpectrumAnalyzerSmooth.setTabOrder(self.windowLengthSpinBox, self.buttonBox)
def retranslateUi(self, QSpectrumAnalyzerSmooth):
QSpectrumAnalyzerSmooth.setWindowTitle(_translate("QSpectrumAnalyzerSmooth", "Smoothing - QSpectrumAnalyzer", None))
self.label.setText(_translate("QSpectrumAnalyzerSmooth", "&Window function:", None))
self.windowFunctionComboBox.setItemText(0, _translate("QSpectrumAnalyzerSmooth", "rectangular", None))
self.windowFunctionComboBox.setItemText(1, _translate("QSpectrumAnalyzerSmooth", "hanning", None))
self.windowFunctionComboBox.setItemText(2, _translate("QSpectrumAnalyzerSmooth", "hamming", None))
self.windowFunctionComboBox.setItemText(3, _translate("QSpectrumAnalyzerSmooth", "bartlett", None))
self.windowFunctionComboBox.setItemText(4, _translate("QSpectrumAnalyzerSmooth", "blackman", None))
self.label_2.setText(_translate("QSpectrumAnalyzerSmooth", "Window len&gth:", None))

View File

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'qspectrumanalyzer/qspectrumanalyzer_smoothing.ui'
#
# Created by: PyQt5 UI code generator 5.8
#
# WARNING! All changes made in this file will be lost!
from Qt import QtCore, QtGui, QtWidgets
class Ui_QSpectrumAnalyzerSmoothing(object):
def setupUi(self, QSpectrumAnalyzerSmoothing):
QSpectrumAnalyzerSmoothing.setObjectName("QSpectrumAnalyzerSmoothing")
QSpectrumAnalyzerSmoothing.resize(250, 130)
self.verticalLayout = QtWidgets.QVBoxLayout(QSpectrumAnalyzerSmoothing)
self.verticalLayout.setObjectName("verticalLayout")
self.formLayout = QtWidgets.QFormLayout()
self.formLayout.setObjectName("formLayout")
self.label = QtWidgets.QLabel(QSpectrumAnalyzerSmoothing)
self.label.setObjectName("label")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)
self.windowFunctionComboBox = QtWidgets.QComboBox(QSpectrumAnalyzerSmoothing)
self.windowFunctionComboBox.setObjectName("windowFunctionComboBox")
self.windowFunctionComboBox.addItem("")
self.windowFunctionComboBox.addItem("")
self.windowFunctionComboBox.addItem("")
self.windowFunctionComboBox.addItem("")
self.windowFunctionComboBox.addItem("")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.windowFunctionComboBox)
self.label_2 = QtWidgets.QLabel(QSpectrumAnalyzerSmoothing)
self.label_2.setObjectName("label_2")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_2)
self.windowLengthSpinBox = QtWidgets.QSpinBox(QSpectrumAnalyzerSmoothing)
self.windowLengthSpinBox.setMinimum(3)
self.windowLengthSpinBox.setMaximum(1001)
self.windowLengthSpinBox.setProperty("value", 11)
self.windowLengthSpinBox.setObjectName("windowLengthSpinBox")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.windowLengthSpinBox)
self.verticalLayout.addLayout(self.formLayout)
spacerItem = QtWidgets.QSpacerItem(20, 1, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.buttonBox = QtWidgets.QDialogButtonBox(QSpectrumAnalyzerSmoothing)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout.addWidget(self.buttonBox)
self.label.setBuddy(self.windowFunctionComboBox)
self.label_2.setBuddy(self.windowLengthSpinBox)
self.retranslateUi(QSpectrumAnalyzerSmoothing)
self.windowFunctionComboBox.setCurrentIndex(1)
self.buttonBox.accepted.connect(QSpectrumAnalyzerSmoothing.accept)
self.buttonBox.rejected.connect(QSpectrumAnalyzerSmoothing.reject)
QtCore.QMetaObject.connectSlotsByName(QSpectrumAnalyzerSmoothing)
QSpectrumAnalyzerSmoothing.setTabOrder(self.windowFunctionComboBox, self.windowLengthSpinBox)
QSpectrumAnalyzerSmoothing.setTabOrder(self.windowLengthSpinBox, self.buttonBox)
def retranslateUi(self, QSpectrumAnalyzerSmoothing):
_translate = QtCore.QCoreApplication.translate
QSpectrumAnalyzerSmoothing.setWindowTitle(_translate("QSpectrumAnalyzerSmoothing", "Smoothing - QSpectrumAnalyzer"))
self.label.setText(_translate("QSpectrumAnalyzerSmoothing", "&Window function:"))
self.windowFunctionComboBox.setItemText(0, _translate("QSpectrumAnalyzerSmoothing", "rectangular"))
self.windowFunctionComboBox.setItemText(1, _translate("QSpectrumAnalyzerSmoothing", "hanning"))
self.windowFunctionComboBox.setItemText(2, _translate("QSpectrumAnalyzerSmoothing", "hamming"))
self.windowFunctionComboBox.setItemText(3, _translate("QSpectrumAnalyzerSmoothing", "bartlett"))
self.windowFunctionComboBox.setItemText(4, _translate("QSpectrumAnalyzerSmoothing", "blackman"))
self.label_2.setText(_translate("QSpectrumAnalyzerSmoothing", "Window len&gth:"))

View File

@ -1,6 +1,6 @@
import numpy as np
from PyQt4 import QtGui
from Qt import QtGui
def smooth(x, window_len=11, window='hanning'):
@ -35,3 +35,19 @@ def str_to_color(color_string):
def color_to_str(color):
"""Create comma separated RGBA string from QColor"""
return ", ".join([str(color.red()), str(color.green()), str(color.blue()), str(color.alpha())])
def human_time(seconds):
"""Format time in seconds to human readable form (e.g. 1 h 2 min 3 s)"""
seconds = int(seconds)
m, s = divmod(seconds, 60)
h, m = divmod(m, 60)
if h > 0:
timestr = '{:.0f} h {:.0f} min {:.0f} s'.format(h, m, s)
elif m > 0:
timestr = '{:.0f} min {:.0f} s'.format(m, s)
else:
timestr = '{:.0f} s'.format(s)
return timestr

View File

@ -1 +1 @@
__version__ = "1.4.0"
__version__ = "2.2.0"

View File

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
"""Windows-specific utilities"""
from ctypes import windll
# --- Window control ---
SW_SHOW = 5 # activate and display
SW_SHOWNA = 8 # show without activation
SW_HIDE = 0
GetConsoleWindow = windll.kernel32.GetConsoleWindow
ShowWindow = windll.user32.ShowWindow
IsWindowVisible = windll.user32.IsWindowVisible
# Handle to console window associated with current Python
# interpreter procss, 0 if there is no window
console_window_handle = GetConsoleWindow()
def set_attached_console_visible(state):
"""Show/hide system console window attached to current process.
Return it's previous state.
Availability: Windows"""
flag = {True: SW_SHOW, False: SW_HIDE}
return bool(ShowWindow(console_window_handle, flag[state]))
def is_attached_console_visible():
"""Return True if attached console window is visible"""
return IsWindowVisible(console_window_handle)
def set_windows_appusermodelid():
"""Make sure correct icon is used on Windows 7 taskbar"""
try:
return windll.shell32.SetCurrentProcessExplicitAppUserModelID("spyder.Spyder")
except AttributeError:
return "SetCurrentProcessExplicitAppUserModelID not found"
# [ ] the console state asks for a storage container
# [ ] reopen console on exit - better die open than become a zombie

View File

@ -1,26 +0,0 @@
#!/usr/bin/env python
import os, shutil
from glob import glob
package = "qspectrumanalyzer"
languages = ["cs"]
print("Rebuilding PyQt resource files...")
for f in glob("{}/*.qrc".format(package)):
os.system("pyrcc4 -o {}/qrc_{}.py {}".format(package, os.path.basename(f[:-4]), f))
print("Rebuilding PyQt UI files...")
for f in glob("{}/*.ui".format(package)):
os.system("pyuic4 -o {}/ui_{}.py {}".format(package, os.path.basename(f[:-3]), f))
print("Updating translations...")
lang_files = " ".join("{}/languages/{}_{}.ts".format(package, package, lang) for lang in languages)
os.system("pylupdate4 {}/*.py -ts {}".format(package, lang_files))
os.system("lrelease {}/languages/*.ts".format(package))
print("Regenerating .pyc files...")
shutil.rmtree("{}/__pycache__".format(package), ignore_errors=True)
for f in glob("{}/*.pyc".format(package)):
os.remove(f)
__import__("{}.__main__".format(package))

View File

@ -1,36 +1,90 @@
#!/usr/bin/env python
import setuptools
from setuptools import setup
from qspectrumanalyzer.version import __version__
setup_cmdclass = {}
setup_entry_points = {
"gui_scripts": [
"qspectrumanalyzer=qspectrumanalyzer.__main__:main",
],
}
# Allow compilation of Qt .qrc, .ui and .ts files (build_qt command)
try:
from setup_qt import build_qt
setup_cmdclass['build_qt'] = build_qt
except ImportError:
pass
# Allow building frozen executables with PyInstaller / subzero (build_exe command)
try:
from subzero import setup, Executable
setup_entry_points = {
"console_scripts": [
Executable('qspectrumanalyzer=qspectrumanalyzer.__main__:main',
console=True, icon_file='qspectrumanalyzer.ico'),
Executable('soapy_power=soapypower.__main__:main',
console=True),
],
}
except ImportError:
pass
setup(
name="QSpectrumAnalyzer",
version=__version__,
description="Spectrum analyzer for RTL-SDR (GUI for rtl_power based on PyQtGraph)",
description=("Spectrum analyzer for multiple SDR platforms "
"(PyQtGraph based GUI for soapy_power, hackrf_sweep, rtl_power, rx_power and other backends)"),
long_description=open('README.rst').read(),
author="Michal Krenek (Mikos)",
author_email="m.krenek@gmail.com",
url="https://github.com/xmikos/qspectrumanalyzer",
license="GNU GPLv3",
packages=["qspectrumanalyzer"],
packages=["qspectrumanalyzer", "qspectrumanalyzer.backends"],
package_data={
"qspectrumanalyzer": [
"qspectrumanalyzer.svg",
"*.ui",
"languages/*.qm",
"languages/*.ts"
]
"languages/*.ts",
],
},
data_files=[
("share/applications", ["qspectrumanalyzer.desktop"]),
("share/pixmaps", ["qspectrumanalyzer.png"])
("share/pixmaps", ["qspectrumanalyzer.png"]),
],
entry_points={
"gui_scripts": [
"qspectrumanalyzer=qspectrumanalyzer.__main__:main"
install_requires=[
"soapy_power>=1.6.0",
"pyqtgraph>=0.10.0",
"Qt.py",
],
options={
'build_qt': {
'packages': ['qspectrumanalyzer'],
'languages': ['cs'],
'replacement_bindings': 'Qt',
},
'build_exe': {
'datas': [
('qspectrumanalyzer/qspectrumanalyzer.svg', 'qspectrumanalyzer'),
('qspectrumanalyzer/*.ui', 'qspectrumanalyzer'),
('qspectrumanalyzer/languages/*.ts', 'qspectrumanalyzer/languages'),
('qspectrumanalyzer/languages/*.qm', 'qspectrumanalyzer/languages'),
('README.rst', '.'),
('LICENSE', '.'),
],
},
install_requires=[
"pyqtgraph"
'bdist_msi': {
'upgrade_code': '30740ef4-84e7-4e67-8e4a-12b53492c387',
'shortcuts': [
'QSpectrumAnalyzer=qspectrumanalyzer',
],
},
},
classifiers=[
"Development Status :: 4 - Beta",
"Environment :: MacOS X",
@ -44,6 +98,8 @@ setup(
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Topic :: Communications :: Ham Radio",
"Topic :: Scientific/Engineering :: Visualization"
]
"Topic :: Scientific/Engineering :: Visualization",
],
entry_points=setup_entry_points,
cmdclass=setup_cmdclass,
)