diff --git a/CHANGELOG b/CHANGELOG index 304a22e44..9a11c0e8c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,27 @@ +sdrangel (4.8.2-1) unstable; urgency=medium + + * SSB demod: fixes + * Audio input: set default volume factor to 1.0 (was 0.15) + + -- Edouard Griffiths, F4EXB Thu, 30 May 2019 20:44:06 +0100 + +sdrangel (4.8.1-1) unstable; urgency=medium + + * AM demod: fixed audio interpolator. Issue #354 + * Implemented interpolation to audio for NFM and SSB demods allowing the use of a 48k audio sink regardless of the channel sample rate + * AM demod: implemented low pass filter after the demod. Issue #352 + * Spectrum window: added ability to use the mouse wheel to move the central channel marker line. Issue #343 + * Frequency Tracker: make lock converge faster + * Added a splash screen on startup. Issue #343 + * SoapySDR output: support floating point type samples. Issue #345 + * NFM demod: made high pass audio filter optional to be able to pass DC to other programs via audio. Issue #343 + * Added UHD rules in udev (and moved away to sdrangel-docker). Issue #343 + * Use Qt precise timer for test and file sources + * "hidden" `--mimo` option to activate MIMO functionality (MIMO device set). Makes it optional until it can be officialy released + * Updated some base classes to adequately support new MIMO devices + + -- Edouard Griffiths, F4EXB Sun, 20 May 2019 20:44:06 +0100 + sdrangel (4.8.0-1) unstable; urgency=medium * Local output plugin diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e9ad84c1..9b417706e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) # configure version set(sdrangel_VERSION_MAJOR "4") set(sdrangel_VERSION_MINOR "8") -set(sdrangel_VERSION_PATCH "0") +set(sdrangel_VERSION_PATCH "2") set(sdrangel_VERSION_SUFFIX "") # SDRAngel cmake options diff --git a/Readme.md b/Readme.md index ffea64778..280d18de1 100644 --- a/Readme.md +++ b/Readme.md @@ -74,6 +74,10 @@ The audio devices with Qt are supported through pulseaudio and unless you are us In case you cannot see anything related to HDMI or your desired audio device in pavucontrol just restart pulseaudio with `pulseaudio -k` (`-k` kills the previous instance before restarting) and do the above steps again. +

Note on udev rules

+ +On Linux you need specific files in `/etc/udev/rules.d` to be able to access the SDR hardware. Please refer to the respective hardware vendor instructions to install these files. +

Software build on Linux

Plese consult the [Wiki page for compilation in Linux](https://github.com/f4exb/sdrangel/wiki/Compile-from-source-in-Linux). The notes below are left for further information if needed although you should be all set with the Wiki. @@ -89,11 +93,11 @@ To be sure you will need at least Qt version 5.5. It definitely does not work wi

Ubuntu

- - `sudo apt-get install cmake g++ pkg-config libfftw3-dev libqt5multimedia5-plugins qtmultimedia5-dev qttools5-dev qttools5-dev-tools libqt5opengl5-dev qtbase5-dev libusb-1.0 librtlsdr-dev libboost-all-dev libasound2-dev pulseaudio libopencv-dev libsqlite3-dev libxml2-dev bison flex ffmpeg libavcodec-dev libavformat-dev libopus-dev` + - `sudo apt-get install cmake g++ pkg-config libfftw3-dev libqt5multimedia5-plugins qtmultimedia5-dev qttools5-dev qttools5-dev-tools libqt5opengl5-dev qtbase5-dev libusb-1.0 librtlsdr-dev libboost-all-dev libasound2-dev pulseaudio libopencv-dev libxml2-dev bison flex ffmpeg libavcodec-dev libavformat-dev libopus-dev`

Debian

- - `sudo apt-get install cmake g++ pkg-config libfftw3-dev libusb-1.0-0-dev libusb-dev qt5-default qtbase5-dev qtchooser libqt5multimedia5-plugins qtmultimedia5-dev qttools5-dev qttools5-dev-tools libqt5opengl5-dev qtbase5-dev librtlsdr-dev libboost-all-dev libasound2-dev pulseaudio libopencv-dev libsqlite3-dev libxml2-dev bison flex ffmpeg libavcodec-dev libavformat-dev libopus-dev` + - `sudo apt-get install cmake g++ pkg-config libfftw3-dev libusb-1.0-0-dev libusb-dev qt5-default qtbase5-dev qtchooser libqt5multimedia5-plugins qtmultimedia5-dev qttools5-dev qttools5-dev-tools libqt5opengl5-dev qtbase5-dev librtlsdr-dev libboost-all-dev libasound2-dev pulseaudio libopencv-dev libxml2-dev bison flex ffmpeg libavcodec-dev libavformat-dev libopus-dev`

openSUSE

@@ -101,10 +105,7 @@ To be sure you will need at least Qt version 5.5. It definitely does not work wi - `sudo zypper install cmake fftw3-devel gcc-c++ libusb-1_0-devel libqt5-qtbase-devel libQt5OpenGL-devel libqt5-qtmultimedia-devel libqt5-qttools-devel libQt5Network-devel libQt5Widgets-devel boost-devel alsa-devel pulseaudio opencv-devel` - Note1: if you are on Leap you will need a more recent g++ compiler so in place of `gcc-c++` use `gcc6-c++` or `gcc7-c++` then add the following in the cmake command: `-DCMAKE_C_COMPILER=/usr/bin/gcc-7 -DCMAKE_CXX_COMPILER=/usr/bin/g++-7` (for gcc 7) and then `-DCMAKE_INSTALL_PREFIX:PATH=...` for the custom install path (not `-DCMAKE_INSTALL_PREFIX=...`) - - Note2 for udev rules: installed udev rules for BladeRF and HackRF are targeted at Debian or Ubuntu systems that have a plugdev group for USB hotplug devices. This is not the case in openSUSE. To fix it you can either: - - make the udev rules file compatible just remove the `GROUP` parameter on all lines and change `MODE` parameter to `666`. - - create a `plugdev` group and add it tou your user group list: `sudo groupadd plugdev` then `sudo usermod -G plugdev -a ` - - Note3: A package has been created in openSUSE thanks to Martin, see: [sdrangel](https://build.opensuse.org/package/show/hardware:sdr/sdrangel). It is based on the latest release on master branch. + - Note2: A package has been created in openSUSE thanks to Martin, see: [sdrangel](https://build.opensuse.org/package/show/hardware:sdr/sdrangel). It is based on the latest release on master branch.

Fedora

@@ -114,16 +115,13 @@ This has been tested with Fedora 23 and 22: - `sudo dnf install mesa-libGL-devel` - `sudo dnf install cmake gcc-c++ pkgconfig fftw-devel libusb-devel qt5-qtbase-devel qt5-qtmultimedia-devel qt5-qttools-devel boost-devel pulseaudio alsa-lib-devel` - - Note for udev rules: the same as for openSUSE applies. This is detailed in the previous paragraph for openSUSE. -

Arch Linux / Manjaro

Tested with the 15.09 version with LXDE desktop (community supported). The exact desktop environment should not matter anyway. Prerequisites should be similar for Arch and all derivatives. `sudo pacman -S cmake pkg-config fftw qt5-multimedia qt5-tools qt5-base libusb boost boost-libs pulseaudio` - - Note1 for udev rules: the same as for openSUSE and Fedora applies. - - Note2: Two package are avaliable in the AUR (thanks Mikos!), [sdrangel](https://aur.archlinux.org/packages/sdrangel), which provides the lastest tagged release (stable), and [sdrangel-git](https://aur.archlinux.org/packages/sdrangel-git), which builds the latest commit from this repository (unstable). + - Note1: Two package are avaliable in the AUR (thanks Mikos!), [sdrangel](https://aur.archlinux.org/packages/sdrangel), which provides the lastest tagged release (stable), and [sdrangel-git](https://aur.archlinux.org/packages/sdrangel-git), which builds the latest commit from this repository (unstable).

Compile for Windows

@@ -355,8 +353,6 @@ Since apt-get v 1.1 installation is possible from a local file: The software is installed in `/opt/sdrangel` you can start it from the command line with: - `/opt/sdrangel/bin/sdrangel` -**⚠** The udev rules are not set by the package installation so you will have to set it manually in order to be able to access the various SDR hardware. The `udev-rules` folder contains the rules file and the `install.sh` script that you can run as sudo to install all rules files. You may also adapt the script to copy only the required files. -

Ubuntu 18.04

The default CPU governor is now `powersave` which exhibits excessive CPU usage when running SDRangel. In the case of benchmarking and maybe high throughput usage it is recommended to switch to `performance` before running SDRangel by running the command: `sudo cpupower frequency-set --governor performance`. You can turn it back to `powersave` any time by running: `sudo cpupower frequency-set --governor powersave`. It is normal that with a lower CPU frequency the relative CPU usage rises for the same actual load. If not impairing operation this is normal and overall beneficial for heat and power consumption. diff --git a/cmake/Modules/FindSQLite3.cmake b/cmake/Modules/FindSQLite3.cmake deleted file mode 100644 index e9f0621ab..000000000 --- a/cmake/Modules/FindSQLite3.cmake +++ /dev/null @@ -1,27 +0,0 @@ -if (NOT SQLITE3_FOUND) - find_path(SQLITE3_INCLUDE_DIRS NAMES sqlite3.h - PATHS - ${SQLITE3_PKG_INCLUDE_DIRS} - /usr/include - /usr/local/include - ) - - find_library(SQLITE3_LIBRARIES NAMES sqlite3 - PATHS - ${SQLITE3_PKG_LIBRARY_DIRS} - /usr/lib - /usr/local/lib - /usr/lib/x86_64-linux-gnu - ) - -if (SQLITE3_INCLUDE_DIRS AND SQLITE3_LIBRARIES) - set(SQLITE3_FOUND TRUE CACHE INTERNAL "sqlite3 found") - message(STATUS "Found sqlite3: ${SQLITE3_INCLUDE_DIRS}, ${SQLITE3_LIBRARIES}") -else (SQLITE3_INCLUDE_DIRS AND SQLITE3_LIBRARIES) - set(SQLITE3_FOUND FALSE CACHE INTERNAL "sqlite3 found") - message(STATUS "sqlite3 not found.") -endif (SQLITE3_INCLUDE_DIRS AND SQLITE3_LIBRARIES) - -MARK_AS_ADVANCED(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES) - -endif (NOT SQLITE3_FOUND) \ No newline at end of file diff --git a/debian/changelog b/debian/changelog deleted file mode 120000 index a53599471..000000000 --- a/debian/changelog +++ /dev/null @@ -1 +0,0 @@ -../CHANGELOG \ No newline at end of file diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 000000000..9a11c0e8c --- /dev/null +++ b/debian/changelog @@ -0,0 +1,804 @@ +sdrangel (4.8.2-1) unstable; urgency=medium + + * SSB demod: fixes + * Audio input: set default volume factor to 1.0 (was 0.15) + + -- Edouard Griffiths, F4EXB Thu, 30 May 2019 20:44:06 +0100 + +sdrangel (4.8.1-1) unstable; urgency=medium + + * AM demod: fixed audio interpolator. Issue #354 + * Implemented interpolation to audio for NFM and SSB demods allowing the use of a 48k audio sink regardless of the channel sample rate + * AM demod: implemented low pass filter after the demod. Issue #352 + * Spectrum window: added ability to use the mouse wheel to move the central channel marker line. Issue #343 + * Frequency Tracker: make lock converge faster + * Added a splash screen on startup. Issue #343 + * SoapySDR output: support floating point type samples. Issue #345 + * NFM demod: made high pass audio filter optional to be able to pass DC to other programs via audio. Issue #343 + * Added UHD rules in udev (and moved away to sdrangel-docker). Issue #343 + * Use Qt precise timer for test and file sources + * "hidden" `--mimo` option to activate MIMO functionality (MIMO device set). Makes it optional until it can be officialy released + * Updated some base classes to adequately support new MIMO devices + + -- Edouard Griffiths, F4EXB Sun, 20 May 2019 20:44:06 +0100 + +sdrangel (4.8.0-1) unstable; urgency=medium + + * Local output plugin + + -- Edouard Griffiths, F4EXB Fri, 10 May 2019 20:44:06 +0100 + +sdrangel (4.7.2-1) unstable; urgency=medium + + * Unique DeviceAPI interface for sources and sinks + * REST API: fixes over last version + + -- Edouard Griffiths, F4EXB Wed, 08 May 2019 22:14:18 +0100 + +sdrangel (4.7.1-1) unstable; urgency=medium + + * SSB modulator: fixed compressor overloading + * REST API: tx, nbStreams and streamIndex names change + + -- Edouard Griffiths, F4EXB Tue, 07 May 2019 14:14:18 +0100 + +sdrangel (4.7.0-1) unstable; urgency=medium + + * New Frequency Tracker Rx channel plugin + + -- Edouard Griffiths, F4EXB Sun, 05 May 2019 16:14:18 +0100 + +sdrangel (4.6.0-1) unstable; urgency=medium + + * New LocalSink and LocalInput plugin pair to use sub basebands internally + + -- Edouard Griffiths, F4EXB Thu, 02 May 2019 11:14:18 +0100 + +sdrangel (4.5.6-1) unstable; urgency=medium + + * Remote channel sink: implemented decimation with possible center shift. Issue #331 + * Remote input: fixed version display + * DSD demod: save PLL enable and autio mute in preset + + -- Edouard Griffiths, F4EXB Sun, 28 Apr 2019 20:14:18 +0100 + +sdrangel (4.5.5-1) unstable; urgency=medium + + * FreeDV: fixes in REST API + * LimeSDR: fixed Rx and Tx antenna labels for Lime mini + * HackRF output: fixed setting of Fc pos from REST API + + -- Edouard Griffiths, F4EXB Sat, 20 Apr 2019 20:14:18 +0100 + +sdrangel (4.5.4-1) unstable; urgency=medium + + * Fixed RTL-SDR gain setting sequence. Fixes issue #321. + + -- Edouard Griffiths, F4EXB Fri, 05 Apr 2019 20:14:18 +0100 + +sdrangel (4.5.3-1) unstable; urgency=medium + + * Fixed HackRF Rx/Tx frequency setting. Fixes issue #318. + + -- Edouard Griffiths, F4EXB Mon, 01 Apr 2019 20:14:18 +0100 + +sdrangel (4.5.2-1) unstable; urgency=medium + + * Send device and channel index in reverse API. Implements issue #312. + + -- Edouard Griffiths, F4EXB Mon, 25 Mar 2019 20:14:18 +0100 + +sdrangel (4.5.1-1) unstable; urgency=medium + + * DATV demod: implemented audio processing + * Perseus: fixed cold start flawed init sequence + * PlutoSDR: recognize networked devices + * FreeDV: internal FreeDV library to facilitate packaging + * Fixed some constness in qrtplib + * Fixed bug in Channel Analyzer deserialization. Fixes issue #314. + + -- Edouard Griffiths, F4EXB Fri, 22 Mar 2019 20:14:18 +0100 + +sdrangel (4.5.0-1) unstable; urgency=medium + + * Implemented a FreeDV modulator and demodulator + + -- Edouard Griffiths, F4EXB Sun, 03 Mar 2019 20:14:18 +0100 + +sdrangel (4.4.5-1) unstable; urgency=medium + + * UDP/RTP audio: added G722 and Opus support + * Debian build: fixed missing sources in dsdcc, libiio and limesuite + + -- Edouard Griffiths, F4EXB Tue, 19 Feb 2019 22:14:18 +0100 + +sdrangel (4.4.4-1) unstable; urgency=medium + + * SoapySDR: fixed some GUI issues + * UDP/RTP audio: added decimation and PCM A-law and Mu-law compression + + -- Edouard Griffiths, F4EXB Fri, 15 Feb 2019 10:14:18 +0100 + +sdrangel (4.4.3-1) unstable; urgency=medium + + * Migration of SDRDaemon* devices to Remote* devices + * PlutoSDR: get LP filter bandwidth from device + + -- Edouard Griffiths, F4EXB Sun, 03 Feb 2019 10:14:18 +0100 + +sdrangel (4.4.2-1) unstable; urgency=medium + + * Migration of Daemon* channels to Remote* channels + * XTRX: fixed GPS status retrieval + + -- Edouard Griffiths, F4EXB Fri, 25 Jan 2019 21:14:18 +0100 + +sdrangel (4.4.1-1) unstable; urgency=medium + + * AirspyHF: Implemented new settings that come along newer version of libairspyhf + * Fixed many glitches in the REST API device settings handling + * SoapySDR: on preset load match on serial or driver id for SoapySDR devices + * SerialDV: corrected cmake find module and improved SerialDV support error messages + * Make the settings location appear in the log and in the about panel of the GUI also + * Temptative XTRX fixes + * REST API examples: Added a randomize channel colors utility as a way to answer to issue #275 + + -- Edouard Griffiths, F4EXB Wed, 02 Jan 2019 21:14:18 +0100 + +sdrangel (4.4.0-1) unstable; urgency=medium + + * XTRX support + + -- Edouard Griffiths, F4EXB Wed, 02 Jan 2019 21:14:18 +0100 + +sdrangel (4.3.2-1) unstable; urgency=medium + + * Reverse API to forward device and channel changes to external application + * Funcube dongle: fixed segfault when stopping device + * Channel analzyer: fixed rational downsampler range + * SoapySDR support: fixed memory leaks + + -- Edouard Griffiths, F4EXB Sun, 09 Dec 2018 21:14:18 +0100 + +sdrangel (4.3.1-1) unstable; urgency=medium + + * RTL-SDR: offset tuning support + * SoapySDR support: 250 ms minimum timeout + * LimeSDR REST API: support GPIO + + -- Edouard Griffiths, F4EXB Fri, 30 Nov 2018 21:14:18 +0100 + +sdrangel (4.3.0-1) unstable; urgency=medium + + * SoapySDR support + * BladeRF2 corrections + * Scope fixes possible seg fault and correct memory processing + * FCDPro/FCDProPlus critical fixes to make it work again + + -- Edouard Griffiths, F4EXB Thu, 22 Nov 2018 21:14:18 +0100 + +sdrangel (4.2.4-1) unstable; urgency=medium + + * LimeSDR: use LimeSuite 18.10.0 for builds + * DSD demod: use 1 dB steps for squelch + * Scope: fixed some trigger issues. Fixes issue #233 + * Scope: implemented trigger holdoff. May fix more trigger issues. + + -- Edouard Griffiths, F4EXB Sat, 27 Oct 2018 21:14:18 +0200 + +sdrangel (4.2.3-1) unstable; urgency=medium + + * Scope: fixed channel rate affecting scope in memory mode. Issue #227 + * Spectrum: limit depth to 1000 when in moving average mode to avoid RAM exhaustion + * Spectrum: reworked phosphor display controls. Re-implements issue #207 + + -- Edouard Griffiths, F4EXB Fri, 19 Oct 2018 21:14:18 +0200 + +sdrangel (4.2.2-1) unstable; urgency=medium + + * Spectrum: option to get max over a number of FFTs. Implements issue #207 + * File Input: fixed wrong times displays due to 32 bit integer ovevlow. Issue #206 + * File Input: implemented play loop and playback acceleration + + -- Edouard Griffiths, F4EXB Sun, 14 Oct 2018 21:14:18 +0200 + +sdrangel (4.2.1-1) unstable; urgency=medium + + * FileRecord improvement with robust header and some fixes. Fixes issue #206 + * BladeRF2 MO Tx fix so that the two channels are used effectively. Fixes issue #225 + * NFM demod: set squelch step to 1 dB + + -- Edouard Griffiths, F4EXB Wed, 10 Oct 2018 21:14:18 +0200 + +sdrangel (4.2.0-1) unstable; urgency=medium + + * LibbladeRF 2.0 support with BladeRF Micro + * Scope: corrected trace memory index position + + -- Edouard Griffiths, F4EXB Sun, 7 Oct 2018 21:14:18 +0200 + +sdrangel (4.1.0-1) unstable; urgency=medium + + * Integrated SDRdaemon with a pair of new channel plugins + * Exchanged UDP sink and source names for better consistency + * Fixed AudioFifo to prevent deadlocks. Fixes issue #210 + + -- Edouard Griffiths, F4EXB Sun, 16 Sep 2018 21:14:18 +0200 + +sdrangel (4.0.7-1) unstable; urgency=medium + + * Scope: removed old scope objects + * Web API: reduced HTTP server debug messages + * Sink plugins: corrected name getters and setters + + -- Edouard Griffiths, F4EXB Sun, 19 Aug 2018 21:14:18 +0200 + +sdrangel (4.0.6-1) unstable; urgency=medium + + * Web API: RTL-SDR: fixed RF bandwidth setting + * Web API: enhanced DV serial and AM demod interfaces + * Web API: fixed bug in PUT/PATCH of modulators not setting differentially + * Fixed power display going to floor value in some demods + * SSB modulator: fixed sample not reset when no modulation is present + + -- Edouard Griffiths, F4EXB Tue, 07 Aug 2018 19:14:18 +0200 + +sdrangel (4.0.5-1) unstable; urgency=medium + + * Web API: handle pre-flight requests + + -- Edouard Griffiths, F4EXB Sun, 22 Jul 2018 09:14:18 +0200 + +sdrangel (4.0.4-1) unstable; urgency=medium + + * Fixed PlutoSDR output sample width. Fixes issue #198 + * Web API: implemented CORS + * Fix preset group delete not removing presets from the preset window + + -- Edouard Griffiths, F4EXB Wed, 18 Jul 2018 19:14:18 +0200 + +sdrangel (4.0.3-1) unstable; urgency=medium + + * Spectrum: linear mode for spectrum + * Scope: fixed power display overlay + + -- Edouard Griffiths, F4EXB Sun, 08 Jul 2018 15:14:18 +0200 + +sdrangel (4.0.2-1) unstable; urgency=medium + + * Spectrum: added averaging + + -- Edouard Griffiths, F4EXB Sun, 01 Jul 2018 21:14:18 +0200 + +sdrangel (4.0.1-1) unstable; urgency=medium + + * DSD demod: added NXDN support + * DATV demod: include it only if FFmpeg > 3.1 is installed + * Fixes for Arch. Manual merge of pull request #183 + * Scope: new magnitude squared projection mainly for radioastronomy + + -- Edouard Griffiths, F4EXB Sat, 23 Jun 2018 09:14:18 +0200 + +sdrangel (4.0.0-1) unstable; urgency=medium + + * Finalization of REST API and server instance + * Removal of old ChannelAnalyzer and TCPSrc plugins + * Renamed Channel Analyzer NG to Channel Analyzer + * DATV demod: added missing AVUTIL cmake variables + + -- Edouard Griffiths, F4EXB Sat, 09 Jun 2018 20:14:18 +0200 + +sdrangel (3.14.7-1) unstable; urgency=medium + + * ChanelAnalyzerNG: added PLL option and source selection with auto correlation + * RTL-SDR: fixed inf/sup decimators + * AM demod: syncrhronous AM detection option + + -- Edouard Griffiths, F4EXB Sun, 20 May 2018 20:14:18 +0200 + +sdrangel (3.14.6-1) unstable; urgency=medium + + * Fixed keyboard input for negative values on realtive integer value dials + * Get rid of ugly native dialogs + * Inf/Sup frequency shift scheme change to be closer to device center frequency + * PlutoSDR input: fixed Inf/Sup frequency shift calculation + * File record default file name with ISO datetime stamp + + -- Edouard Griffiths, F4EXB Fri, 11 May 2018 20:14:18 +0200 + +sdrangel (3.14.5-1) unstable; urgency=medium + + * DSD demod: allow audio rates integer multiples of 8k other than 48k + * Added a benchmark program testing decimators + * Optimization of decimators using even/odd technique + * SSB mod: fixed channel unregistration + * AM demod: fixed delayed squelch + + -- Edouard Griffiths, F4EXB Sun, 06 May 2018 20:14:18 +0200 + +sdrangel (3.14.4-1) unstable; urgency=medium + + * AM demod: squelch buffer to open at start of valid squelch + * NFM demod: same as AM with squelch noise tail cut + * SSB demod: squelch buffer to cut squelch noise tail + * DSD demod: squelch buffer to open at start of valid squelch not loosing any samples + + -- Edouard Griffiths, F4EXB Sun, 22 Apr 2018 17:14:18 +0200 + +sdrangel (3.14.3-1) unstable; urgency=medium + + * LimeSDR: compiled with LimeSuite release 18.04.1 + * LimeSDR: implemented transverter dialog (issue #157) + * UDP source and sink: make sure audio samples are always on 16 bits + * UDP source and sink: dialog elements for address and port + * Reviewed FFT destruction in many channel sources and sinks (issue #159) + + -- Edouard Griffiths, F4EXB Fri, 20 Apr 2018 20:14:18 +0200 + +sdrangel (3.14.2-1) unstable; urgency=medium + + * Web API: settings and report for all channel Tx plugins + * Server: AirspyHF, BladeRF and all channel Tx plugins support + * PVS-Studio static analysis corrections (4) + * NFM demod: fixed AF squelch and audio sample rate handling + * BFM demod: fixed segfault in RDS parser + + -- Edouard Griffiths, F4EXB Sun, 15 Apr 2018 12:14:18 +0200 + +sdrangel (3.14.1-1) unstable; urgency=medium + + * NFM: fixed lowpass filter initialization (CTCSS) + * DSD demod: set FM deviation independent from RF bandwidth + * DSD demod: implemented DMR negative with DSDcc v1.7.5 + * DSD demod: implemented dialog to view the log of status text messages + + -- Edouard Griffiths, F4EXB Sun, 01 Apr 2018 12:14:18 +0200 + +sdrangel (3.14.0-1) unstable; urgency=medium + + * New audio devices management + * DATV demod: fixed message handling and thus screen initialization issue + * Removed UDP/RTP copy audio from channel sink plugins entirely + * Removed UDP address and port from Channel marker + + -- Edouard Griffiths, F4EXB Fri, 30 Mar 2018 16:14:18 +0200 + +sdrangel (3.13.1-1) unstable; urgency=medium + + * Web API: settings and report enry points for AM demod and AirspyHF + * Web API: client Python script scanner example + * LimeSDR: fixed channelA/B frequency setting with latest LimeSuite + + -- Edouard Griffiths, F4EXB Sun, 25 Mar 2018 06:14:18 +0100 + +sdrangel (3.13.0-1) unstable; urgency=medium + + * DATV (Digital Amateur TV) demodulator. + * Option to use RTP protocol for UDP audio for AM, NFM, SSB, WFM. + * LimeSDR: show NCO and center frequency actual values + * DSD demod: new simplified symbol scope display. Reworked GUI. + + -- Edouard Griffiths, F4EXB Sun, 11 Mar 2018 06:14:18 +0100 + +sdrangel (3.12.0-1) unstable; urgency=medium + + * Perseus support. + * 24 bit Rx DSP Debian builds + * DC and IQ correction fixes + * AirspyHF: fall back to official library support + * Test source: implemented phase imbalance + + -- Edouard Griffiths, F4EXB Sun, 11 Feb 2018 12:14:18 +0100 + +sdrangel (3.11.1-1) unstable; urgency=medium + + * Replaced hardcoded bit scaling literals by defines. 24 bit sample option. + + -- Edouard Griffiths, F4EXB Sun, 28 Jan 2018 12:14:18 +0100 + +sdrangel (3.11.0-1) unstable; urgency=medium + + * AirspyHF: support + * Refactored 8 bit samples shifting during decimation (RTL-SDR and HackRF Rx) + * RTL-SDR: implemented RF filter control (tuner bandwidth) + * Airspy, BladeRF, HackRF, PlutoSDR, RTLSDR, SDRPlay: fix for no decimation + * Test source input plugin for test of software internals + * GUI: show REST API URL in about dialog + + -- Edouard Griffiths, F4EXB Sat, 20 Jan 2018 12:14:18 +0100 + +sdrangel (3.10.1-1) unstable; urgency=medium + + * LimeSDR: fixed segfault when stopping one device in multiple stream confguration + * PlutoSDR: fixed segfault when stopping one device in multiple stream confguration + * Channel plugins: apply initial channel settings at construction and start + * SSB demod: refactored decimator filtering + * FileSource: fixed initialization of do apply settings flag possibly preventing start + + -- Edouard Griffiths, F4EXB Sun, 07 Jan 2018 23:14:18 +0100 + +sdrangel (3.10.0-1) unstable; urgency=medium + + * Implemented external command handling from the GUI + * AM, SSB demodulators and SSB modulator: fix sample rate handling + * Enhancements to presets processing and GUI + * Improved build and system info logging + * Web API: added function to set device set focus (GUI only) + + -- Edouard Griffiths, F4EXB Sun, 07 Jan 2018 09:14:18 +0100 + +sdrangel (3.9.1-1) unstable; urgency=medium + + * Transmission: make source channels working concurrently in multiple channel + * Transmission: connect source channel directly to device FIFO in single channel + * NFM, WFM modulators and UDP sink: fixes + * DSD demodulator: use lower cutoff for optional audio high pass filter + + -- Edouard Griffiths, F4EXB Tue, 02 Jan 2018 18:14:18 +0100 + +sdrangel (3.9.0-1) unstable; urgency=medium + + * Server: proof of concept + * DSD demodulator: added optional high pass filter on audio (uese dsdcc v1.7.3) + * Down/Up channelizers: enqeue MsgChannelizerNotification to sample sink/source + * Separate channel sample rate and offset frequency this data from settings + * Use specific method to apply channelizer sample rate and frequency offset changes + + -- Edouard Griffiths, F4EXB Sat, 30 Dec 2017 17:14:18 +0100 + +sdrangel (3.8.6-1) unstable; urgency=medium + + * LimeSDR: fixed second channel solo or first one streaming + * Web API: implemented some device plugins methods: RTLSDR, Lime + + -- Edouard Griffiths, F4EXB Sun, 10 Dec 2017 12:14:18 +0100 + +sdrangel (3.8.5-1) unstable; urgency=medium + + * SSB mod/demod: improve LSB/USB experience + * Web API: online static documentation page + * Web API: implemented all main window methods + * LimeSDR: fixed analog LP filter setting + * LimeSDR: builds done with LimeSuite commit 0167e64 to suport LimeSDR mini + * Debian: added installation of udev rules in postinst script + + -- Edouard Griffiths, F4EXB Sat, 02 Dec 2017 12:14:18 +0100 + +sdrangel (3.8.4-1) unstable; urgency=medium + + * Optimize channel marker signal handling. Applied to all mods and demods + * Fixed value dials direct digits setting + * Implemented minimal version of web API sever + + -- Edouard Griffiths, F4EXB Sat, 18 Nov 2017 12:14:18 +0100 + +sdrangel (3.8.3-1) unstable; urgency=medium + + * ChannelAnalyzerNG: fixed GUI displays when channel sample rate changes + * SSB demod: fixed low cutoff channel marker display + * LimeSDR: increased start/stop stream wait time to 50ms + * New logging system with optional copy to files + + -- Edouard Griffiths, F4EXB Sun, 12 Nov 2017 18:14:18 +0100 + +sdrangel (3.8.2-1) unstable; urgency=medium + + * WFM Demod: fixed segfault due to sequence of actions in demod constructor + + -- Edouard Griffiths, F4EXB Wed, 08 Nov 2017 18:14:18 +0100 + +sdrangel (3.8.1-1) unstable; urgency=medium + + * SSB Mod: refactored bandwidths settings and fixed segfault + * LimeSDR: implemented external clock reference input option + + -- Edouard Griffiths, F4EXB Sun, 05 Nov 2017 18:14:18 +0100 + +sdrangel (3.8.0-1) unstable; urgency=medium + + * Redesigned the device handling to accomodate multi channel devices like LimeSDR + * Refactoring: Separate DeviceAPI from the GUI + + -- Edouard Griffiths, F4EXB Thu, 02 Nov 2017 18:14:18 +0100 + +sdrangel (3.7.8-1) unstable; urgency=medium + + * PlutoSDR: restored and fixed Tx support + * LimeSDR: reworked the start/stop stream so it does not need the workaround delay + + -- Edouard Griffiths, F4EXB Sun, 28 Oct 2017 18:14:18 +0100 + +sdrangel (3.7.7-1) unstable; urgency=medium + + * PlutoSDR: removed Tx support + + -- Edouard Griffiths, F4EXB Sat, 27 Oct 2017 23:14:18 +0200 + +sdrangel (3.7.6-1) unstable; urgency=medium + + * LimeSDR: attempt to fix lockup conditions when Rx and Tx run concurrently + * LimeSDR: handle hardware decimation and sample rate Rx/Tx relation correctly + * Create sdrbase library own CMakeLists.txt + * Created sdrgui library and split off GUI stuff from sdrbase + + -- Edouard Griffiths, F4EXB Sun, 21 Oct 2017 23:14:18 +0200 + +sdrangel (3.7.5-1) unstable; urgency=medium + + * CW keyer: make ramp as 20% of dot length to smoothen even more + * Fixed BFM demod frequency shift + * GUI and demod separation step 1 full + + -- Edouard Griffiths, F4EXB Sun, 14 Oct 2017 23:14:18 +0200 + +sdrangel (3.7.4-1) unstable; urgency=medium + + * GUI and demod separation step 1 full + + -- Edouard Griffiths, F4EXB Sun, 08 Oct 2017 23:14:18 +0200 + +sdrangel (3.7.3-1) unstable; urgency=medium + + * For Airspy, Funcube Pro and Pro+, PlutoSDR Rx and Tx, RTLSDR: + * Button and dialog to set frequency translation for transverter operation + * GUI and demod separation step 1 partial + + -- Edouard Griffiths, F4EXB Wed, 04 Oct 2017 23:14:18 +0200 + +sdrangel (3.7.2-1) unstable; urgency=medium + + * PlutoSDR: Remove from device enumeration if device is not accessible + * RTLSDR: Fixed gains retrieval in the GUI + + -- Edouard Griffiths, F4EXB Sat, 23 Sep 2017 05:14:18 +0200 + +sdrangel (3.7.1-1) unstable; urgency=medium + + * PlutoSDR: Tx support + * LimeSDR: use version 17.09 of LimeSuite providing bug fixes + * GUI segregation: remove device source or sink lifecycle from the GUI + * GUI segregation: handle GUI and device buddy updates separately + + -- Edouard Griffiths, F4EXB Thu, 21 Sep 2017 21:14:18 +0200 + +sdrangel (3.7.0-1) unstable; urgency=medium + + * PlutoSDR: Rx support + * GUI segregation: preliminary works + + -- Edouard Griffiths, F4EXB Thu, 17 Sep 2017 23:14:18 +0200 + +sdrangel (3.6.1-1) unstable; urgency=medium + + * Basic channel settings dialog with title+color update and UDP parameters + * Applied to UDPSink, UDPSource, DSDDemod, AMDemod, BFMDemod, NFMDemod + * DSD, AM, NFM, BFM demods: added possibility to send AF via UDP + + -- Edouard Griffiths, F4EXB Thu, 31 Aug 2017 23:14:18 +0200 + +sdrangel (3.6.0-1) unstable; urgency=medium + + * UDPSink Tx plugin: new + * LimeSDR output: fixed Tx not stopping on stop (issue #50) + + -- Edouard Griffiths, F4EXB Thu, 17 Aug 2017 23:14:18 +0200 + +sdrangel (3.5.5-1) unstable; urgency=medium + + * Output plugins: use fixed time length for sample FIFO of ~0.25s + * Audio modulators: reduce audio input file buffer to 0.1s + + -- Edouard Griffiths, F4EXB Sun, 13 Aug 2017 23:14:18 +0200 + +sdrangel (3.5.4-1) unstable; urgency=medium + + * LimeSDR: fixed NCO lower boundary calculation by flooring to zero + * LimeSDR: corrected NCO de-tune when sample rate or hardware decim/interp changes + * All audio modulators: fixed audio input + * SSB modulator: added an audio compressor + + -- Edouard Griffiths, F4EXB Sun, 06 Aug 2017 23:14:18 +0200 + +sdrangel (3.5.3-1) unstable; urgency=medium + + * SSB demod: anded optional AGC + * ChannelAnalyzerNG: better handling of LSB in the UI + + -- Edouard Griffiths, F4EXB Mon, 31 Jul 2017 09:14:18 +0200 + +sdrangel (3.5.2-1) unstable; urgency=medium + + * HackRF: stop Rx before start Tx automatically and vice versa + * HackRF: added option on Rx to drive Tx frequency change + * SSB mod and demod: make UI displays consistent with DSB, USB and LSB modes + + -- Edouard Griffiths, F4EXB Sat, 22 Jul 2017 09:14:18 +0200 + +sdrangel (3.5.1-1) unstable; urgency=medium + + * LimeSDR input: added individual gains control and ADC rate display + * LimeSDR output: added DAC rate display + * LimeSDR all: added board temperature display + * Added pulseaudio and libqt5multimedia5-plugins in the Debian dependencies + * Updates to build on aarch64 in openSUSE + + -- Edouard Griffiths, F4EXB Sat, 01 Jul 2017 19:14:18 +0200 + +sdrangel (3.5.0-1) unstable; urgency=medium + + * SDRdaemonSink plugin connecting to a distant sdrdaemontx instance + * SDRdemonFEC plugin renamed to SDRdaemonSource + * SDRdaemon plugin deprecated and removed from the build and distributions + * Changed frequency thumbweels color scheme + * Activated compiler warnings and fixed warnings + * Lots of little GUI fixes + + -- Edouard Griffiths, F4EXB Mon, 11 Jun 2017 19:14:18 +0200 + +sdrangel (3.4.5-1) unstable; urgency=medium + + * Removed default constuctors in Moving average and AGC classes + + -- Edouard Griffiths, F4EXB Mon, 11 May 2017 21:14:18 +0100 + +sdrangel (3.4.4-1) unstable; urgency=medium + + * LimeSDR output: fixed timeout value on LMS_SendStream + * LimeSDR: various fixes to make it run smoothly + * LimeSDR: Windows 64 build + * LimeSDR: integrated Debian build + * cmake modules: search lib64 libraries + + -- Edouard Griffiths, F4EXB Mon, 08 May 2017 21:14:18 +0100 + +sdrangel (3.4.3-1) unstable; urgency=medium + + * DSD demod: use version 1.7.1 of dsdcc with PLL for symbol synchronization as an option + * LimeSDR: fixed antenna selection in both input and output plugins + + -- Edouard Griffiths, F4EXB Mon, 08 May 2017 23:14:18 +0100 + +sdrangel (3.4.2-1) unstable; urgency=medium + + * DSD demod: use version 1.7.0 of dsdcc with PLL for symbol synchronization + * DSD demod: kernel >= 4.4.52 workaround for SerialDV + * Code cleanup: cppchack and Eclipse warnings + + -- Edouard Griffiths, F4EXB Wed, 26 Apr 2017 23:14:18 +0100 + +sdrangel (3.4.1-1) unstable; urgency=medium + + * Optimization of halfband interpolation FIR fitler processing + * HackRF support: fixed start/stop sequence + * WFM Demod enhancement + * CW Keyer: specifiy char signedness to fix error with some compilers + + -- Edouard Griffiths, F4EXB Wed, 26 Apr 2017 23:14:18 +0100 + +sdrangel (3.4.0-1) unstable; urgency=medium + + * LimeSDR support with redesign of source and sink management + + -- Edouard Griffiths, F4EXB Mon, 17 Apr 2017 23:14:18 +0100 + +sdrangel (3.3.4-1) unstable; urgency=medium + + * ATV Demod: fixed issue #22. Segfault when starting the plugin while the + * source is running + + -- Edouard Griffiths, F4EXB Mon, 10 Apr 2017 21:14:18 +0100 + +sdrangel (3.3.3-1) unstable; urgency=medium + + * HackRF, BladeRF, RTLSDR plugins: continuous sample rate setting + * ATV: implemented narrow band modes + + -- Edouard Griffiths, F4EXB Sun, 9 Apr 2017 23:14:18 +0100 + +sdrangel (3.3.2-1) unstable; urgency=medium + + * ATV plugins: added 405 lines, 20 and 16 FPS modes + * ATV demodulator: added a scope panel in a tab combo with the TV screen + + -- Edouard Griffiths, F4EXB Wed, 22 Mar 2017 23:14:18 +0100 + +sdrangel (3.3.1-1) unstable; urgency=medium + + * ATV plugins: SSB and vestigiial sideband support + + -- Edouard Griffiths, F4EXB Sun, 19 Mar 2017 23:14:18 +0100 + +sdrangel (3.3.0-1) unstable; urgency=medium + + * NFM demod: new discriminator and optional FM deviation based squelch + * ATV modulator + + -- Edouard Griffiths, F4EXB Wed, 15 Mar 2017 23:14:18 +0100 + +sdrangel (3.2.0-1) unstable; urgency=medium + + * ATV demodulator for amateur Analog TV + * New channel analyzer Channel Analyzer NG with a new generation signal scope + + -- Edouard Griffiths, F4EXB Thu, 02 Mar 2017 23:14:18 +0100 + +sdrangel (3.1.1-1) unstable; urgency=medium + + * DSD demod: adapt to dsdcc version 1.6 + + -- Edouard Griffiths, F4EXB Fri, 13 Jan 2017 03:14:18 +0100 + +sdrangel (3.1.0-1) unstable; urgency=medium + + * HackRF Tx support + + -- Edouard Griffiths, F4EXB Sun, 08 Jan 2017 23:14:18 +0100 + +sdrangel (3.0.1-1) unstable; urgency=medium + + * Fixed audio preferences dialog and handling + + -- Edouard Griffiths, F4EXB Sat, 07 Jan 2017 11:14:18 +0100 + +sdrangel (3.0.0-1) unstable; urgency=medium + + * Implemented real Tx devices (BladeRF and HackRF) for real transmission + + -- Edouard Griffiths, F4EXB Sun, 01 Jan 2017 23:14:18 +0100 + +sdrangel (2.5.2-1) unstable; urgency=medium + + * Changed modulators source device feeding + + -- Edouard Griffiths, F4EXB Sun, 25 Dec 2016 23:14:18 +0100 + +sdrangel (2.5.1-1) unstable; urgency=medium + + * WFM Modulator + + -- Edouard Griffiths, F4EXB Sun, 18 Dec 2016 23:14:18 +0100 + +sdrangel (2.5.0-1) unstable; urgency=medium + + * SSB Modulator + * CW Keyer + + -- Edouard Griffiths, F4EXB Thu, 15 Dec 2016 23:14:18 +0100 + +sdrangel (2.4.0-1) unstable; urgency=medium + + * NFM Modulator + + -- Edouard Griffiths, F4EXB Sun, 05 Dec 2016 23:14:18 +0100 + +sdrangel (2.3.1-1) unstable; urgency=medium + + * AM Modulator: support file input + + -- Edouard Griffiths, F4EXB Sun, 27 Nov 2016 23:14:18 +0100 + +sdrangel (2.3.0-1) unstable; urgency=medium + + * SDRplay support: new input source plugin + + -- Edouard Griffiths, F4EXB Sun, 13 Nov 2016 23:14:18 +0100 + +sdrangel (2.2.2-1) unstable; urgency=medium + + * Baseband Halfband FIR filter optimizations + + -- Edouard Griffiths, F4EXB Wed, 11 Nov 2016 12:25:34 +0100 + +sdrangel (2.2.1-1) unstable; urgency=medium + + * Baseband Tx support (phase 2) + * Code optimizations + + -- Edouard Griffiths, F4EXB Wed, 02 Nov 2016 14:15:15 +0100 + +sdrangel (2.2.0-1) unstable; urgency=medium + + * Initial release as a Debian package + * Embryonic Tx support (phase 1) + + -- Edouard Griffiths, F4EXB Tue, 24 Oct 2016 23:15:15 +0200 diff --git a/devices/soapysdr/devicesoapysdrscan.cpp b/devices/soapysdr/devicesoapysdrscan.cpp index 7dfbb5378..61bb5fbbd 100644 --- a/devices/soapysdr/devicesoapysdrscan.cpp +++ b/devices/soapysdr/devicesoapysdrscan.cpp @@ -73,11 +73,6 @@ void DeviceSoapySDRScan::scan() m_deviceEnums.back().m_label = QString("%1-%2").arg(m_deviceEnums.back().m_driverName).arg(deviceSeq); } - qDebug("DeviceSoapySDRScan::scan: %s #%u %s", - m_deviceEnums.back().m_driverName.toStdString().c_str(), - deviceSeq, - m_deviceEnums.back().m_label.toStdString().c_str()); - if ((kargIt = kit->find("serial")) != kit->end()) { m_deviceEnums.back().m_idKey = QString(kargIt->first.c_str()); @@ -94,6 +89,13 @@ void DeviceSoapySDRScan::scan() m_deviceEnums.back().m_idValue = QString(kargIt->second.c_str()); } + qDebug("DeviceSoapySDRScan::scan: %s #%u %s id: %s=%s", + m_deviceEnums.back().m_driverName.toStdString().c_str(), + deviceSeq, + m_deviceEnums.back().m_label.toStdString().c_str(), + m_deviceEnums.back().m_idKey.toStdString().c_str(), + m_deviceEnums.back().m_idValue.toStdString().c_str()); + // access the device to get the number of Rx and Tx channels and at the same time probe // whether it is available for Soapy diff --git a/doc/img/NFMdemod_plugin.png b/doc/img/NFMdemod_plugin.png index fbacd4b8b..56aeab458 100644 Binary files a/doc/img/NFMdemod_plugin.png and b/doc/img/NFMdemod_plugin.png differ diff --git a/doc/img/NFMdemod_plugin.xcf b/doc/img/NFMdemod_plugin.xcf index 17fa30289..56ba60fd9 100644 Binary files a/doc/img/NFMdemod_plugin.xcf and b/doc/img/NFMdemod_plugin.xcf differ diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 84bac4c75..7da77d43d 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -34,5 +34,6 @@ endif() add_subdirectory(channelrx) add_subdirectory(channeltx) +add_subdirectory(samplemimo) add_subdirectory(samplesource) add_subdirectory(samplesink) diff --git a/plugins/channelrx/demodam/amdemod.cpp b/plugins/channelrx/demodam/amdemod.cpp index 18ab637e1..bde9606e3 100644 --- a/plugins/channelrx/demodam/amdemod.cpp +++ b/plugins/channelrx/demodam/amdemod.cpp @@ -107,6 +107,11 @@ AMDemod::~AMDemod() delete SSBFilter; } +uint32_t AMDemod::getNumberOfDeviceStreams() const +{ + return m_deviceAPI->getNbSourceStreams(); +} + void AMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) { (void) firstOfBurst; @@ -125,14 +130,11 @@ void AMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector if (m_interpolatorDistance < 1.0f) // interpolate { - processOneSample(ci); - - while (m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) + while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) { processOneSample(ci); + m_interpolatorDistanceRemain += m_interpolatorDistance; } - - m_interpolatorDistanceRemain += m_interpolatorDistance; } else // decimate { @@ -258,6 +260,10 @@ void AMDemod::processOneSample(Complex &ci) demod = m_bandpass.filter(demod); demod /= 301.0f; } + else + { + demod = m_lowpass.filter(demod); + } Real attack = (m_squelchCount - 0.05f * m_audioSampleRate) / (0.05f * m_audioSampleRate); sample = demod * StepFunctions::smootherstep(attack) * (m_audioSampleRate/24) * m_settings.m_volume; @@ -381,6 +387,7 @@ void AMDemod::applyAudioSampleRate(int sampleRate) m_interpolatorDistanceRemain = 0; m_interpolatorDistance = (Real) m_inputSampleRate / (Real) sampleRate; m_bandpass.create(301, sampleRate, 300.0, m_settings.m_rfBandwidth / 2.0f); + m_lowpass.create(301, sampleRate, m_settings.m_rfBandwidth / 2.0f); m_audioFifo.setSize(sampleRate); m_squelchDelayLine.resize(sampleRate/5); DSBFilter->create_dsb_filter((2.0f * m_settings.m_rfBandwidth) / (float) sampleRate); @@ -436,6 +443,7 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force) << " m_audioDeviceName: " << settings.m_audioDeviceName << " m_pll: " << settings.m_pll << " m_syncAMOperation: " << (int) settings.m_syncAMOperation + << " m_streamIndex: " << settings.m_streamIndex << " m_useReverseAPI: " << settings.m_useReverseAPI << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress << " m_reverseAPIPort: " << settings.m_reverseAPIPort @@ -453,6 +461,7 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force) m_interpolatorDistanceRemain = 0; m_interpolatorDistance = (Real) m_inputSampleRate / (Real) m_audioSampleRate; m_bandpass.create(301, m_audioSampleRate, 300.0, settings.m_rfBandwidth / 2.0f); + m_lowpass.create(301, m_audioSampleRate, settings.m_rfBandwidth / 2.0f); DSBFilter->create_dsb_filter((2.0f * settings.m_rfBandwidth) / (float) m_audioSampleRate); m_settingsMutex.unlock(); @@ -520,6 +529,10 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force) reverseAPIKeys.append("volume"); } + if ((m_settings.m_streamIndex != settings.m_streamIndex) || force) { + reverseAPIKeys.append("streamIndex"); + } + if (settings.m_useReverseAPI) { bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || @@ -617,6 +630,9 @@ int AMDemod::webapiSettingsPutPatch( AMDemodSettings::SyncAMLSB : (AMDemodSettings::SyncAMOperation) syncAMOperationCode; } + if (channelSettingsKeys.contains("streamIndex")) { + settings.m_streamIndex = response.getAmDemodSettings()->getStreamIndex(); + } if (channelSettingsKeys.contains("useReverseAPI")) { settings.m_useReverseAPI = response.getAmDemodSettings()->getUseReverseApi() != 0; } @@ -690,6 +706,7 @@ void AMDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& respo response.getAmDemodSettings()->setPll(settings.m_pll ? 1 : 0); response.getAmDemodSettings()->setSyncAmOperation((int) m_settings.m_syncAMOperation); + response.getAmDemodSettings()->setStreamIndex(m_settings.m_streamIndex); response.getAmDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); if (response.getAmDemodSettings()->getReverseApiAddress()) { @@ -760,6 +777,9 @@ void AMDemod::webapiReverseSendSettings(QList& channelSettingsKeys, con if (channelSettingsKeys.contains("syncAMOperation") || force) { swgAMDemodSettings->setSyncAmOperation((int) settings.m_syncAMOperation); } + if (channelSettingsKeys.contains("streamIndex") || force) { + swgAMDemodSettings->setStreamIndex(settings.m_streamIndex); + } QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") .arg(settings.m_reverseAPIAddress) diff --git a/plugins/channelrx/demodam/amdemod.h b/plugins/channelrx/demodam/amdemod.h index 7abf39f22..1b2d2f764 100644 --- a/plugins/channelrx/demodam/amdemod.h +++ b/plugins/channelrx/demodam/amdemod.h @@ -158,6 +158,8 @@ public: m_magsqCount = 0; } + uint32_t getNumberOfDeviceStreams() const; + static const QString m_channelIdURI; static const QString m_channelId; @@ -205,6 +207,7 @@ private: MovingAverageUtil m_movingAverage; SimpleAGC<4800> m_volumeAGC; Bandpass m_bandpass; + Lowpass m_lowpass; Lowpass > m_pllFilt; PhaseLockComplex m_pll; fftfilt* DSBFilter; diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index 5e68d7cf6..8a4aabfc6 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -31,6 +31,7 @@ #include "util/simpleserializer.h" #include "util/db.h" #include "gui/basicchannelsettingsdialog.h" +#include "gui/devicestreamselectiondialog.h" #include "dsp/dspengine.h" #include "mainwindow.h" #include "gui/crightclickenabler.h" @@ -230,6 +231,17 @@ void AMDemodGUI::onMenuDialogCalled(const QPoint &p) applySettings(); } + else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) + { + DeviceStreamSelectionDialog dialog(this); + dialog.setNumberOfStreams(m_amDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + dialog.move(p); + dialog.exec(); + + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + applySettings(); + } resetContextMenuType(); } @@ -379,9 +391,20 @@ void AMDemodGUI::displaySettings() ui->ssb->setIcon(m_iconDSBUSB); } + displayStreamIndex(); + blockApplySettings(false); } +void AMDemodGUI::displayStreamIndex() +{ + if (m_deviceUISet->m_deviceMIMOEngine) { + setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); + } else { + setStreamIndicator("S"); // single channel indicator + } +} + void AMDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodam/amdemodgui.h b/plugins/channelrx/demodam/amdemodgui.h index 4849f1196..eb91cb141 100644 --- a/plugins/channelrx/demodam/amdemodgui.h +++ b/plugins/channelrx/demodam/amdemodgui.h @@ -65,6 +65,7 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); + void displayStreamIndex(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodam/amdemodsettings.cpp b/plugins/channelrx/demodam/amdemodsettings.cpp index 615754c0a..55aaf0c36 100644 --- a/plugins/channelrx/demodam/amdemodsettings.cpp +++ b/plugins/channelrx/demodam/amdemodsettings.cpp @@ -41,6 +41,7 @@ void AMDemodSettings::resetToDefaults() m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; m_pll = false; m_syncAMOperation = SyncAMDSB; + m_streamIndex = 0; m_useReverseAPI = false; m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; @@ -53,6 +54,7 @@ QByteArray AMDemodSettings::serialize() const SimpleSerializer s(1); s.writeS32(1, m_inputFrequencyOffset); s.writeS32(2, m_rfBandwidth/100); + s.writeS32(3, m_streamIndex); s.writeS32(4, m_volume*10); s.writeS32(5, m_squelch); @@ -95,6 +97,7 @@ bool AMDemodSettings::deserialize(const QByteArray& data) d.readS32(1, &m_inputFrequencyOffset, 0); d.readS32(2, &tmp, 4); m_rfBandwidth = 100 * tmp; + d.readS32(3, &m_streamIndex, 0); d.readS32(4, &tmp, 20); m_volume = tmp * 0.1; d.readS32(5, &tmp, -40); diff --git a/plugins/channelrx/demodam/amdemodsettings.h b/plugins/channelrx/demodam/amdemodsettings.h index a3b443ec5..8701e78cd 100644 --- a/plugins/channelrx/demodam/amdemodsettings.h +++ b/plugins/channelrx/demodam/amdemodsettings.h @@ -43,6 +43,7 @@ struct AMDemodSettings QString m_audioDeviceName; bool m_pll; SyncAMOperation m_syncAMOperation; + int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx). bool m_useReverseAPI; QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; diff --git a/plugins/channelrx/demodnfm/nfmdemod.cpp b/plugins/channelrx/demodnfm/nfmdemod.cpp index dc5518277..c1aaa931a 100644 --- a/plugins/channelrx/demodnfm/nfmdemod.cpp +++ b/plugins/channelrx/demodnfm/nfmdemod.cpp @@ -90,7 +90,7 @@ NFMDemod::NFMDemod(DeviceAPI *devieAPI) : m_ctcssDetector.setCoefficients(m_audioSampleRate/16, m_audioSampleRate/8.0f); // 0.5s / 2 Hz resolution m_afSquelch.setCoefficients(m_audioSampleRate/2000, 600, m_audioSampleRate, 200, 0, afSqTones); // 0.5ms test period, 300ms average span, audio SR, 100ms attack, no decay - m_lowpass.create(301, m_audioSampleRate, 250.0); + m_ctcssLowpass.create(301, m_audioSampleRate, 250.0); applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true); applySettings(m_settings, true); @@ -163,169 +163,189 @@ void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto Complex c(it->real(), it->imag()); c *= m_nco.nextIQ(); - if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) + if (m_interpolatorDistance < 1.0f) // interpolate { - - qint16 sample; - - double magsqRaw; // = ci.real()*ci.real() + c.imag()*c.imag(); - Real deviation; - - Real demod = m_phaseDiscri.phaseDiscriminatorDelta(ci, magsqRaw, deviation); - - Real magsq = magsqRaw / (SDR_RX_SCALED*SDR_RX_SCALED); - m_movingAverage(magsq); - m_magsqSum += magsq; - - if (magsq > m_magsqPeak) + while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) { - m_magsqPeak = magsq; + processOneSample(ci); + m_interpolatorDistanceRemain += m_interpolatorDistance; } - - m_magsqCount++; - m_sampleCount++; - - // AF processing - - if (m_settings.m_deltaSquelch) + } + else // decimate + { + if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) { - if (m_afSquelch.analyze(demod * m_discriCompensation)) - { - m_afSquelchOpen = m_afSquelch.evaluate(); // ? m_squelchGate + m_squelchDecay : 0; - - if (!m_afSquelchOpen) { - m_squelchDelayLine.zeroBack(m_audioSampleRate/10); // zero out evaluation period - } - } - - if (m_afSquelchOpen) - { - m_squelchDelayLine.write(demod * m_discriCompensation); - - if (m_squelchCount < 2*m_squelchGate) { - m_squelchCount++; - } - } - else - { - m_squelchDelayLine.write(0); - - if (m_squelchCount > 0) { - m_squelchCount--; - } - } + processOneSample(ci); + m_interpolatorDistanceRemain += m_interpolatorDistance; } - else + } + } + + m_settingsMutex.unlock(); +} + +void NFMDemod::processOneSample(Complex &ci) +{ + qint16 sample; + + double magsqRaw; // = ci.real()*ci.real() + c.imag()*c.imag(); + Real deviation; + + Real demod = m_phaseDiscri.phaseDiscriminatorDelta(ci, magsqRaw, deviation); + + Real magsq = magsqRaw / (SDR_RX_SCALED*SDR_RX_SCALED); + m_movingAverage(magsq); + m_magsqSum += magsq; + + if (magsq > m_magsqPeak) + { + m_magsqPeak = magsq; + } + + m_magsqCount++; + m_sampleCount++; + + // AF processing + + if (m_settings.m_deltaSquelch) + { + if (m_afSquelch.analyze(demod * m_discriCompensation)) + { + m_afSquelchOpen = m_afSquelch.evaluate(); // ? m_squelchGate + m_squelchDecay : 0; + + if (!m_afSquelchOpen) { + m_squelchDelayLine.zeroBack(m_audioSampleRate/10); // zero out evaluation period + } + } + + if (m_afSquelchOpen) + { + m_squelchDelayLine.write(demod * m_discriCompensation); + + if (m_squelchCount < 2*m_squelchGate) { + m_squelchCount++; + } + } + else + { + m_squelchDelayLine.write(0); + + if (m_squelchCount > 0) { + m_squelchCount--; + } + } + } + else + { + if ((Real) m_movingAverage < m_squelchLevel) + { + m_squelchDelayLine.write(0); + + if (m_squelchCount > 0) { + m_squelchCount--; + } + } + else + { + m_squelchDelayLine.write(demod * m_discriCompensation); + + if (m_squelchCount < 2*m_squelchGate) { + m_squelchCount++; + } + } + } + + m_squelchOpen = (m_squelchCount > m_squelchGate); + + if (m_settings.m_audioMute) + { + sample = 0; + } + else + { + if (m_squelchOpen) + { + if (m_settings.m_ctcssOn) { - if ((Real) m_movingAverage < m_squelchLevel) - { - m_squelchDelayLine.write(0); + Real ctcss_sample = m_ctcssLowpass.filter(demod * m_discriCompensation); - if (m_squelchCount > 0) { - m_squelchCount--; - } - } - else + if ((m_sampleCount & 7) == 7) // decimate 48k -> 6k { - m_squelchDelayLine.write(demod * m_discriCompensation); + if (m_ctcssDetector.analyze(&ctcss_sample)) + { + int maxToneIndex; - if (m_squelchCount < 2*m_squelchGate) { - m_squelchCount++; + if (m_ctcssDetector.getDetectedTone(maxToneIndex)) + { + if (maxToneIndex+1 != m_ctcssIndex) + { + if (getMessageQueueToGUI()) { + MsgReportCTCSSFreq *msg = MsgReportCTCSSFreq::create(m_ctcssDetector.getToneSet()[maxToneIndex]); + getMessageQueueToGUI()->push(msg); + } + m_ctcssIndex = maxToneIndex+1; + } + } + else + { + if (m_ctcssIndex != 0) + { + if (getMessageQueueToGUI()) { + MsgReportCTCSSFreq *msg = MsgReportCTCSSFreq::create(0); + getMessageQueueToGUI()->push(msg); + } + m_ctcssIndex = 0; + } + } } } } - m_squelchOpen = (m_squelchCount > m_squelchGate); - - if (m_settings.m_audioMute) + if (m_settings.m_ctcssOn && m_ctcssIndexSelected && (m_ctcssIndexSelected != m_ctcssIndex)) { sample = 0; } else { - if (m_squelchOpen) - { - if (m_settings.m_ctcssOn) - { - Real ctcss_sample = m_lowpass.filter(demod * m_discriCompensation); - - if ((m_sampleCount & 7) == 7) // decimate 48k -> 6k - { - if (m_ctcssDetector.analyze(&ctcss_sample)) - { - int maxToneIndex; - - if (m_ctcssDetector.getDetectedTone(maxToneIndex)) - { - if (maxToneIndex+1 != m_ctcssIndex) - { - if (getMessageQueueToGUI()) { - MsgReportCTCSSFreq *msg = MsgReportCTCSSFreq::create(m_ctcssDetector.getToneSet()[maxToneIndex]); - getMessageQueueToGUI()->push(msg); - } - m_ctcssIndex = maxToneIndex+1; - } - } - else - { - if (m_ctcssIndex != 0) - { - if (getMessageQueueToGUI()) { - MsgReportCTCSSFreq *msg = MsgReportCTCSSFreq::create(0); - getMessageQueueToGUI()->push(msg); - } - m_ctcssIndex = 0; - } - } - } - } - } - - if (m_settings.m_ctcssOn && m_ctcssIndexSelected && (m_ctcssIndexSelected != m_ctcssIndex)) - { - sample = 0; - } - else - { - sample = m_bandpass.filter(m_squelchDelayLine.readBack(m_squelchGate)) * m_settings.m_volume; - } - } - else - { - if (m_ctcssIndex != 0) - { - if (getMessageQueueToGUI()) { - MsgReportCTCSSFreq *msg = MsgReportCTCSSFreq::create(0); - getMessageQueueToGUI()->push(msg); - } - - m_ctcssIndex = 0; - } - - sample = 0; + if (m_settings.m_highPass) { + sample = m_bandpass.filter(m_squelchDelayLine.readBack(m_squelchGate)) * m_settings.m_volume; + } else { + sample = m_lowpass.filter(m_squelchDelayLine.readBack(m_squelchGate)) * m_settings.m_volume; } } - - - m_audioBuffer[m_audioBufferFill].l = sample; - m_audioBuffer[m_audioBufferFill].r = sample; - ++m_audioBufferFill; - - if (m_audioBufferFill >= m_audioBuffer.size()) - { - uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); - - if (res != m_audioBufferFill) - { - qDebug("NFMDemod::feed: %u/%u audio samples written", res, m_audioBufferFill); - } - - m_audioBufferFill = 0; - } - - m_interpolatorDistanceRemain += m_interpolatorDistance; } - } + else + { + if (m_ctcssIndex != 0) + { + if (getMessageQueueToGUI()) { + MsgReportCTCSSFreq *msg = MsgReportCTCSSFreq::create(0); + getMessageQueueToGUI()->push(msg); + } + + m_ctcssIndex = 0; + } + + sample = 0; + } + } + + + m_audioBuffer[m_audioBufferFill].l = sample; + m_audioBuffer[m_audioBufferFill].r = sample; + ++m_audioBufferFill; + + if (m_audioBufferFill >= m_audioBuffer.size()) + { + uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); + + if (res != m_audioBufferFill) + { + qDebug("NFMDemod::feed: %u/%u audio samples written", res, m_audioBufferFill); + } + + m_audioBufferFill = 0; + } if (m_audioBufferFill > 0) { @@ -338,8 +358,6 @@ void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto m_audioBufferFill = 0; } - - m_settingsMutex.unlock(); } void NFMDemod::start() @@ -436,8 +454,9 @@ void NFMDemod::applyAudioSampleRate(int sampleRate) m_interpolator.create(16, m_inputSampleRate, m_settings.m_rfBandwidth / 2.2f); m_interpolatorDistanceRemain = 0; m_interpolatorDistance = (Real) m_inputSampleRate / (Real) sampleRate; - m_lowpass.create(301, sampleRate, 250.0); + m_ctcssLowpass.create(301, sampleRate, 250.0); m_bandpass.create(301, sampleRate, 300.0, m_settings.m_afBandwidth); + m_lowpass.create(301, sampleRate, m_settings.m_afBandwidth); m_squelchGate = (sampleRate / 100) * m_settings.m_squelchGate; // gate is given in 10s of ms at 48000 Hz audio sample rate m_squelchCount = 0; // reset squelch open counter m_ctcssDetector.setCoefficients(sampleRate/16, sampleRate/8.0f); // 0.5s / 2 Hz resolution @@ -499,6 +518,7 @@ void NFMDemod::applySettings(const NFMDemodSettings& settings, bool force) << " m_squelch: " << settings.m_squelch << " m_ctcssIndex: " << settings.m_ctcssIndex << " m_ctcssOn: " << settings.m_ctcssOn + << " m_highPass: " << m_settings.m_highPass << " m_audioMute: " << settings.m_audioMute << " m_audioDeviceName: " << settings.m_audioDeviceName << " m_useReverseAPI: " << settings.m_useReverseAPI @@ -550,6 +570,7 @@ void NFMDemod::applySettings(const NFMDemodSettings& settings, bool force) reverseAPIKeys.append("afBandwidth"); m_settingsMutex.lock(); m_bandpass.create(301, m_audioSampleRate, 300.0, settings.m_afBandwidth); + m_lowpass.create(301, m_audioSampleRate, settings.m_afBandwidth); m_settingsMutex.unlock(); } @@ -591,6 +612,10 @@ void NFMDemod::applySettings(const NFMDemodSettings& settings, bool force) setSelectedCtcssIndex(settings.m_ctcssIndex); } + if ((settings.m_highPass != m_settings.m_highPass) || force) { + reverseAPIKeys.append("highPass"); + } + if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { reverseAPIKeys.append("audioDeviceName"); @@ -670,6 +695,9 @@ int NFMDemod::webapiSettingsPutPatch( if (channelSettingsKeys.contains("audioMute")) { settings.m_audioMute = response.getNfmDemodSettings()->getAudioMute() != 0; } + if (channelSettingsKeys.contains("highPass")) { + settings.m_highPass = response.getNfmDemodSettings()->getHighPass() != 0; + } if (channelSettingsKeys.contains("ctcssIndex")) { settings.m_ctcssIndex = response.getNfmDemodSettings()->getCtcssIndex(); } @@ -760,6 +788,7 @@ void NFMDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& resp { response.getNfmDemodSettings()->setAfBandwidth(settings.m_afBandwidth); response.getNfmDemodSettings()->setAudioMute(settings.m_audioMute ? 1 : 0); + response.getNfmDemodSettings()->setHighPass(settings.m_highPass ? 1 : 0); response.getNfmDemodSettings()->setCtcssIndex(settings.m_ctcssIndex); response.getNfmDemodSettings()->setCtcssOn(settings.m_ctcssOn ? 1 : 0); response.getNfmDemodSettings()->setDeltaSquelch(settings.m_deltaSquelch ? 1 : 0); @@ -827,6 +856,9 @@ void NFMDemod::webapiReverseSendSettings(QList& channelSettingsKeys, co if (channelSettingsKeys.contains("audioMute") || force) { swgNFMDemodSettings->setAudioMute(settings.m_audioMute ? 1 : 0); } + if (channelSettingsKeys.contains("highPass") || force) { + swgNFMDemodSettings->setAudioMute(settings.m_highPass ? 1 : 0); + } if (channelSettingsKeys.contains("ctcssIndex") || force) { swgNFMDemodSettings->setCtcssIndex(settings.m_ctcssIndex); } diff --git a/plugins/channelrx/demodnfm/nfmdemod.h b/plugins/channelrx/demodnfm/nfmdemod.h index a4510edb1..0d0aeab47 100644 --- a/plugins/channelrx/demodnfm/nfmdemod.h +++ b/plugins/channelrx/demodnfm/nfmdemod.h @@ -220,8 +220,9 @@ private: Interpolator m_interpolator; Real m_interpolatorDistance; Real m_interpolatorDistanceRemain; - Lowpass m_lowpass; + Lowpass m_ctcssLowpass; Bandpass m_bandpass; + Lowpass m_lowpass; CTCSSDetector m_ctcssDetector; int m_ctcssIndex; // 0 for nothing detected int m_ctcssIndexSelected; @@ -264,6 +265,8 @@ private: void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); void webapiReverseSendSettings(QList& channelSettingsKeys, const NFMDemodSettings& settings, bool force); + void processOneSample(Complex &ci); + private slots: void networkManagerFinished(QNetworkReply *reply); }; diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp index 606ed6a34..010d9166f 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp @@ -201,6 +201,12 @@ void NFMDemodGUI::on_ctcssOn_toggled(bool checked) applySettings(); } +void NFMDemodGUI::on_highPassFilter_toggled(bool checked) +{ + m_settings.m_highPass = checked; + applySettings(); +} + void NFMDemodGUI::on_audioMute_toggled(bool checked) { m_settings.m_audioMute = checked; @@ -398,6 +404,7 @@ void NFMDemodGUI::displaySettings() } ui->ctcssOn->setChecked(m_settings.m_ctcssOn); + ui->highPassFilter->setChecked(m_settings.m_highPass); ui->audioMute->setChecked(m_settings.m_audioMute); ui->ctcss->setCurrentIndex(m_settings.m_ctcssIndex); diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.h b/plugins/channelrx/demodnfm/nfmdemodgui.h index 5dd19ede4..2c8ea09f2 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.h +++ b/plugins/channelrx/demodnfm/nfmdemodgui.h @@ -76,6 +76,7 @@ private slots: void on_squelch_valueChanged(int value); void on_ctcss_currentIndexChanged(int index); void on_ctcssOn_toggled(bool checked); + void on_highPassFilter_toggled(bool checked); void on_audioMute_toggled(bool checked); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.ui b/plugins/channelrx/demodnfm/nfmdemodgui.ui index fc8fe0075..d7cb4820c 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.ui +++ b/plugins/channelrx/demodnfm/nfmdemodgui.ui @@ -574,6 +574,20 @@ + + + + High pass audio filter + + + + + + + :/filter_highpass.png:/filter_highpass.png + + + diff --git a/plugins/channelrx/demodnfm/nfmdemodsettings.cpp b/plugins/channelrx/demodnfm/nfmdemodsettings.cpp index be4b187d9..920e20dc1 100644 --- a/plugins/channelrx/demodnfm/nfmdemodsettings.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodsettings.cpp @@ -53,6 +53,7 @@ void NFMDemodSettings::resetToDefaults() m_rgbColor = QColor(255, 0, 0).rgb(); m_title = "NFM Demodulator"; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_highPass = true; m_useReverseAPI = false; m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; @@ -68,6 +69,7 @@ QByteArray NFMDemodSettings::serialize() const s.writeS32(3, m_afBandwidth/1000.0); s.writeS32(4, m_volume*10.0); s.writeS32(5, static_cast(m_squelch)); + s.writeBool(6, m_highPass); s.writeU32(7, m_rgbColor); s.writeS32(8, m_ctcssIndex); s.writeBool(9, m_ctcssOn); @@ -123,6 +125,7 @@ bool NFMDemodSettings::deserialize(const QByteArray& data) m_volume = tmp / 10.0; d.readS32(5, &tmp, -30); m_squelch = (tmp < -100 ? tmp/10 : tmp) * 1.0; + d.readBool(6, &m_highPass, true); d.readU32(7, &m_rgbColor, QColor(255, 0, 0).rgb()); d.readS32(8, &m_ctcssIndex, 0); d.readBool(9, &m_ctcssOn, false); diff --git a/plugins/channelrx/demodnfm/nfmdemodsettings.h b/plugins/channelrx/demodnfm/nfmdemodsettings.h index 35043e0de..bbad59173 100644 --- a/plugins/channelrx/demodnfm/nfmdemodsettings.h +++ b/plugins/channelrx/demodnfm/nfmdemodsettings.h @@ -42,6 +42,7 @@ struct NFMDemodSettings quint32 m_rgbColor; QString m_title; QString m_audioDeviceName; + bool m_highPass; bool m_useReverseAPI; QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; diff --git a/plugins/channelrx/demodnfm/readme.md b/plugins/channelrx/demodnfm/readme.md index aa3fff81b..78406836c 100644 --- a/plugins/channelrx/demodnfm/readme.md +++ b/plugins/channelrx/demodnfm/readme.md @@ -10,7 +10,7 @@ This plugin can be used to listen to a narrowband FM modulated signal. "Narrowba

1: Frequency shift from center frequency of reception value

-Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. Left click on a digit sets the cursor position at this digit. +Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. Left click on a digit sets the cursor position at this digit.

2: Channel power

@@ -26,13 +26,13 @@ Average total power in dB relative to a +/- 1.0 amplitude signal received in the This is the bandwidth in kHz of the channel signal before demodulation. It can be set in steps as 5, 6.25, 8.33, 10, 12.5, 15, 20, 25 and 40 kHz. The expected one side frequency deviation is 0.4 times the bandwidth. -☞ The demodulation is done at the channel sample rate which is guaranteed not to be lower than the requested audio sample rate but can possibly be equal to it. This means that for correct operation in any case you must ensure that the sample rate of the audio device is not lower than the Nyquist rate required to process this channel bandwidth. +☞ The demodulation is done at the channel sample rate which is guaranteed not to be lower than the requested audio sample rate but can possibly be equal to it. This means that for correct operation in any case you must ensure that the sample rate of the audio device is not lower than the Nyquist rate required to process this channel bandwidth. ☞ The channel sample rate is always the baseband signal rate divided by an integer power of two so depending on the baseband sample rate obtained from the sampling device you could also guarantee a minimal channel bandwidth. For example with a 125 kS/s baseband sample rate and a 8 kS/s audio sample rate the channel sample rate cannot be lower than 125/8 = 15.625 kS/s (125/16 = 7.8125 kS/s is too small) which is still OK for 5 or 6.25 kHz channel bandwidths.

5: AF bandwidth

-This is the bandwidth of the audio signal in kHz (i.e. after demodulation). It can be set in continuous kHz steps from 1 to 20 kHz. +This is the bandwidth of the audio signal in kHz (i.e. after demodulation). It can be set in continuous kHz steps from 1 to 20 kHz.

6: Volume

@@ -50,7 +50,7 @@ Case when the delta/Level squelch control (7) is off (power). This is the squelc

Audio frequency delta mode

-Case when the delta/Level squelch control (7) is on (delta). In this mode the squelch compares the power of the demodulated audio signal in a low frequency band and a high frequency band. In the absence of signal the discriminator response is nearly flat and the power in the two bands is more or less balanced. In the presence of a signal the lower band will receive more power than the higher band. The squelch does the ratio of both powers and the squelch is opened if this ratio is lower than the threshold given in percent. +Case when the delta/Level squelch control (7) is on (delta). In this mode the squelch compares the power of the demodulated audio signal in a low frequency band and a high frequency band. In the absence of signal the discriminator response is nearly flat and the power in the two bands is more or less balanced. In the presence of a signal the lower band will receive more power than the higher band. The squelch does the ratio of both powers and the squelch is opened if this ratio is lower than the threshold given in percent. A ratio of 1 (100%) will always open the squelch and a ratio of 0 will always close it. The value can be varied to detect more distorted and thus weak signals towards the higher values. The button rotation runs from higher to lower as you turn it clockwise thus giving the same feel as in power mode. The best ratio for a standard NFM transmission is ~40%. @@ -66,7 +66,7 @@ This is the squelch gate in milliseconds. The squelch input must be open for thi

10: CTCSS on/off

-Use the checkbox to toggle CTCSS activation. When activated it will look for a tone squelch in the demodulated signal and display its frequency (see 10). +Use the checkbox to toggle CTCSS activation. When activated it will look for a tone squelch in the demodulated signal and display its frequency (see 10).

11: CTCSS tone

@@ -76,7 +76,11 @@ This is the tone squelch in Hz. It can be selected using the toolbox among the u This is the value of the tone squelch received when the CTCSS is activated. It displays `--` if the CTCSS system is de-activated. -

13: Audio mute and audio output select

+

13: Audio high pass filter

+ +Toggle a 300 Hz cutoff high pass filter on audio to cut-off CTCSS frequencies. It is on by default for normal audio channels usage. You can switch it off to pipe the audio in programs requiring DC like DSD+ or Multimon. + +

14: Audio mute and audio output select

Left click on this button to toggle audio mute for this channel. The button will light up in green if the squelch is open. This helps identifying which channels are active in a multi-channel configuration. diff --git a/plugins/channelrx/demodssb/ssbdemod.cpp b/plugins/channelrx/demodssb/ssbdemod.cpp index b76a6dd79..afe9f4db0 100644 --- a/plugins/channelrx/demodssb/ssbdemod.cpp +++ b/plugins/channelrx/demodssb/ssbdemod.cpp @@ -157,124 +157,133 @@ void SSBDemod::configure(MessageQueue* messageQueue, void SSBDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly) { (void) positiveOnly; - Complex ci; - fftfilt::cmplx *sideband; - int n_out; - + Complex ci; m_settingsMutex.lock(); - int decim = 1<<(m_spanLog2 - 1); - unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1) - for(SampleVector::const_iterator it = begin; it < end; ++it) { Complex c(it->real(), it->imag()); c *= m_nco.nextIQ(); - if(m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) - { - if (m_dsb) - { - n_out = DSBFilter->runDSB(ci, &sideband); - } - else - { - n_out = SSBFilter->runSSB(ci, &sideband, m_usb); - } + if (m_interpolatorDistance < 1.0f) // interpolate + { + while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } + else + { + if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } + } - m_interpolatorDistanceRemain += m_interpolatorDistance; - } - else - { - n_out = 0; - } + m_settingsMutex.unlock(); +} - for (int i = 0; i < n_out; i++) - { - // Downsample by 2^(m_scaleLog2 - 1) for SSB band spectrum display - // smart decimation with bit gain using float arithmetic (23 bits significand) +void SSBDemod::processOneSample(Complex &ci) +{ + fftfilt::cmplx *sideband; + int n_out = 0; + int decim = 1<<(m_spanLog2 - 1); + unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1) - m_sum += sideband[i]; + if (m_dsb) { + n_out = DSBFilter->runDSB(ci, &sideband); + } else { + n_out = SSBFilter->runSSB(ci, &sideband, m_usb); + } - if (!(m_undersampleCount++ & decim_mask)) - { - Real avgr = m_sum.real() / decim; - Real avgi = m_sum.imag() / decim; - m_magsq = (avgr * avgr + avgi * avgi) / (SDR_RX_SCALED*SDR_RX_SCALED); + for (int i = 0; i < n_out; i++) + { + // Downsample by 2^(m_scaleLog2 - 1) for SSB band spectrum display + // smart decimation with bit gain using float arithmetic (23 bits significand) - m_magsqSum += m_magsq; + m_sum += sideband[i]; - if (m_magsq > m_magsqPeak) + if (!(m_undersampleCount++ & decim_mask)) + { + Real avgr = m_sum.real() / decim; + Real avgi = m_sum.imag() / decim; + m_magsq = (avgr * avgr + avgi * avgi) / (SDR_RX_SCALED*SDR_RX_SCALED); + + m_magsqSum += m_magsq; + + if (m_magsq > m_magsqPeak) + { + m_magsqPeak = m_magsq; + } + + m_magsqCount++; + + if (!m_dsb & !m_usb) + { // invert spectrum for LSB + m_sampleBuffer.push_back(Sample(avgi, avgr)); + } + else + { + m_sampleBuffer.push_back(Sample(avgr, avgi)); + } + + m_sum.real(0.0); + m_sum.imag(0.0); + } + + float agcVal = m_agcActive ? m_agc.feedAndGetValue(sideband[i]) : 0.1; + fftfilt::cmplx& delayedSample = m_squelchDelayLine.readBack(m_agc.getStepDownDelay()); + m_audioActive = delayedSample.real() != 0.0; + m_squelchDelayLine.write(sideband[i]*agcVal); + + if (m_audioMute) + { + m_audioBuffer[m_audioBufferFill].r = 0; + m_audioBuffer[m_audioBufferFill].l = 0; + } + else + { + fftfilt::cmplx z = m_agcActive ? delayedSample * m_agc.getStepValue() : delayedSample; + + if (m_audioBinaual) + { + if (m_audioFlipChannels) { - m_magsqPeak = m_magsq; + m_audioBuffer[m_audioBufferFill].r = (qint16)(z.imag() * m_volume); + m_audioBuffer[m_audioBufferFill].l = (qint16)(z.real() * m_volume); } + else + { + m_audioBuffer[m_audioBufferFill].r = (qint16)(z.real() * m_volume); + m_audioBuffer[m_audioBufferFill].l = (qint16)(z.imag() * m_volume); + } + } + else + { + Real demod = (z.real() + z.imag()) * 0.7; + qint16 sample = (qint16)(demod * m_volume); + m_audioBuffer[m_audioBufferFill].l = sample; + m_audioBuffer[m_audioBufferFill].r = sample; + } + } - m_magsqCount++; + ++m_audioBufferFill; - if (!m_dsb & !m_usb) - { // invert spectrum for LSB - m_sampleBuffer.push_back(Sample(avgi, avgr)); - } - else - { - m_sampleBuffer.push_back(Sample(avgr, avgi)); - } + if (m_audioBufferFill >= m_audioBuffer.size()) + { + uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); - m_sum.real(0.0); - m_sum.imag(0.0); - } + if (res != m_audioBufferFill) + { + qDebug("SSBDemod::feed: %u/%u samples written", res, m_audioBufferFill); + } - float agcVal = m_agcActive ? m_agc.feedAndGetValue(sideband[i]) : 10.0; // 10.0 for 3276.8, 1.0 for 327.68 - fftfilt::cmplx& delayedSample = m_squelchDelayLine.readBack(m_agc.getStepDownDelay()); - m_audioActive = delayedSample.real() != 0.0; - m_squelchDelayLine.write(sideband[i]*agcVal); - - if (m_audioMute) - { - m_audioBuffer[m_audioBufferFill].r = 0; - m_audioBuffer[m_audioBufferFill].l = 0; - } - else - { - fftfilt::cmplx z = delayedSample * m_agc.getStepValue(); - - if (m_audioBinaual) - { - if (m_audioFlipChannels) - { - m_audioBuffer[m_audioBufferFill].r = (qint16)(z.imag() * m_volume); - m_audioBuffer[m_audioBufferFill].l = (qint16)(z.real() * m_volume); - } - else - { - m_audioBuffer[m_audioBufferFill].r = (qint16)(z.real() * m_volume); - m_audioBuffer[m_audioBufferFill].l = (qint16)(z.imag() * m_volume); - } - } - else - { - Real demod = (z.real() + z.imag()) * 0.7; - qint16 sample = (qint16)(demod * m_volume); - m_audioBuffer[m_audioBufferFill].l = sample; - m_audioBuffer[m_audioBufferFill].r = sample; - } - } - - ++m_audioBufferFill; - - if (m_audioBufferFill >= m_audioBuffer.size()) - { - uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); - - if (res != m_audioBufferFill) - { - qDebug("SSBDemod::feed: %u/%u samples written", res, m_audioBufferFill); - } - - m_audioBufferFill = 0; - } - } - } + m_audioBufferFill = 0; + } + } uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); @@ -292,7 +301,6 @@ void SSBDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto m_sampleBuffer.clear(); - m_settingsMutex.unlock(); } void SSBDemod::start() @@ -389,7 +397,8 @@ void SSBDemod::applyChannelSettings(int inputSampleRate, int inputFrequencyOffse if ((m_inputSampleRate != inputSampleRate) || force) { m_settingsMutex.lock(); - m_interpolator.create(16, inputSampleRate, m_Bandwidth * 1.5f, 2.0f); + Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > inputSampleRate ? inputSampleRate : (m_Bandwidth * 1.5f); + m_interpolator.create(16, inputSampleRate, interpolatorBandwidth, 2.0f); m_interpolatorDistanceRemain = 0; m_interpolatorDistance = (Real) inputSampleRate / (Real) m_audioSampleRate; m_settingsMutex.unlock(); @@ -409,7 +418,8 @@ void SSBDemod::applyAudioSampleRate(int sampleRate) m_settingsMutex.lock(); - m_interpolator.create(16, m_inputSampleRate, m_Bandwidth * 1.5f, 2.0f); + Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > m_inputSampleRate ? m_inputSampleRate : (m_Bandwidth * 1.5f); + m_interpolator.create(16, m_inputSampleRate, interpolatorBandwidth, 2.0f); m_interpolatorDistanceRemain = 0; m_interpolatorDistance = (Real) m_inputSampleRate / (Real) sampleRate; @@ -508,7 +518,8 @@ void SSBDemod::applySettings(const SSBDemodSettings& settings, bool force) m_LowCutoff = lowCutoff; m_settingsMutex.lock(); - m_interpolator.create(16, m_inputSampleRate, m_Bandwidth * 1.5f, 2.0f); + Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > m_inputSampleRate ? m_inputSampleRate : (m_Bandwidth * 1.5f); + m_interpolator.create(16, m_inputSampleRate, interpolatorBandwidth, 2.0f); m_interpolatorDistanceRemain = 0; m_interpolatorDistance = (Real) m_inputSampleRate / (Real) m_audioSampleRate; SSBFilter->create_filter(m_LowCutoff / (float) m_audioSampleRate, m_Bandwidth / (float) m_audioSampleRate); diff --git a/plugins/channelrx/demodssb/ssbdemod.h b/plugins/channelrx/demodssb/ssbdemod.h index 873051c2f..4b7d1542d 100644 --- a/plugins/channelrx/demodssb/ssbdemod.h +++ b/plugins/channelrx/demodssb/ssbdemod.h @@ -137,6 +137,7 @@ public: } uint32_t getAudioSampleRate() const { return m_audioSampleRate; } + uint32_t getInputSampleRate() const { return m_inputSampleRate; } double getMagSq() const { return m_magsq; } bool getAudioActive() const { return m_audioActive; } @@ -338,6 +339,8 @@ private: void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); void webapiReverseSendSettings(QList& channelSettingsKeys, const SSBDemodSettings& settings, bool force); + void processOneSample(Complex &ci); + private slots: void networkManagerFinished(QNetworkReply *reply); }; diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index decf0130d..2bd3532e4 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -371,14 +371,25 @@ void SSBDemodGUI::applySettings(bool force) } } +int SSBDemodGUI::spanLog2Limit(int spanLog2) +{ + while (((m_ssbDemod->getAudioSampleRate() / (1< m_ssbDemod->getInputSampleRate()) && (spanLog2 < 4)) { + spanLog2++; + } + + return spanLog2; +} + void SSBDemodGUI::applyBandwidths(int spanLog2, bool force) { + spanLog2 = spanLog2Limit(spanLog2); + ui->spanLog2->setMaximum(5 - spanLog2Limit(1)); bool dsb = ui->dsb->isChecked(); //int spanLog2 = ui->spanLog2->value(); m_spectrumRate = m_ssbDemod->getAudioSampleRate() / (1<BW->value(); int lw = ui->lowCut->value(); - int bwMax = m_ssbDemod->getAudioSampleRate() / (100*(1<getAudioSampleRate() / (100*(1<getInputSampleRate()/100); int tickInterval = m_spectrumRate / 1200; tickInterval = tickInterval == 0 ? 1 : tickInterval; diff --git a/plugins/channelrx/demodssb/ssbdemodgui.h b/plugins/channelrx/demodssb/ssbdemodgui.h index 20639178c..fceead47a 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.h +++ b/plugins/channelrx/demodssb/ssbdemodgui.h @@ -71,6 +71,7 @@ private: bool blockApplySettings(bool block); void applySettings(bool force = false); void applyBandwidths(int spanLog2, bool force = false); + int spanLog2Limit(int spanLog2); void displaySettings(); void displayAGCPowerThreshold(int value); diff --git a/plugins/channelrx/demodssb/ssbdemodsettings.cpp b/plugins/channelrx/demodssb/ssbdemodsettings.cpp index 9990d021e..db57b8db2 100644 --- a/plugins/channelrx/demodssb/ssbdemodsettings.cpp +++ b/plugins/channelrx/demodssb/ssbdemodsettings.cpp @@ -45,12 +45,12 @@ void SSBDemodSettings::resetToDefaults() m_audioMute = false; m_agc = false; m_agcClamping = false; - m_agcPowerThreshold = -40; + m_agcPowerThreshold = -100; m_agcThresholdGate = 4; m_agcTimeLog2 = 7; m_rfBandwidth = 3000; m_lowCutoff = 300; - m_volume = 3.0; + m_volume = 1.0; m_spanLog2 = 3; m_inputFrequencyOffset = 0; m_rgbColor = QColor(0, 255, 0).rgb(); diff --git a/plugins/channelrx/demodssb/ssbplugin.cpp b/plugins/channelrx/demodssb/ssbplugin.cpp index 41f1e6d7f..932de22d7 100644 --- a/plugins/channelrx/demodssb/ssbplugin.cpp +++ b/plugins/channelrx/demodssb/ssbplugin.cpp @@ -9,7 +9,7 @@ const PluginDescriptor SSBPlugin::m_pluginDescriptor = { QString("SSB Demodulator"), - QString("4.5.2"), + QString("4.8.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/freqtracker/freqtracker.cpp b/plugins/channelrx/freqtracker/freqtracker.cpp index ff47abf57..417561f94 100644 --- a/plugins/channelrx/freqtracker/freqtracker.cpp +++ b/plugins/channelrx/freqtracker/freqtracker.cpp @@ -749,7 +749,7 @@ void FreqTracker::tick() m_avgDeltaFreq = m_settings.m_alphaEMA*getFrequency() + (1.0 - m_settings.m_alphaEMA)*m_avgDeltaFreq; } - if (m_tickCount < 19) + if (m_tickCount < 9) { m_tickCount++; } @@ -757,7 +757,7 @@ void FreqTracker::tick() { if ((m_settings.m_tracking) && getSquelchOpen()) { - uint32_t decayDivider = 1000.0 * m_settings.m_alphaEMA; + uint32_t decayDivider = 200.0 * m_settings.m_alphaEMA; int decayAmount = m_channelSampleRate < decayDivider ? 1 : m_channelSampleRate / decayDivider; int trim = m_channelSampleRate / 1000; diff --git a/plugins/samplemimo/CMakeLists.txt b/plugins/samplemimo/CMakeLists.txt new file mode 100644 index 000000000..acc069928 --- /dev/null +++ b/plugins/samplemimo/CMakeLists.txt @@ -0,0 +1,3 @@ +project(samplemimo) + +add_subdirectory(testmi) diff --git a/plugins/samplemimo/testmi/CMakeLists.txt b/plugins/samplemimo/testmi/CMakeLists.txt new file mode 100644 index 000000000..53c94644b --- /dev/null +++ b/plugins/samplemimo/testmi/CMakeLists.txt @@ -0,0 +1,54 @@ +project(testmi) + +set(testmi_SOURCES + testmi.cpp + testmiplugin.cpp + testmithread.cpp + testmisettings.cpp +) + +set(testmi_HEADERS + testmi.h + testmiplugin.h + testmithread.h + testmisettings.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client +) + +if (NOT SERVER_MODE) + set (testmi_SOURCES + ${testmi_SOURCES} + testmigui.cpp + testmigui.ui + ) + set(testmi_HEADERS + ${testmi_HEADERS} + testsourcegui.h + ) + set(TARGET_NAME mimotestmi) + set(TARGET_LIB "Qt5::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME mimotestmisrv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +add_library(${TARGET_NAME} SHARED + ${testmi_SOURCES} +) + +target_link_libraries(${TARGET_NAME} + Qt5::Core + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} + swagger +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) diff --git a/plugins/samplemimo/testmi/readme.md b/plugins/samplemimo/testmi/readme.md new file mode 100644 index 000000000..979a42991 --- /dev/null +++ b/plugins/samplemimo/testmi/readme.md @@ -0,0 +1,134 @@ +

Test source input plugin

+ +

Introduction

+ +This input sample source plugin is an internal continuous wave generator that can be used to carry out test of software internals. + +

Build

+ +The plugin is present in the core of the software and thus is always present in the list of sources. + +

Interface

+ +![Test source input plugin GUI](../../../doc/img/TestSourceInput_plugin.png) + +

1: Common stream parameters

+ +![Remote source input stream GUI](../../../doc/img/RemoteInput_plugin_01.png) + +

1.1: Frequency

+ +This is the center frequency of reception in kHz. + +

1.2: Start/Stop

+ +Device start / stop button. + + - Blue triangle icon: device is ready and can be started + - Green square icon: device is running and can be stopped + - Magenta (or pink) square icon: an error occurred. In the case the device was accidentally disconnected you may click on the icon, plug back in and start again. + +

1.3: Record

+ +Record baseband I/Q stream toggle button + +

1.4: Stream sample rate

+ +Baseband I/Q sample rate in kS/s. This is the device to host sample rate (3) divided by the decimation factor (4). + +

2: Various options

+ +![Test source input plugin GUI 2](../../../doc/img/TestSourceInput_plugin_2.png) + +

2.1: Auto corrections

+ +This combo box control the local DSP auto correction options: + + - **None**: no correction + - **DC**: auto remove DC component + - **DC+IQ**: auto remove DC component and correct I/Q balance. + +

2.2: Decimation factor

+ +The I/Q stream from the generator is downsampled by a power of two before being sent to the passband. Possible values are increasing powers of two: 1 (no decimation), 2, 4, 8, 16, 32. This exercises the decimation chain. + +This exercises the decimation chain. + +

2.3: Baseband center frequency position relative the center frequency

+ + - **Cen**: the decimation operation takes place around the center frequency Fs + - **Inf**: the decimation operation takes place around Fs - Fc. + - **Sup**: the decimation operation takes place around Fs + Fc. + +With SR as the sample rate before decimation Fc is calculated as: + + - if decimation n is 4 or lower: Fc = SR/2^(log2(n)-1). The device center frequency is on the side of the baseband. You need a RF filter bandwidth at least twice the baseband. + - if decimation n is 8 or higher: Fc = SR/n. The device center frequency is half the baseband away from the side of the baseband. You need a RF filter bandwidth at least 3 times the baseband. + +

2.4: Sample size

+ +This is the sample size in number of bits. It corresponds to the actual sample size used by the devices supported: + + - **8**: RTL-SDR, HackRF + - **12**: Airspy, BladeRF, LimeSDR, PlutoSDR, SDRplay + - **16**: Airspy HF+, FCD Pro, FCD Pro+ + +

3: Sample rate

+ +This controls the generator sample rate in samples per second. + +

4: Modulation

+ + - **No**: No modulation + - **AM**: Amplitude modulation (AM) + - **FM**: Frequency modulation (FM) + - **P0**: Pattern 0 is a binary pattern + - Pulse width: 150 samples + - Sync pattern: 010 at full amplitude + - Binary pattern LSB first on 3 bits from 0 to 7 at 0.3 amplitude + - **P1**: Pattern 1 is a sawtooth pattern + - Pulse width: 1000 samples + - Starts at full amplitude then amplitude decreases linearly down to zero + - **P2**: Pattern 2 is a 50% duty cycle square pattern + - Pulse width: 1000 samples + - Starts with a full amplitude pulse then down to zero for the duration of one pulse + +

5: Modulating tone frequency

+ +This controls the modulating tone frequency in kHz in 10 Hz steps. + +

6: Carrier shift from center frequency

+ +Use this control to set the offset of the carrier from the center frequency of reception. + +

7: AM modulation factor

+ +This controls the AM modulation factor from 0 to 99% + +

8: FM deviation

+ +This controls the frequency modulation deviation in kHz in 100 Hz steps. It cannot exceed the sample rate. + +

9: Amplitude coarse control

+ +This slider controls the number of amplitude bits by steps of 100 bits. The total number of amplitude bits appear on the right. + +

10: Amplitude fine control

+ +This slider controls the number of amplitude bits by steps of 1 bit. The signal power in dB relative to the maximum power (full bit range) appear on the right. + +

11: DC bias

+ +Use this slider to give a DC component in percentage of maximum amplitude. + +

12: I bias

+ +Use this slider to give an in-phase (I) bias in percentage of maximum amplitude. + +

13: Q bias

+ +Use this slider to give an quadrature-phase (Q) bias in percentage of maximum amplitude. + +

14: Phase imbalance

+ +Use this slider to introduce a phase imbalance in percentage of full period (continuous wave) or percentage of I signal injected in Q (AM, FM). diff --git a/plugins/samplemimo/testmi/testmi.cpp b/plugins/samplemimo/testmi/testmi.cpp new file mode 100644 index 000000000..8e11b548c --- /dev/null +++ b/plugins/samplemimo/testmi/testmi.cpp @@ -0,0 +1,754 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include +#include +#include +#include + +#include "SWGDeviceSettings.h" +#include "SWGDeviceState.h" + +#include "device/deviceapi.h" +#include "dsp/dspcommands.h" +#include "dsp/dspengine.h" +#include "dsp/dspdevicemimoengine.h" +#include "dsp/devicesamplesource.h" +#include "dsp/filerecord.h" + +#include "testmithread.h" +#include "testmi.h" + +MESSAGE_CLASS_DEFINITION(TestMI::MsgConfigureTestSource, Message) +MESSAGE_CLASS_DEFINITION(TestMI::MsgFileRecord, Message) +MESSAGE_CLASS_DEFINITION(TestMI::MsgStartStop, Message) + + +TestMI::TestMI(DeviceAPI *deviceAPI) : + m_deviceAPI(deviceAPI), + m_settings(), + m_testSourceThread(0), + m_deviceDescription(), + m_running(false), + m_masterTimer(deviceAPI->getMasterTimer()) +{ + m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); + m_deviceAPI->addSourceStream(); // Add a new source stream data set in the engine + m_deviceAPI->addAncillarySink(m_fileSink); + m_sampleSinkFifos.push_back(SampleSinkFifo(96000 * 4)); + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); +} + +TestMI::~TestMI() +{ + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + + m_deviceAPI->removeAncillarySink(m_fileSink); + m_deviceAPI->removeLastSourceStream(); // Remove the last source stream data set in the engine + delete m_fileSink; +} + +void TestMI::destroy() +{ + delete this; +} + +void TestMI::init() +{ + applySettings(m_settings, true); +} + +bool TestMI::start() +{ + QMutexLocker mutexLocker(&m_mutex); + + if (m_running) stop(); + + m_testSourceThread = new TestMIThread(&m_sampleSinkFifos[0]); + m_testSourceThread->setSamplerate(m_settings.m_sampleRate); + m_testSourceThread->startStop(true); + + mutexLocker.unlock(); + + applySettings(m_settings, true); + m_running = true; + + return true; +} + +void TestMI::stop() +{ + QMutexLocker mutexLocker(&m_mutex); + + if (m_testSourceThread != 0) + { + m_testSourceThread->startStop(false); + m_testSourceThread->deleteLater(); + m_testSourceThread = 0; + } + + m_running = false; +} + +QByteArray TestMI::serialize() const +{ + return m_settings.serialize(); +} + +bool TestMI::deserialize(const QByteArray& data) +{ + bool success = true; + + if (!m_settings.deserialize(data)) + { + m_settings.resetToDefaults(); + success = false; + } + + MsgConfigureTestSource* message = MsgConfigureTestSource::create(m_settings, true); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) + { + MsgConfigureTestSource* messageToGUI = MsgConfigureTestSource::create(m_settings, true); + m_guiMessageQueue->push(messageToGUI); + } + + return success; +} + +const QString& TestMI::getDeviceDescription() const +{ + return m_deviceDescription; +} + +int TestMI::getSourceSampleRate(int index) const +{ + (void) index; + return m_settings.m_sampleRate/(1<push(messageToGUI); + } +} + +bool TestMI::handleMessage(const Message& message) +{ + if (MsgConfigureTestSource::match(message)) + { + MsgConfigureTestSource& conf = (MsgConfigureTestSource&) message; + qDebug() << "TestMI::handleMessage: MsgConfigureTestSource"; + + bool success = applySettings(conf.getSettings(), conf.getForce()); + + if (!success) + { + qDebug("TestMI::handleMessage: config error"); + } + + return true; + } + else if (MsgFileRecord::match(message)) + { + MsgFileRecord& conf = (MsgFileRecord&) message; + qDebug() << "TestMI::handleMessage: MsgFileRecord: " << conf.getStartStop(); + + if (conf.getStartStop()) + { + if (m_settings.m_fileRecordName.size() != 0) { + m_fileSink->setFileName(m_settings.m_fileRecordName); + } else { + m_fileSink->genUniqueFileName(m_deviceAPI->getDeviceUID()); + } + + m_fileSink->startRecording(); + } + else + { + m_fileSink->stopRecording(); + } + + return true; + } + else if (MsgStartStop::match(message)) + { + MsgStartStop& cmd = (MsgStartStop&) message; + qDebug() << "TestMI::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop"); + + if (cmd.getStartStop()) + { + if (m_deviceAPI->initDeviceEngine()) + { + m_deviceAPI->startDeviceEngine(); + } + } + else + { + m_deviceAPI->stopDeviceEngine(); + } + + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + + return true; + } + else + { + return false; + } +} + +bool TestMI::applySettings(const TestMISettings& settings, bool force) +{ + QList reverseAPIKeys; + + if ((m_settings.m_autoCorrOptions != settings.m_autoCorrOptions) || force) + { + reverseAPIKeys.append("autoCorrOptions"); + + switch(settings.m_autoCorrOptions) + { + case TestMISettings::AutoCorrDC: + m_deviceAPI->configureCorrections(true, false); + break; + case TestMISettings::AutoCorrDCAndIQ: + m_deviceAPI->configureCorrections(true, true); + break; + case TestMISettings::AutoCorrNone: + default: + m_deviceAPI->configureCorrections(false, false); + break; + } + } + + if ((m_settings.m_sampleRate != settings.m_sampleRate) || force) + { + reverseAPIKeys.append("sampleRate"); + + if (m_testSourceThread != 0) + { + m_testSourceThread->setSamplerate(settings.m_sampleRate); + qDebug("TestMI::applySettings: sample rate set to %d", settings.m_sampleRate); + } + } + + if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) + { + reverseAPIKeys.append("log2Decim"); + + if (m_testSourceThread != 0) + { + m_testSourceThread->setLog2Decimation(settings.m_log2Decim); + qDebug() << "TestMI::applySettings: set decimation to " << (1<setFcPos((int) settings.m_fcPos); + m_testSourceThread->setFrequencyShift(frequencyShift); + qDebug() << "TestMI::applySettings:" + << " center freq: " << settings.m_centerFrequency << " Hz" + << " device center freq: " << deviceCenterFrequency << " Hz" + << " device sample rate: " << devSampleRate << "Hz" + << " Actual sample rate: " << devSampleRate/(1<setAmplitudeBits(settings.m_amplitudeBits); + } + } + + if ((m_settings.m_dcFactor != settings.m_dcFactor) || force) + { + reverseAPIKeys.append("dcFactor"); + + if (m_testSourceThread != 0) { + m_testSourceThread->setDCFactor(settings.m_dcFactor); + } + } + + if ((m_settings.m_iFactor != settings.m_iFactor) || force) + { + reverseAPIKeys.append("iFactor"); + + if (m_testSourceThread != 0) { + m_testSourceThread->setIFactor(settings.m_iFactor); + } + } + + if ((m_settings.m_qFactor != settings.m_qFactor) || force) + { + reverseAPIKeys.append("qFactor"); + + if (m_testSourceThread != 0) { + m_testSourceThread->setQFactor(settings.m_qFactor); + } + } + + if ((m_settings.m_phaseImbalance != settings.m_phaseImbalance) || force) + { + reverseAPIKeys.append("phaseImbalance"); + + if (m_testSourceThread != 0) { + m_testSourceThread->setPhaseImbalance(settings.m_phaseImbalance); + } + } + + if ((m_settings.m_sampleSizeIndex != settings.m_sampleSizeIndex) || force) + { + reverseAPIKeys.append("sampleSizeIndex"); + + if (m_testSourceThread != 0) { + m_testSourceThread->setBitSize(settings.m_sampleSizeIndex); + } + } + + if ((m_settings.m_sampleRate != settings.m_sampleRate) + || (m_settings.m_centerFrequency != settings.m_centerFrequency) + || (m_settings.m_log2Decim != settings.m_log2Decim) + || (m_settings.m_fcPos != settings.m_fcPos) || force) + { + int sampleRate = settings.m_sampleRate/(1<handleMessage(*notif); // forward to file sink + DSPDeviceMIMOEngine::SignalNotification *engineNotif = new DSPDeviceMIMOEngine::SignalNotification( + sampleRate, settings.m_centerFrequency, true, 0); + m_deviceAPI->getDeviceEngineInputMessageQueue()->push(engineNotif); + } + + if ((m_settings.m_modulationTone != settings.m_modulationTone) || force) + { + reverseAPIKeys.append("modulationTone"); + + if (m_testSourceThread != 0) { + m_testSourceThread->setToneFrequency(settings.m_modulationTone * 10); + } + } + + if ((m_settings.m_modulation != settings.m_modulation) || force) + { + reverseAPIKeys.append("modulation"); + + if (m_testSourceThread != 0) + { + m_testSourceThread->setModulation(settings.m_modulation); + + if (settings.m_modulation == TestMISettings::ModulationPattern0) { + m_testSourceThread->setPattern0(); + } else if (settings.m_modulation == TestMISettings::ModulationPattern1) { + m_testSourceThread->setPattern1(); + } else if (settings.m_modulation == TestMISettings::ModulationPattern2) { + m_testSourceThread->setPattern2(); + } + } + } + + if ((m_settings.m_amModulation != settings.m_amModulation) || force) + { + reverseAPIKeys.append("amModulation"); + + if (m_testSourceThread != 0) { + m_testSourceThread->setAMModulation(settings.m_amModulation / 100.0f); + } + } + + if ((m_settings.m_fmDeviation != settings.m_fmDeviation) || force) + { + reverseAPIKeys.append("fmDeviation"); + + if (m_testSourceThread != 0) { + m_testSourceThread->setFMDeviation(settings.m_fmDeviation * 100.0f); + } + } + + if (settings.m_useReverseAPI) + { + qDebug("TestMI::applySettings: call webapiReverseSendSettings"); + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; + return true; +} + +int TestMI::webapiRunGet( + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + (void) errorMessage; + m_deviceAPI->getDeviceEngineStateStr(*response.getState()); + return 200; +} + +int TestMI::webapiRun( + bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + (void) errorMessage; + m_deviceAPI->getDeviceEngineStateStr(*response.getState()); + MsgStartStop *message = MsgStartStop::create(run); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgStartStop *msgToGUI = MsgStartStop::create(run); + m_guiMessageQueue->push(msgToGUI); + } + + return 200; +} + +int TestMI::webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setTestMiSettings(new SWGSDRangel::SWGTestMISettings()); + response.getTestMiSettings()->init(); + webapiFormatDeviceSettings(response, m_settings); + return 200; +} + +int TestMI::webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage) +{ + (void) errorMessage; + TestMISettings settings = m_settings; + + if (deviceSettingsKeys.contains("centerFrequency")) { + settings.m_centerFrequency = response.getTestMiSettings()->getCenterFrequency(); + } + if (deviceSettingsKeys.contains("frequencyShift")) { + settings.m_frequencyShift = response.getTestMiSettings()->getFrequencyShift(); + } + if (deviceSettingsKeys.contains("sampleRate")) { + settings.m_sampleRate = response.getTestMiSettings()->getSampleRate(); + } + if (deviceSettingsKeys.contains("log2Decim")) { + settings.m_log2Decim = response.getTestMiSettings()->getLog2Decim(); + } + if (deviceSettingsKeys.contains("fcPos")) { + int fcPos = response.getTestMiSettings()->getFcPos(); + fcPos = fcPos < 0 ? 0 : fcPos > 2 ? 2 : fcPos; + settings.m_fcPos = (TestMISettings::fcPos_t) fcPos; + } + if (deviceSettingsKeys.contains("sampleSizeIndex")) { + int sampleSizeIndex = response.getTestMiSettings()->getSampleSizeIndex(); + sampleSizeIndex = sampleSizeIndex < 0 ? 0 : sampleSizeIndex > 1 ? 2 : sampleSizeIndex; + settings.m_sampleSizeIndex = sampleSizeIndex; + } + if (deviceSettingsKeys.contains("amplitudeBits")) { + settings.m_amplitudeBits = response.getTestMiSettings()->getAmplitudeBits(); + } + if (deviceSettingsKeys.contains("autoCorrOptions")) { + int autoCorrOptions = response.getTestMiSettings()->getAutoCorrOptions(); + autoCorrOptions = autoCorrOptions < 0 ? 0 : autoCorrOptions >= TestMISettings::AutoCorrLast ? TestMISettings::AutoCorrLast-1 : autoCorrOptions; + settings.m_sampleSizeIndex = (TestMISettings::AutoCorrOptions) autoCorrOptions; + } + if (deviceSettingsKeys.contains("modulation")) { + int modulation = response.getTestMiSettings()->getModulation(); + modulation = modulation < 0 ? 0 : modulation >= TestMISettings::ModulationLast ? TestMISettings::ModulationLast-1 : modulation; + settings.m_modulation = (TestMISettings::Modulation) modulation; + } + if (deviceSettingsKeys.contains("modulationTone")) { + settings.m_modulationTone = response.getTestMiSettings()->getModulationTone(); + } + if (deviceSettingsKeys.contains("amModulation")) { + settings.m_amModulation = response.getTestMiSettings()->getAmModulation(); + }; + if (deviceSettingsKeys.contains("fmDeviation")) { + settings.m_fmDeviation = response.getTestMiSettings()->getFmDeviation(); + }; + if (deviceSettingsKeys.contains("dcFactor")) { + settings.m_dcFactor = response.getTestMiSettings()->getDcFactor(); + }; + if (deviceSettingsKeys.contains("iFactor")) { + settings.m_iFactor = response.getTestMiSettings()->getIFactor(); + }; + if (deviceSettingsKeys.contains("qFactor")) { + settings.m_qFactor = response.getTestMiSettings()->getQFactor(); + }; + if (deviceSettingsKeys.contains("phaseImbalance")) { + settings.m_phaseImbalance = response.getTestMiSettings()->getPhaseImbalance(); + }; + if (deviceSettingsKeys.contains("fileRecordName")) { + settings.m_fileRecordName = *response.getTestMiSettings()->getFileRecordName(); + } + if (deviceSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getTestMiSettings()->getUseReverseApi() != 0; + } + if (deviceSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getTestMiSettings()->getReverseApiAddress(); + } + if (deviceSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getTestMiSettings()->getReverseApiPort(); + } + if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getTestMiSettings()->getReverseApiDeviceIndex(); + } + + MsgConfigureTestSource *msg = MsgConfigureTestSource::create(settings, force); + m_inputMessageQueue.push(msg); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureTestSource *msgToGUI = MsgConfigureTestSource::create(settings, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatDeviceSettings(response, settings); + return 200; +} + +void TestMI::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const TestMISettings& settings) +{ + response.getTestMiSettings()->setCenterFrequency(settings.m_centerFrequency); + response.getTestMiSettings()->setFrequencyShift(settings.m_frequencyShift); + response.getTestMiSettings()->setSampleRate(settings.m_sampleRate); + response.getTestMiSettings()->setLog2Decim(settings.m_log2Decim); + response.getTestMiSettings()->setFcPos((int) settings.m_fcPos); + response.getTestMiSettings()->setSampleSizeIndex((int) settings.m_sampleSizeIndex); + response.getTestMiSettings()->setAmplitudeBits(settings.m_amplitudeBits); + response.getTestMiSettings()->setAutoCorrOptions((int) settings.m_autoCorrOptions); + response.getTestMiSettings()->setModulation((int) settings.m_modulation); + response.getTestMiSettings()->setModulationTone(settings.m_modulationTone); + response.getTestMiSettings()->setAmModulation(settings.m_amModulation); + response.getTestMiSettings()->setFmDeviation(settings.m_fmDeviation); + response.getTestMiSettings()->setDcFactor(settings.m_dcFactor); + response.getTestMiSettings()->setIFactor(settings.m_iFactor); + response.getTestMiSettings()->setQFactor(settings.m_qFactor); + response.getTestMiSettings()->setPhaseImbalance(settings.m_phaseImbalance); + + if (response.getTestMiSettings()->getFileRecordName()) { + *response.getTestMiSettings()->getFileRecordName() = settings.m_fileRecordName; + } else { + response.getTestMiSettings()->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + response.getTestMiSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getTestMiSettings()->getReverseApiAddress()) { + *response.getTestMiSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getTestMiSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getTestMiSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getTestMiSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); +} + +void TestMI::webapiReverseSendSettings(QList& deviceSettingsKeys, const TestMISettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setDirection(0); // single Rx + swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex()); + swgDeviceSettings->setDeviceHwType(new QString("TestSource")); + swgDeviceSettings->setTestMiSettings(new SWGSDRangel::SWGTestMISettings()); + SWGSDRangel::SWGTestMISettings *swgTestMISettings = swgDeviceSettings->getTestMiSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgTestMISettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("frequencyShift") || force) { + swgTestMISettings->setFrequencyShift(settings.m_frequencyShift); + } + if (deviceSettingsKeys.contains("sampleRate") || force) { + swgTestMISettings->setSampleRate(settings.m_sampleRate); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgTestMISettings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("fcPos") || force) { + swgTestMISettings->setFcPos((int) settings.m_fcPos); + } + if (deviceSettingsKeys.contains("sampleSizeIndex") || force) { + swgTestMISettings->setSampleSizeIndex(settings.m_sampleSizeIndex); + } + if (deviceSettingsKeys.contains("amplitudeBits") || force) { + swgTestMISettings->setAmplitudeBits(settings.m_amplitudeBits); + } + if (deviceSettingsKeys.contains("autoCorrOptions") || force) { + swgTestMISettings->setAutoCorrOptions((int) settings.m_sampleSizeIndex); + } + if (deviceSettingsKeys.contains("modulation") || force) { + swgTestMISettings->setModulation((int) settings.m_modulation); + } + if (deviceSettingsKeys.contains("modulationTone")) { + swgTestMISettings->setModulationTone(settings.m_modulationTone); + } + if (deviceSettingsKeys.contains("amModulation") || force) { + swgTestMISettings->setAmModulation(settings.m_amModulation); + }; + if (deviceSettingsKeys.contains("fmDeviation") || force) { + swgTestMISettings->setFmDeviation(settings.m_fmDeviation); + }; + if (deviceSettingsKeys.contains("dcFactor") || force) { + swgTestMISettings->setDcFactor(settings.m_dcFactor); + }; + if (deviceSettingsKeys.contains("iFactor") || force) { + swgTestMISettings->setIFactor(settings.m_iFactor); + }; + if (deviceSettingsKeys.contains("qFactor") || force) { + swgTestMISettings->setQFactor(settings.m_qFactor); + }; + if (deviceSettingsKeys.contains("phaseImbalance") || force) { + swgTestMISettings->setPhaseImbalance(settings.m_phaseImbalance); + }; + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgTestMISettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); +// qDebug("TestMI::webapiReverseSendSettings: %s", channelSettingsURL.toStdString().c_str()); +// qDebug("TestMI::webapiReverseSendSettings: query:\n%s", swgDeviceSettings->asJson().toStdString().c_str()); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void TestMI::webapiReverseSendStartStop(bool start) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setDirection(0); // single Rx + swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex()); + swgDeviceSettings->setDeviceHwType(new QString("TestSource")); + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer); + } +} + +void TestMI::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "TestMI::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("TestMI::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplemimo/testmi/testmi.h b/plugins/samplemimo/testmi/testmi.h new file mode 100644 index 000000000..be13a535b --- /dev/null +++ b/plugins/samplemimo/testmi/testmi.h @@ -0,0 +1,165 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef _TESTMI_TESTMI_H_ +#define _TESTMI_TESTMI_H_ + +#include +#include +#include +#include + +#include "dsp/devicesamplemimo.h" +#include "testmisettings.h" + +class DeviceAPI; +class TestMIThread; +class FileRecord; +class QNetworkAccessManager; +class QNetworkReply; + +class TestMI : public DeviceSampleMIMO { + Q_OBJECT +public: + class MsgConfigureTestSource : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const TestMISettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureTestSource* create(const TestMISettings& settings, bool force) + { + return new MsgConfigureTestSource(settings, force); + } + + private: + TestMISettings m_settings; + bool m_force; + + MsgConfigureTestSource(const TestMISettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgFileRecord : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getStartStop() const { return m_startStop; } + + static MsgFileRecord* create(bool startStop) { + return new MsgFileRecord(startStop); + } + + protected: + bool m_startStop; + + MsgFileRecord(bool startStop) : + Message(), + m_startStop(startStop) + { } + }; + + class MsgStartStop : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getStartStop() const { return m_startStop; } + + static MsgStartStop* create(bool startStop) { + return new MsgStartStop(startStop); + } + + protected: + bool m_startStop; + + MsgStartStop(bool startStop) : + Message(), + m_startStop(startStop) + { } + }; + + TestMI(DeviceAPI *deviceAPI); + virtual ~TestMI(); + virtual void destroy(); + + virtual void init(); + virtual bool start(); + virtual void stop(); + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; } + virtual const QString& getDeviceDescription() const; + + virtual int getSourceSampleRate(int index) const; + virtual void setSourceSampleRate(int sampleRate, int index) { (void) sampleRate; (void) index; } + virtual quint64 getSourceCenterFrequency(int index) const; + virtual void setSourceCenterFrequency(qint64 centerFrequency, int index); + + virtual int getSinkSampleRate(int index) const { return 0; (void) index; } + virtual void setSinkSampleRate(int sampleRate, int index) { (void) sampleRate; (void) index; } + virtual quint64 getSinkCenterFrequency(int index) const { return 0; (void) index; } + virtual void setSinkCenterFrequency(qint64 centerFrequency, int index) { (void) centerFrequency; (void) index; } + + virtual bool handleMessage(const Message& message); + + virtual int webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage); + + virtual int webapiRunGet( + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + virtual int webapiRun( + bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + +private: + DeviceAPI *m_deviceAPI; + FileRecord *m_fileSink; //!< File sink to record device I/Q output + QMutex m_mutex; + TestMISettings m_settings; + TestMIThread* m_testSourceThread; + QString m_deviceDescription; + bool m_running; + const QTimer& m_masterTimer; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + bool applySettings(const TestMISettings& settings, bool force); + void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const TestMISettings& settings); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const TestMISettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); +}; + +#endif // _TESTMI_TESTMI_H_ diff --git a/plugins/samplemimo/testmi/testmigui.cpp b/plugins/samplemimo/testmi/testmigui.cpp new file mode 100644 index 000000000..fe9ece930 --- /dev/null +++ b/plugins/samplemimo/testmi/testmigui.cpp @@ -0,0 +1,540 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include +#include +#include +#include + +#include "plugin/pluginapi.h" +#include "device/deviceapi.h" +#include "device/deviceuiset.h" +#include "gui/colormapper.h" +#include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" +#include "dsp/dspengine.h" +#include "dsp/dspdevicemimoengine.h" +#include "dsp/dspcommands.h" +#include "util/db.h" + +#include "mainwindow.h" + +#include "ui_testmigui.h" +#include "testmigui.h" + +TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : + QWidget(parent), + ui(new Ui::TestMIGui), + m_deviceUISet(deviceUISet), + m_settings(), + m_doApplySettings(true), + m_forceSettings(true), + m_sampleMIMO(nullptr), + m_tickCount(0), + m_lastEngineState(DeviceAPI::StNotStarted) +{ + qDebug("TestMIGui::TestMIGui"); + m_sampleMIMO = m_deviceUISet->m_deviceAPI->getSampleMIMO(); + + ui->setupUi(this); + ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->centerFrequency->setValueRange(7, 0, 9999999); + ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); + ui->sampleRate->setValueRange(7, 48000, 9999999); + ui->frequencyShift->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->frequencyShift->setValueRange(false, 7, -9999999, 9999999); + ui->frequencyShiftLabel->setText(QString("%1").arg(QChar(0x94, 0x03))); + + displaySettings(); + + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); + connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); + m_statusTimer.start(500); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); + m_sampleMIMO->setMessageQueueToGUI(&m_inputMessageQueue); + + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); +} + +TestMIGui::~TestMIGui() +{ + delete ui; +} + +void TestMIGui::destroy() +{ + delete this; +} + +void TestMIGui::setName(const QString& name) +{ + setObjectName(name); +} + +QString TestMIGui::getName() const +{ + return objectName(); +} + +void TestMIGui::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + sendSettings(); +} + +qint64 TestMIGui::getCenterFrequency() const +{ + return m_settings.m_centerFrequency; +} + +void TestMIGui::setCenterFrequency(qint64 centerFrequency) +{ + m_settings.m_centerFrequency = centerFrequency; + displaySettings(); + sendSettings(); +} + +QByteArray TestMIGui::serialize() const +{ + return m_settings.serialize(); +} + +bool TestMIGui::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + m_forceSettings = true; + sendSettings(); + return true; + } else { + resetToDefaults(); + return false; + } +} + +void TestMIGui::on_startStop_toggled(bool checked) +{ + if (m_doApplySettings) + { + TestMI::MsgStartStop *message = TestMI::MsgStartStop::create(checked); + m_sampleMIMO->getInputMessageQueue()->push(message); + } +} + +void TestMIGui::on_centerFrequency_changed(quint64 value) +{ + m_settings.m_centerFrequency = value * 1000; + sendSettings(); +} + +void TestMIGui::on_autoCorr_currentIndexChanged(int index) +{ + if ((index < 0) || (index > TestMISettings::AutoCorrLast)) { + return; + } + + m_settings.m_autoCorrOptions = (TestMISettings::AutoCorrOptions) index; + sendSettings(); +} + +void TestMIGui::on_frequencyShift_changed(qint64 value) +{ + m_settings.m_frequencyShift = value; + sendSettings(); +} + +void TestMIGui::on_decimation_currentIndexChanged(int index) +{ + if ((index < 0) || (index > 6)) { + return; + } + + m_settings.m_log2Decim = index; + sendSettings(); +} + +void TestMIGui::on_fcPos_currentIndexChanged(int index) +{ + if ((index < 0) || (index > 2)) { + return; + } + + m_settings.m_fcPos = (TestMISettings::fcPos_t) index; + sendSettings(); +} + +void TestMIGui::on_sampleRate_changed(quint64 value) +{ + updateFrequencyShiftLimit(); + m_settings.m_frequencyShift = ui->frequencyShift->getValueNew(); + m_settings.m_sampleRate = value; + sendSettings(); +} + +void TestMIGui::on_sampleSize_currentIndexChanged(int index) +{ + if ((index < 0) || (index > 2)) { + return; + } + + updateAmpCoarseLimit(); + updateAmpFineLimit(); + displayAmplitude(); + m_settings.m_amplitudeBits = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value(); + m_settings.m_sampleSizeIndex = index; + sendSettings(); +} + +void TestMIGui::on_amplitudeCoarse_valueChanged(int value) +{ + (void) value; + updateAmpFineLimit(); + displayAmplitude(); + m_settings.m_amplitudeBits = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value(); + sendSettings(); +} + +void TestMIGui::on_amplitudeFine_valueChanged(int value) +{ + (void) value; + displayAmplitude(); + m_settings.m_amplitudeBits = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value(); + sendSettings(); +} + +void TestMIGui::on_modulation_currentIndexChanged(int index) +{ + if ((index < 0) || (index > TestMISettings::ModulationLast)) { + return; + } + + m_settings.m_modulation = (TestMISettings::Modulation) index; + sendSettings(); +} + +void TestMIGui::on_modulationFrequency_valueChanged(int value) +{ + m_settings.m_modulationTone = value; + ui->modulationFrequencyText->setText(QString("%1").arg(m_settings.m_modulationTone / 100.0, 0, 'f', 2)); + sendSettings(); +} + +void TestMIGui::on_amModulation_valueChanged(int value) +{ + m_settings.m_amModulation = value; + ui->amModulationText->setText(QString("%1").arg(m_settings.m_amModulation)); + sendSettings(); +} + +void TestMIGui::on_fmDeviation_valueChanged(int value) +{ + m_settings.m_fmDeviation = value; + ui->fmDeviationText->setText(QString("%1").arg(m_settings.m_fmDeviation / 10.0, 0, 'f', 1)); + sendSettings(); +} + +void TestMIGui::on_dcBias_valueChanged(int value) +{ + ui->dcBiasText->setText(QString(tr("%1 %").arg(value))); + m_settings.m_dcFactor = value / 100.0f; + sendSettings(); +} + +void TestMIGui::on_iBias_valueChanged(int value) +{ + ui->iBiasText->setText(QString(tr("%1 %").arg(value))); + m_settings.m_iFactor = value / 100.0f; + sendSettings(); +} + +void TestMIGui::on_qBias_valueChanged(int value) +{ + ui->qBiasText->setText(QString(tr("%1 %").arg(value))); + m_settings.m_qFactor = value / 100.0f; + sendSettings(); +} + +void TestMIGui::on_phaseImbalance_valueChanged(int value) +{ + ui->phaseImbalanceText->setText(QString(tr("%1 %").arg(value))); + m_settings.m_phaseImbalance = value / 100.0f; + sendSettings(); +} + +void TestMIGui::on_record_toggled(bool checked) +{ + if (checked) { + ui->record->setStyleSheet("QToolButton { background-color : red; }"); + } else { + ui->record->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + + TestMI::MsgFileRecord* message = TestMI::MsgFileRecord::create(checked); + m_sampleMIMO->getInputMessageQueue()->push(message); +} + +void TestMIGui::displayAmplitude() +{ + int amplitudeInt = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value(); + double power; + + switch (ui->sampleSize->currentIndex()) + { + case 0: // 8 bits: 128 + power = (double) amplitudeInt*amplitudeInt / (double) (1<<14); + break; + case 1: // 12 bits 2048 + power = (double) amplitudeInt*amplitudeInt / (double) (1<<22); + break; + case 2: // 16 bits 32768 + default: + power = (double) amplitudeInt*amplitudeInt / (double) (1<<30); + break; + } + + ui->amplitudeBits->setText(QString(tr("%1 b").arg(amplitudeInt))); + double powerDb = CalcDb::dbPower(power); + ui->power->setText(QString(tr("%1 dB").arg(QString::number(powerDb, 'f', 1)))); +} + +void TestMIGui::updateAmpCoarseLimit() +{ + switch (ui->sampleSize->currentIndex()) + { + case 0: // 8 bits: 128 + ui->amplitudeCoarse->setMaximum(1); + break; + case 1: // 12 bits 2048 + ui->amplitudeCoarse->setMaximum(20); + break; + case 2: // 16 bits 32768 + default: + ui->amplitudeCoarse->setMaximum(327); + break; + } +} + +void TestMIGui::updateAmpFineLimit() +{ + switch (ui->sampleSize->currentIndex()) + { + case 0: // 8 bits: 128 + if (ui->amplitudeCoarse->value() == 1) { + ui->amplitudeFine->setMaximum(27); + } else { + ui->amplitudeFine->setMaximum(99); + } + break; + case 1: // 12 bits 2048 + if (ui->amplitudeCoarse->value() == 20) { + ui->amplitudeFine->setMaximum(47); + } else { + ui->amplitudeFine->setMaximum(99); + } + break; + case 2: // 16 bits 32768 + default: + if (ui->amplitudeCoarse->value() == 327) { + ui->amplitudeFine->setMaximum(67); + } else { + ui->amplitudeFine->setMaximum(99); + } + break; + } +} + +void TestMIGui::updateFrequencyShiftLimit() +{ + int sampleRate = ui->sampleRate->getValueNew(); + ui->frequencyShift->setValueRange(false, 7, -sampleRate, sampleRate); +} + +void TestMIGui::displaySettings() +{ + blockApplySettings(true); + ui->sampleSize->blockSignals(true); + + ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000); + ui->decimation->setCurrentIndex(m_settings.m_log2Decim); + ui->fcPos->setCurrentIndex((int) m_settings.m_fcPos); + ui->sampleRate->setValue(m_settings.m_sampleRate); + updateFrequencyShiftLimit(); + ui->frequencyShift->setValue(m_settings.m_frequencyShift); + ui->sampleSize->setCurrentIndex(m_settings.m_sampleSizeIndex); + updateAmpCoarseLimit(); + int amplitudeBits = m_settings.m_amplitudeBits; + ui->amplitudeCoarse->setValue(amplitudeBits/100); + updateAmpFineLimit(); + ui->amplitudeFine->setValue(amplitudeBits%100); + displayAmplitude(); + int dcBiasPercent = roundf(m_settings.m_dcFactor * 100.0f); + ui->dcBias->setValue((int) dcBiasPercent); + ui->dcBiasText->setText(QString(tr("%1 %").arg(dcBiasPercent))); + int iBiasPercent = roundf(m_settings.m_iFactor * 100.0f); + ui->iBias->setValue((int) iBiasPercent); + ui->iBiasText->setText(QString(tr("%1 %").arg(iBiasPercent))); + int qBiasPercent = roundf(m_settings.m_qFactor * 100.0f); + ui->qBias->setValue((int) qBiasPercent); + ui->qBiasText->setText(QString(tr("%1 %").arg(qBiasPercent))); + int phaseImbalancePercent = roundf(m_settings.m_phaseImbalance * 100.0f); + ui->phaseImbalance->setValue((int) phaseImbalancePercent); + ui->phaseImbalanceText->setText(QString(tr("%1 %").arg(phaseImbalancePercent))); + ui->autoCorr->setCurrentIndex(m_settings.m_autoCorrOptions); + ui->sampleSize->blockSignals(false); + ui->modulation->setCurrentIndex((int) m_settings.m_modulation); + ui->modulationFrequency->setValue(m_settings.m_modulationTone); + ui->modulationFrequencyText->setText(QString("%1").arg(m_settings.m_modulationTone / 100.0, 0, 'f', 2)); + ui->amModulation->setValue(m_settings.m_amModulation); + ui->amModulationText->setText(QString("%1").arg(m_settings.m_amModulation)); + ui->fmDeviation->setValue(m_settings.m_fmDeviation); + ui->fmDeviationText->setText(QString("%1").arg(m_settings.m_fmDeviation / 10.0, 0, 'f', 1)); + blockApplySettings(false); +} + +void TestMIGui::sendSettings() +{ + if(!m_updateTimer.isActive()) { + m_updateTimer.start(100); + } +} + +void TestMIGui::updateHardware() +{ + if (m_doApplySettings) + { + TestMI::MsgConfigureTestSource* message = TestMI::MsgConfigureTestSource::create(m_settings, m_forceSettings); + m_sampleMIMO->getInputMessageQueue()->push(message); + m_forceSettings = false; + m_updateTimer.stop(); + } +} + +void TestMIGui::updateStatus() +{ + int state = m_deviceUISet->m_deviceAPI->state(); + + if(m_lastEngineState != state) + { + switch(state) + { + case DeviceAPI::StNotStarted: + ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + break; + case DeviceAPI::StIdle: + ui->startStop->setStyleSheet("QToolButton { background-color : blue; }"); + break; + case DeviceAPI::StRunning: + ui->startStop->setStyleSheet("QToolButton { background-color : green; }"); + break; + case DeviceAPI::StError: + ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage()); + break; + default: + break; + } + + m_lastEngineState = state; + } +} + +bool TestMIGui::handleMessage(const Message& message) +{ + if (TestMI::MsgConfigureTestSource::match(message)) + { + qDebug("TestMIGui::handleMessage: MsgConfigureTestSource"); + const TestMI::MsgConfigureTestSource& cfg = (TestMI::MsgConfigureTestSource&) message; + m_settings = cfg.getSettings(); + displaySettings(); + return true; + } + else if (TestMI::MsgStartStop::match(message)) + { + qDebug("TestMIGui::handleMessage: MsgStartStop"); + TestMI::MsgStartStop& notif = (TestMI::MsgStartStop&) message; + blockApplySettings(true); + ui->startStop->setChecked(notif.getStartStop()); + blockApplySettings(false); + + return true; + } + else + { + return false; + } +} + +void TestMIGui::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != 0) + { + if (DSPDeviceMIMOEngine::SignalNotification::match(*message)) + { + DSPDeviceMIMOEngine::SignalNotification* notif = (DSPDeviceMIMOEngine::SignalNotification*) message; + m_deviceSampleRate = notif->getSampleRate(); + m_deviceCenterFrequency = notif->getCenterFrequency(); + // Do not consider multiple sources at this time + qDebug("TestMIGui::handleInputMessages: DSPDeviceMIMOEngine::SignalNotification: SampleRate:%d, CenterFrequency:%llu", + notif->getSampleRate(), + notif->getCenterFrequency()); + updateSampleRateAndFrequency(); + + delete message; + } + else + { + if (handleMessage(*message)) + { + delete message; + } + } + } +} + +void TestMIGui::updateSampleRateAndFrequency() +{ + m_deviceUISet->getSpectrum()->setSampleRate(m_deviceSampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + ui->deviceRateText->setText(tr("%1k").arg((float)m_deviceSampleRate / 1000)); +} + +void TestMIGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} \ No newline at end of file diff --git a/plugins/samplemimo/testmi/testmigui.h b/plugins/samplemimo/testmi/testmigui.h new file mode 100644 index 000000000..95d6b8a64 --- /dev/null +++ b/plugins/samplemimo/testmi/testmigui.h @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef _TESTMI_TESTMIGUI_H_ +#define _TESTMI_TESTMIGUI_H_ + +#include +#include +#include + +#include "util/messagequeue.h" + +#include "testmisettings.h" +#include "testmi.h" + +class DeviceUISet; + +namespace Ui { + class TestMIGui; +} + +class TestMIGui : public QWidget, public PluginInstanceGUI { + Q_OBJECT + +public: + explicit TestMIGui(DeviceUISet *deviceUISet, QWidget* parent = 0); + virtual ~TestMIGui(); + virtual void destroy(); + + void setName(const QString& name); + QString getName() const; + + void resetToDefaults(); + virtual qint64 getCenterFrequency() const; + virtual void setCenterFrequency(qint64 centerFrequency); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual bool handleMessage(const Message& message); + +private: + Ui::TestMIGui* ui; + + DeviceUISet* m_deviceUISet; + TestMISettings m_settings; + QTimer m_updateTimer; + QTimer m_statusTimer; + bool m_doApplySettings; + bool m_forceSettings; + DeviceSampleMIMO* m_sampleMIMO; + std::size_t m_tickCount; + int m_deviceSampleRate; + quint64 m_deviceCenterFrequency; //!< Center frequency in device + int m_lastEngineState; + MessageQueue m_inputMessageQueue; + + void blockApplySettings(bool block) { m_doApplySettings = !block; } + void displaySettings(); + void sendSettings(); + void updateSampleRateAndFrequency(); + void displayAmplitude(); + void updateAmpCoarseLimit(); + void updateAmpFineLimit(); + void updateFrequencyShiftLimit(); + +private slots: + void handleInputMessages(); + void on_startStop_toggled(bool checked); + void on_centerFrequency_changed(quint64 value); + void on_autoCorr_currentIndexChanged(int index); + void on_frequencyShift_changed(qint64 value); + void on_decimation_currentIndexChanged(int index); + void on_fcPos_currentIndexChanged(int index); + void on_sampleRate_changed(quint64 value); + void on_sampleSize_currentIndexChanged(int index); + void on_amplitudeCoarse_valueChanged(int value); + void on_amplitudeFine_valueChanged(int value); + void on_modulation_currentIndexChanged(int index); + void on_modulationFrequency_valueChanged(int value); + void on_amModulation_valueChanged(int value); + void on_fmDeviation_valueChanged(int value); + void on_dcBias_valueChanged(int value); + void on_iBias_valueChanged(int value); + void on_qBias_valueChanged(int value); + void on_phaseImbalance_valueChanged(int value); + void on_record_toggled(bool checked); + void openDeviceSettingsDialog(const QPoint& p); + void updateStatus(); + void updateHardware(); +}; + +#endif // _TESTMI_TESTMIGUI_H_ diff --git a/plugins/samplemimo/testmi/testmigui.ui b/plugins/samplemimo/testmi/testmigui.ui new file mode 100644 index 000000000..d03340371 --- /dev/null +++ b/plugins/samplemimo/testmi/testmigui.ui @@ -0,0 +1,1012 @@ + + + TestMIGui + + + + 0 + 0 + 360 + 300 + + + + + 0 + 0 + + + + + 360 + 300 + + + + + Liberation Sans + 9 + 50 + false + false + + + + Test Multiple Input + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 4 + + + + + + + + + start/stop acquisition + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + Toggle record I/Q samples from device + + + + + + + :/record_off.png:/record_off.png + + + + + + + + + + + + 58 + 0 + + + + I/Q sample rate kS/s + + + 0000.00k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 20 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Tuner center frequency in kHz + + + + + + + kHz + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + + + + + + Corr + + + + + + + DC offset and IQ correction options + + + + None + + + + + DC + + + + + DC+IQ + + + + + + + + Dec + + + + + + + + 45 + 16777215 + + + + Decimation factor + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + 32 + + + + + 64 + + + + + + + + Fp + + + + + + + + 50 + 16777215 + + + + Relative position of generator center frequency + + + 2 + + + + Inf + + + + + Sup + + + + + Cen + + + + + + + + Sz + + + + + + + + 45 + 16777215 + + + + Sample size + + + 0 + + + + 8 + + + + + 12 + + + + + 16 + + + + + + + + bits + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 2 + + + 2 + + + + + + 0 + 0 + + + + + 16 + 0 + + + + SR + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Generator sample rate (S/s) + + + + + + + S/s + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Mod + + + + + + + + 50 + 16777215 + + + + Modulation + + + + No + + + + + AM + + + + + FM + + + + + P0 + + + + + P1 + + + + + P2 + + + + + + + + + 22 + 22 + + + + Modulation tone (kHz) + + + 1 + + + 999 + + + 1 + + + + + + + + 35 + 0 + + + + Modulation tone value (kHz) + + + 0.00 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + 16 + 0 + + + + D + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Shift from center frequency + + + + + + + Hz + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + AM + + + + + + + + 22 + 22 + + + + AM modulation (%) + + + 1 + + + + + + + AM modulation value (%) + + + 00 + + + + + + + Qt::Vertical + + + + + + + FM + + + + + + + + 22 + 22 + + + + FM deviation (kHz) + + + 1 + + + 999 + + + 1 + + + + + + + + 35 + 0 + + + + FM deviation value (kHz) + + + 00.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + + + Amp fine + + + + + + + + 0 + 0 + + + + Amp coarse + + + + + + + true + + + Amplitude coarse (x100) + + + 327 + + + 1 + + + Qt::Horizontal + + + + + + + Amplitude in bits + + + 32768 b + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 52 + 0 + + + + Power + + + -100 dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Amplitude fine (x1) + + + 1 + + + Qt::Horizontal + + + + + + + + + + + -99 + + + 1 + + + Qt::Horizontal + + + + + + + -100 % + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Q bias + + + + + + + + + + + + + + + - + + + + + + + I bias + + + + + + + - + + + + + + + -99 + + + 1 + + + Qt::Horizontal + + + + + + + -100 % + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + DC bias + + + + + + + - + + + + + + + -99 + + + 1 + + + Qt::Horizontal + + + + + + + + + + + + + + + -100 % + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Phase + + + + + + + - + + + + + + + + + + + + + + + + 45 + 0 + + + + -100 % + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + -99 + + + 1 + + + Qt::Horizontal + + + + + + + + + + + + + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
+
+ + + + +
diff --git a/plugins/samplemimo/testmi/testmiplugin.cpp b/plugins/samplemimo/testmi/testmiplugin.cpp new file mode 100644 index 000000000..edb6628bd --- /dev/null +++ b/plugins/samplemimo/testmi/testmiplugin.cpp @@ -0,0 +1,111 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "plugin/pluginapi.h" +#include "util/simpleserializer.h" + +#ifdef SERVER_MODE +#include "testmi.h" +#else +#include "testmigui.h" +#endif +#include "testmiplugin.h" + +const PluginDescriptor TestMIPlugin::m_pluginDescriptor = { + QString("Test Multiple Input"), + QString("4.8.1"), + QString("(c) Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +const QString TestMIPlugin::m_hardwareID = "TestMI"; +const QString TestMIPlugin::m_deviceTypeID = TESTMI_DEVICE_TYPE_ID; + +TestMIPlugin::TestMIPlugin(QObject* parent) : + QObject(parent) +{ +} + +const PluginDescriptor& TestMIPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void TestMIPlugin::initPlugin(PluginAPI* pluginAPI) +{ + pluginAPI->registerSampleMIMO(m_deviceTypeID, this); +} + +PluginInterface::SamplingDevices TestMIPlugin::enumSampleMIMO() +{ + SamplingDevices result; + + result.append(SamplingDevice( + "TestMI", + m_hardwareID, + m_deviceTypeID, + QString::null, + 0, + PluginInterface::SamplingDevice::BuiltInDevice, + PluginInterface::SamplingDevice::StreamMIMO, + 1, + 0)); + + return result; +} + +#ifdef SERVER_MODE +PluginInstanceGUI* TestMIPlugin::createSampleMIMOPluginInstanceGUI( + const QString& sourceId __attribute((unused)), + QWidget **widget __attribute((unused)), + DeviceUISet *deviceUISet __attribute((unused))) +{ + return 0; +} +#else +PluginInstanceGUI* TestMIPlugin::createSampleMIMOPluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet) +{ + if (sourceId == m_deviceTypeID) { + TestMIGui* gui = new TestMIGui(deviceUISet); + *widget = gui; + return gui; + } else { + return nullptr; + } +} +#endif + +DeviceSampleMIMO *TestMIPlugin::createSampleMIMOPluginInstance(const QString& mimoId, DeviceAPI *deviceAPI) +{ + if (mimoId == m_deviceTypeID) + { + TestMI* input = new TestMI(deviceAPI); + return input; + } + else + { + return nullptr; + } +} + diff --git a/plugins/samplemimo/testmi/testmiplugin.h b/plugins/samplemimo/testmi/testmiplugin.h new file mode 100644 index 000000000..d002f39f2 --- /dev/null +++ b/plugins/samplemimo/testmi/testmiplugin.h @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef _TESTMI_TESTMIPLUGIN_H +#define _TESTMI_TESTMIPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class PluginAPI; + +#define TESTMI_DEVICE_TYPE_ID "sdrangel.samplemimo.testmi" + +class TestMIPlugin : public QObject, public PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID TESTMI_DEVICE_TYPE_ID) + +public: + explicit TestMIPlugin(QObject* parent = NULL); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual SamplingDevices enumSampleMIMO(); + virtual PluginInstanceGUI* createSampleMIMOPluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet); + virtual DeviceSampleMIMO* createSampleMIMOPluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); + + static const QString m_hardwareID; + static const QString m_deviceTypeID; + +private: + static const PluginDescriptor m_pluginDescriptor; +}; + +#endif // _TESTMI_TESTMIPLUGIN_H diff --git a/plugins/samplemimo/testmi/testmisettings.cpp b/plugins/samplemimo/testmi/testmisettings.cpp new file mode 100644 index 000000000..d36a858c2 --- /dev/null +++ b/plugins/samplemimo/testmi/testmisettings.cpp @@ -0,0 +1,150 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include "util/simpleserializer.h" +#include "testmisettings.h" + +TestMISettings::TestMISettings() +{ + resetToDefaults(); +} + +void TestMISettings::resetToDefaults() +{ + m_centerFrequency = 435000*1000; + m_frequencyShift = 0; + m_sampleRate = 768*1000; + m_log2Decim = 4; + m_fcPos = FC_POS_CENTER; + m_sampleSizeIndex = 0; + m_amplitudeBits = 127; + m_autoCorrOptions = AutoCorrNone; + m_modulation = ModulationNone; + m_modulationTone = 44; // 440 Hz + m_amModulation = 50; // 50% + m_fmDeviation = 50; // 5 kHz + m_dcFactor = 0.0f; + m_iFactor = 0.0f; + m_qFactor = 0.0f; + m_phaseImbalance = 0.0f; + m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; +} + +QByteArray TestMISettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeS32(2, m_frequencyShift); + s.writeU32(3, m_sampleRate); + s.writeU32(4, m_log2Decim); + s.writeS32(5, (int) m_fcPos); + s.writeU32(6, m_sampleSizeIndex); + s.writeS32(7, m_amplitudeBits); + s.writeS32(8, (int) m_autoCorrOptions); + s.writeFloat(10, m_dcFactor); + s.writeFloat(11, m_iFactor); + s.writeFloat(12, m_qFactor); + s.writeFloat(13, m_phaseImbalance); + s.writeS32(14, (int) m_modulation); + s.writeS32(15, m_modulationTone); + s.writeS32(16, m_amModulation); + s.writeS32(17, m_fmDeviation); + s.writeBool(18, m_useReverseAPI); + s.writeString(19, m_reverseAPIAddress); + s.writeU32(20, m_reverseAPIPort); + s.writeU32(21, m_reverseAPIDeviceIndex); + return s.final(); +} + +bool TestMISettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + int intval; + uint32_t utmp; + + d.readS32(2, &m_frequencyShift, 0); + d.readU32(3, &m_sampleRate, 768*1000); + d.readU32(4, &m_log2Decim, 4); + d.readS32(5, &intval, 0); + m_fcPos = (fcPos_t) intval; + d.readU32(6, &m_sampleSizeIndex, 0); + d.readS32(7, &m_amplitudeBits, 128); + d.readS32(8, &intval, 0); + + if (intval < 0 || intval > (int) AutoCorrLast) { + m_autoCorrOptions = AutoCorrNone; + } else { + m_autoCorrOptions = (AutoCorrOptions) intval; + } + + d.readFloat(10, &m_dcFactor, 0.0f); + d.readFloat(11, &m_iFactor, 0.0f); + d.readFloat(12, &m_qFactor, 0.0f); + d.readFloat(13, &m_phaseImbalance, 0.0f); + d.readS32(14, &intval, 0); + + if (intval < 0 || intval > (int) ModulationLast) { + m_modulation = ModulationNone; + } else { + m_modulation = (Modulation) intval; + } + + d.readS32(15, &m_modulationTone, 44); + d.readS32(16, &m_amModulation, 50); + d.readS32(17, &m_fmDeviation, 50); + + d.readBool(18, &m_useReverseAPI, false); + d.readString(19, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(20, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(21, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + + return true; + } + else + { + resetToDefaults(); + return false; + } +} + + + + + + diff --git a/plugins/samplemimo/testmi/testmisettings.h b/plugins/samplemimo/testmi/testmisettings.h new file mode 100644 index 000000000..9a5daa8be --- /dev/null +++ b/plugins/samplemimo/testmi/testmisettings.h @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef _TESTMI_TESTMISETTINGS_H_ +#define _TESTMI_TESTMISETTINGS_H_ + +#include + +struct TestMISettings { + typedef enum { + FC_POS_INFRA = 0, + FC_POS_SUPRA, + FC_POS_CENTER + } fcPos_t; + + typedef enum { + AutoCorrNone, + AutoCorrDC, + AutoCorrDCAndIQ, + AutoCorrLast, + } AutoCorrOptions; + + typedef enum { + ModulationNone, + ModulationAM, + ModulationFM, + ModulationPattern0, + ModulationPattern1, + ModulationPattern2, + ModulationLast + } Modulation; + + quint64 m_centerFrequency; + qint32 m_frequencyShift; + quint32 m_sampleRate; + quint32 m_log2Decim; + fcPos_t m_fcPos; + quint32 m_sampleSizeIndex; + qint32 m_amplitudeBits; + AutoCorrOptions m_autoCorrOptions; + Modulation m_modulation; + int m_modulationTone; //!< 10'Hz + int m_amModulation; //!< percent + int m_fmDeviation; //!< 100'Hz + float m_dcFactor; //!< -1.0 < x < 1.0 + float m_iFactor; //!< -1.0 < x < 1.0 + float m_qFactor; //!< -1.0 < x < 1.0 + float m_phaseImbalance; //!< -1.0 < x < 1.0 + QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + + TestMISettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + + + + + +#endif /* _TESTMI_TESTMISETTINGS_H_ */ diff --git a/plugins/samplemimo/testmi/testmithread.cpp b/plugins/samplemimo/testmi/testmithread.cpp new file mode 100644 index 000000000..02aea93ce --- /dev/null +++ b/plugins/samplemimo/testmi/testmithread.cpp @@ -0,0 +1,450 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#define _USE_MATH_DEFINES +#include +#include +#include + +#include "dsp/samplesinkfifo.h" + +#include "testmithread.h" + +#define TESTMI_BLOCKSIZE 16384 + +MESSAGE_CLASS_DEFINITION(TestMIThread::MsgStartStop, Message) + +TestMIThread::TestMIThread(SampleSinkFifo* sampleFifo, QObject* parent) : + QThread(parent), + m_running(false), + m_buf(0), + m_bufsize(0), + m_chunksize(0), + m_convertBuffer(TESTMI_BLOCKSIZE), + m_sampleFifo(sampleFifo), + m_frequencyShift(0), + m_toneFrequency(440), + m_modulation(TestMISettings::ModulationNone), + m_amModulation(0.5f), + m_fmDeviationUnit(0.0f), + m_fmPhasor(0.0f), + m_pulseWidth(150), + m_pulseSampleCount(0), + m_pulsePatternCount(0), + m_pulsePatternCycle(8), + m_pulsePatternPlaces(3), + m_samplerate(48000), + m_log2Decim(4), + m_fcPos(0), + m_bitSizeIndex(0), + m_bitShift(8), + m_amplitudeBits(127), + m_dcBias(0.0f), + m_iBias(0.0f), + m_qBias(0.0f), + m_phaseImbalance(0.0f), + m_amplitudeBitsDC(0), + m_amplitudeBitsI(127), + m_amplitudeBitsQ(127), + m_frequency(435*1000), + m_fcPosShift(0), + m_throttlems(TESTMI_THROTTLE_MS), + m_throttleToggle(false), + m_mutex(QMutex::Recursive) +{ + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); +} + +TestMIThread::~TestMIThread() +{ +} + +void TestMIThread::startWork() +{ + m_timer.setTimerType(Qt::PreciseTimer); + connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); + m_timer.start(50); + m_startWaitMutex.lock(); + m_elapsedTimer.start(); + start(); + while(!m_running) + m_startWaiter.wait(&m_startWaitMutex, 100); + m_startWaitMutex.unlock(); +} + +void TestMIThread::stopWork() +{ + m_running = false; + wait(); + m_timer.stop(); + disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); +} + +void TestMIThread::setSamplerate(int samplerate) +{ + QMutexLocker mutexLocker(&m_mutex); + + m_samplerate = samplerate; + m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000); + m_throttleToggle = !m_throttleToggle; + m_nco.setFreq(m_frequencyShift, m_samplerate); + m_toneNco.setFreq(m_toneFrequency, m_samplerate); +} + +void TestMIThread::setLog2Decimation(unsigned int log2_decim) +{ + m_log2Decim = log2_decim; +} + +void TestMIThread::setFcPos(int fcPos) +{ + m_fcPos = fcPos; +} + +void TestMIThread::setBitSize(quint32 bitSizeIndex) +{ + switch (bitSizeIndex) + { + case 0: + m_bitShift = 7; + m_bitSizeIndex = 0; + break; + case 1: + m_bitShift = 11; + m_bitSizeIndex = 1; + break; + case 2: + default: + m_bitShift = 15; + m_bitSizeIndex = 2; + break; + } +} + +void TestMIThread::setAmplitudeBits(int32_t amplitudeBits) +{ + m_amplitudeBits = amplitudeBits; + m_amplitudeBitsDC = m_dcBias * amplitudeBits; + m_amplitudeBitsI = (1.0f + m_iBias) * amplitudeBits; + m_amplitudeBitsQ = (1.0f + m_qBias) * amplitudeBits; +} + +void TestMIThread::setDCFactor(float dcFactor) +{ + m_dcBias = dcFactor; + m_amplitudeBitsDC = m_dcBias * m_amplitudeBits; +} + +void TestMIThread::setIFactor(float iFactor) +{ + m_iBias = iFactor; + m_amplitudeBitsI = (1.0f + m_iBias) * m_amplitudeBits; +} + +void TestMIThread::setQFactor(float iFactor) +{ + m_qBias = iFactor; + m_amplitudeBitsQ = (1.0f + m_qBias) * m_amplitudeBits; +} + +void TestMIThread::setPhaseImbalance(float phaseImbalance) +{ + m_phaseImbalance = phaseImbalance; +} + +void TestMIThread::setFrequencyShift(int shift) +{ + m_nco.setFreq(shift, m_samplerate); +} + +void TestMIThread::setToneFrequency(int toneFrequency) +{ + m_toneNco.setFreq(toneFrequency, m_samplerate); +} + +void TestMIThread::setModulation(TestMISettings::Modulation modulation) +{ + m_modulation = modulation; +} + +void TestMIThread::setAMModulation(float amModulation) +{ + m_amModulation = amModulation < 0.0f ? 0.0f : amModulation > 1.0f ? 1.0f : amModulation; +} + +void TestMIThread::setFMDeviation(float deviation) +{ + float fmDeviationUnit = deviation / (float) m_samplerate; + m_fmDeviationUnit = fmDeviationUnit < 0.0f ? 0.0f : fmDeviationUnit > 0.5f ? 0.5f : fmDeviationUnit; + qDebug("TestMIThread::setFMDeviation: m_fmDeviationUnit: %f", m_fmDeviationUnit); +} + +void TestMIThread::startStop(bool start) +{ + MsgStartStop *msg = MsgStartStop::create(start); + m_inputMessageQueue.push(msg); +} + +void TestMIThread::run() +{ + m_running = true; + m_startWaiter.wakeAll(); + + while (m_running) // actual work is in the tick() function + { + sleep(1); + } + + m_running = false; +} + +void TestMIThread::setBuffers(quint32 chunksize) +{ + if (chunksize > m_bufsize) + { + m_bufsize = chunksize; + + if (m_buf == 0) + { + qDebug() << "TestMIThread::setBuffer: Allocate buffer: " + << " size: " << m_bufsize << " bytes" + << " #samples: " << (m_bufsize/4); + m_buf = (qint16*) malloc(m_bufsize); + } + else + { + qDebug() << "TestMIThread::setBuffer: Re-allocate buffer: " + << " size: " << m_bufsize << " bytes" + << " #samples: " << (m_bufsize/4); + free(m_buf); + m_buf = (qint16*) malloc(m_bufsize); + } + + m_convertBuffer.resize(chunksize/4); + } +} + +void TestMIThread::generate(quint32 chunksize) +{ + int n = chunksize / 2; + setBuffers(chunksize); + + for (int i = 0; i < n-1;) + { + switch (m_modulation) + { + case TestMISettings::ModulationAM: + { + Complex c = m_nco.nextIQ(); + Real t, re, im; + pullAF(t); + t = (t*m_amModulation + 1.0f)*0.5f; + re = c.real()*t; + im = c.imag()*t + m_phaseImbalance*re; + m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC; + m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ); + } + break; + case TestMISettings::ModulationFM: + { + Complex c = m_nco.nextIQ(); + Real t, re, im; + pullAF(t); + m_fmPhasor += m_fmDeviationUnit * t; + m_fmPhasor = m_fmPhasor < -1.0f ? -m_fmPhasor - 1.0f : m_fmPhasor > 1.0f ? m_fmPhasor - 1.0f : m_fmPhasor; + re = c.real()*cos(m_fmPhasor*M_PI) - c.imag()*sin(m_fmPhasor*M_PI); + im = (c.real()*sin(m_fmPhasor*M_PI) + c.imag()*cos(m_fmPhasor*M_PI)) + m_phaseImbalance*re; + m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC; + m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ); + } + break; + case TestMISettings::ModulationPattern0: // binary pattern + { + if (m_pulseSampleCount < m_pulseWidth) // sync pattern: 0 + { + m_buf[i++] = m_amplitudeBitsDC; + m_buf[i++] = 0; + } + else if (m_pulseSampleCount < 2*m_pulseWidth) // sync pattern: 1 + { + m_buf[i++] = (int16_t) (m_amplitudeBitsI + m_amplitudeBitsDC); + m_buf[i++] = (int16_t) (m_phaseImbalance * (float) m_amplitudeBitsQ); + } + else if (m_pulseSampleCount < 3*m_pulseWidth) // sync pattern: 0 + { + m_buf[i++] = m_amplitudeBitsDC; + m_buf[i++] = 0; + } + else if (m_pulseSampleCount < (3+m_pulsePatternPlaces)*m_pulseWidth) // binary pattern + { + uint32_t patPulseSampleCount = m_pulseSampleCount - 3*m_pulseWidth; + uint32_t patPulseIndex = patPulseSampleCount / m_pulseWidth; + float patFigure = (m_pulsePatternCount & (1<write(m_convertBuffer.begin(), it); +} + +void TestMIThread::tick() +{ + if (m_running) + { + qint64 throttlems = m_elapsedTimer.restart(); + + if ((throttlems > 45) && (throttlems < 55) && (throttlems != m_throttlems)) + { + QMutexLocker mutexLocker(&m_mutex); + m_throttlems = throttlems; + m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000); + m_throttleToggle = !m_throttleToggle; + } + + generate(m_chunksize); + } +} + +void TestMIThread::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != 0) + { + if (MsgStartStop::match(*message)) + { + MsgStartStop* notif = (MsgStartStop*) message; + qDebug("TestMIThread::handleInputMessages: MsgStartStop: %s", notif->getStartStop() ? "start" : "stop"); + + if (notif->getStartStop()) { + startWork(); + } else { + stopWork(); + } + + delete message; + } + } +} + +void TestMIThread::setPattern0() +{ + m_pulseWidth = 150; + m_pulseSampleCount = 0; + m_pulsePatternCount = 0; + m_pulsePatternCycle = 8; + m_pulsePatternPlaces = 3; +} + +void TestMIThread::setPattern1() +{ + m_pulseWidth = 1000; + m_pulseSampleCount = 0; +} + +void TestMIThread::setPattern2() +{ + m_pulseWidth = 1000; + m_pulseSampleCount = 0; +} diff --git a/plugins/samplemimo/testmi/testmithread.h b/plugins/samplemimo/testmi/testmithread.h new file mode 100644 index 000000000..27f094c83 --- /dev/null +++ b/plugins/samplemimo/testmi/testmithread.h @@ -0,0 +1,385 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef _TESTMI_TESTMITHREAD_H_ +#define _TESTMI_TESTMITHREAD_H_ + +#include +#include +#include +#include +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "dsp/decimators.h" +#include "dsp/ncof.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "testmisettings.h" + +#define TESTMI_THROTTLE_MS 50 + +class TestMIThread : public QThread { + Q_OBJECT + +public: + class MsgStartStop : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getStartStop() const { return m_startStop; } + + static MsgStartStop* create(bool startStop) { + return new MsgStartStop(startStop); + } + + protected: + bool m_startStop; + + MsgStartStop(bool startStop) : + Message(), + m_startStop(startStop) + { } + }; + + TestMIThread(SampleSinkFifo* sampleFifo, QObject* parent = 0); + ~TestMIThread(); + + void startStop(bool start); + void setSamplerate(int samplerate); + void setLog2Decimation(unsigned int log2_decim); + void setFcPos(int fcPos); + void setBitSize(uint32_t bitSizeIndex); + void setAmplitudeBits(int32_t amplitudeBits); + void setDCFactor(float iFactor); + void setIFactor(float iFactor); + void setQFactor(float qFactor); + void setPhaseImbalance(float phaseImbalance); + void setFrequencyShift(int shift); + void setToneFrequency(int toneFrequency); + void setModulation(TestMISettings::Modulation modulation); + void setAMModulation(float amModulation); + void setFMDeviation(float deviation); + void setPattern0(); + void setPattern1(); + void setPattern2(); + +private: + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + volatile bool m_running; + + qint16 *m_buf; + quint32 m_bufsize; + quint32 m_chunksize; + SampleVector m_convertBuffer; + SampleSinkFifo* m_sampleFifo; + NCOF m_nco; + NCOF m_toneNco; + int m_frequencyShift; + int m_toneFrequency; + TestMISettings::Modulation m_modulation; + float m_amModulation; + float m_fmDeviationUnit; + float m_fmPhasor; + uint32_t m_pulseWidth; //!< pulse width in number of samples + uint32_t m_pulseSampleCount; + uint32_t m_pulsePatternCount; + uint32_t m_pulsePatternCycle; + uint32_t m_pulsePatternPlaces; + + int m_samplerate; + unsigned int m_log2Decim; + int m_fcPos; + uint32_t m_bitSizeIndex; + uint32_t m_bitShift; + int32_t m_amplitudeBits; + float m_dcBias; + float m_iBias; + float m_qBias; + float m_phaseImbalance; + int32_t m_amplitudeBitsDC; + int32_t m_amplitudeBitsI; + int32_t m_amplitudeBitsQ; + + uint64_t m_frequency; + int m_fcPosShift; + + int m_throttlems; + QTimer m_timer; + QElapsedTimer m_elapsedTimer; + bool m_throttleToggle; + QMutex m_mutex; + + MessageQueue m_inputMessageQueue; + + Decimators m_decimators_8; + Decimators m_decimators_12; + Decimators m_decimators_16; + + void startWork(); + void stopWork(); + void run(); + void callback(const qint16* buf, qint32 len); + void setBuffers(quint32 chunksize); + void generate(quint32 chunksize); + void pullAF(Real& afSample); + + // Decimate according to specified log2 (ex: log2=4 => decim=16) + inline void convert_8(SampleVector::iterator* it, const qint16* buf, qint32 len) + { + if (m_log2Decim == 0) { + m_decimators_8.decimate1(it, buf, len); + } else { + if (m_fcPos == 0) { // Infradyne + switch (m_log2Decim) { + case 1: + m_decimators_8.decimate2_inf(it, buf, len); + break; + case 2: + m_decimators_8.decimate4_inf(it, buf, len); + break; + case 3: + m_decimators_8.decimate8_inf(it, buf, len); + break; + case 4: + m_decimators_8.decimate16_inf(it, buf, len); + break; + case 5: + m_decimators_8.decimate32_inf(it, buf, len); + break; + case 6: + m_decimators_8.decimate64_inf(it, buf, len); + break; + default: + break; + } + } else if (m_fcPos == 1) {// Supradyne + switch (m_log2Decim) { + case 1: + m_decimators_8.decimate2_sup(it, buf, len); + break; + case 2: + m_decimators_8.decimate4_sup(it, buf, len); + break; + case 3: + m_decimators_8.decimate8_sup(it, buf, len); + break; + case 4: + m_decimators_8.decimate16_sup(it, buf, len); + break; + case 5: + m_decimators_8.decimate32_sup(it, buf, len); + break; + case 6: + m_decimators_8.decimate64_sup(it, buf, len); + break; + default: + break; + } + } else { // Centered + switch (m_log2Decim) { + case 1: + m_decimators_8.decimate2_cen(it, buf, len); + break; + case 2: + m_decimators_8.decimate4_cen(it, buf, len); + break; + case 3: + m_decimators_8.decimate8_cen(it, buf, len); + break; + case 4: + m_decimators_8.decimate16_cen(it, buf, len); + break; + case 5: + m_decimators_8.decimate32_cen(it, buf, len); + break; + case 6: + m_decimators_8.decimate64_cen(it, buf, len); + break; + default: + break; + } + } + } + } + + void convert_12(SampleVector::iterator* it, const qint16* buf, qint32 len) + { + if (m_log2Decim == 0) { + m_decimators_12.decimate1(it, buf, len); + } else { + if (m_fcPos == 0) { // Infradyne + switch (m_log2Decim) { + case 1: + m_decimators_12.decimate2_inf(it, buf, len); + break; + case 2: + m_decimators_12.decimate4_inf(it, buf, len); + break; + case 3: + m_decimators_12.decimate8_inf(it, buf, len); + break; + case 4: + m_decimators_12.decimate16_inf(it, buf, len); + break; + case 5: + m_decimators_12.decimate32_inf(it, buf, len); + break; + case 6: + m_decimators_12.decimate64_inf(it, buf, len); + break; + default: + break; + } + } else if (m_fcPos == 1) {// Supradyne + switch (m_log2Decim) { + case 1: + m_decimators_12.decimate2_sup(it, buf, len); + break; + case 2: + m_decimators_12.decimate4_sup(it, buf, len); + break; + case 3: + m_decimators_12.decimate8_sup(it, buf, len); + break; + case 4: + m_decimators_12.decimate16_sup(it, buf, len); + break; + case 5: + m_decimators_12.decimate32_sup(it, buf, len); + break; + case 6: + m_decimators_12.decimate64_sup(it, buf, len); + break; + default: + break; + } + } else { // Centered + switch (m_log2Decim) { + case 1: + m_decimators_12.decimate2_cen(it, buf, len); + break; + case 2: + m_decimators_12.decimate4_cen(it, buf, len); + break; + case 3: + m_decimators_12.decimate8_cen(it, buf, len); + break; + case 4: + m_decimators_12.decimate16_cen(it, buf, len); + break; + case 5: + m_decimators_12.decimate32_cen(it, buf, len); + break; + case 6: + m_decimators_12.decimate64_cen(it, buf, len); + break; + default: + break; + } + } + } + } + + void convert_16(SampleVector::iterator* it, const qint16* buf, qint32 len) + { + if (m_log2Decim == 0) { + m_decimators_16.decimate1(it, buf, len); + } else { + if (m_fcPos == 0) { // Infradyne + switch (m_log2Decim) { + case 1: + m_decimators_16.decimate2_inf(it, buf, len); + break; + case 2: + m_decimators_16.decimate4_inf(it, buf, len); + break; + case 3: + m_decimators_16.decimate8_inf(it, buf, len); + break; + case 4: + m_decimators_16.decimate16_inf(it, buf, len); + break; + case 5: + m_decimators_16.decimate32_inf(it, buf, len); + break; + case 6: + m_decimators_16.decimate64_inf(it, buf, len); + break; + default: + break; + } + } else if (m_fcPos == 1) {// Supradyne + switch (m_log2Decim) { + case 1: + m_decimators_16.decimate2_sup(it, buf, len); + break; + case 2: + m_decimators_16.decimate4_sup(it, buf, len); + break; + case 3: + m_decimators_16.decimate8_sup(it, buf, len); + break; + case 4: + m_decimators_16.decimate16_sup(it, buf, len); + break; + case 5: + m_decimators_16.decimate32_sup(it, buf, len); + break; + case 6: + m_decimators_16.decimate64_sup(it, buf, len); + break; + default: + break; + } + } else { // Centered + switch (m_log2Decim) { + case 1: + m_decimators_16.decimate2_cen(it, buf, len); + break; + case 2: + m_decimators_16.decimate4_cen(it, buf, len); + break; + case 3: + m_decimators_16.decimate8_cen(it, buf, len); + break; + case 4: + m_decimators_16.decimate16_cen(it, buf, len); + break; + case 5: + m_decimators_16.decimate32_cen(it, buf, len); + break; + case 6: + m_decimators_16.decimate64_cen(it, buf, len); + break; + default: + break; + } + } + } + } + + +private slots: + void tick(); + void handleInputMessages(); +}; + +#endif // _TESTSOURCE_TESTSOURCETHREAD_H_ diff --git a/plugins/samplemimo/testmi/testsourcemi.pro b/plugins/samplemimo/testmi/testsourcemi.pro new file mode 100644 index 000000000..92f455cfc --- /dev/null +++ b/plugins/samplemimo/testmi/testsourcemi.pro @@ -0,0 +1,48 @@ +#-------------------------------------------------------- +# +# Pro file for Android and Windows builds with Qt Creator +# +#-------------------------------------------------------- + +TEMPLATE = lib +CONFIG += plugin + +QT += core gui widgets multimedia opengl + +TARGET = mimotestsourcemi + +DEFINES += USE_SSE2=1 +QMAKE_CXXFLAGS += -msse2 +DEFINES += USE_SSE4_1=1 +QMAKE_CXXFLAGS += -msse4.1 +QMAKE_CXXFLAGS += -std=c++11 +macx:QMAKE_LFLAGS_SONAME = -Wl,-install_name,@rpath/ + +INCLUDEPATH += $$PWD +INCLUDEPATH += ../../../exports +INCLUDEPATH += ../../../sdrbase +INCLUDEPATH += ../../../sdrgui +INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client + +CONFIG(Release):build_subdir = release +CONFIG(Debug):build_subdir = debug + +SOURCES += testsourcegui.cpp\ + testsourcemiinput.cpp\ + testsourcemiplugin.cpp\ + testsourcemisettings.cpp\ + testsourcemithread.cpp + +HEADERS += testsourcemigui.h\ + testsourcemiinput.h\ + testsourcemiplugin.h\ + testsourcemisettings.h\ + testsourcemithread.h + +FORMS += testsourcemigui.ui + +LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase +LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui +LIBS += -L../../../swagger/$${build_subdir} -lswagger + +RESOURCES = ../../../sdrgui/resources/res.qrc diff --git a/plugins/samplesink/bladerf1output/bladerf1output.cpp b/plugins/samplesink/bladerf1output/bladerf1output.cpp index 30187c905..e6989cf53 100644 --- a/plugins/samplesink/bladerf1output/bladerf1output.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1output.cpp @@ -48,6 +48,7 @@ Bladerf1Output::Bladerf1Output(DeviceAPI *deviceAPI) : { m_sampleSourceFifo.resize(16*BLADERFOUTPUT_BLOCKSIZE); openDevice(); + m_deviceAPI->setNbSinkStreams(1); m_deviceAPI->setBuddySharedPtr(&m_sharedParams); m_networkManager = new QNetworkAccessManager(); connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); diff --git a/plugins/samplesink/bladerf1output/bladerf1outputplugin.cpp b/plugins/samplesink/bladerf1output/bladerf1outputplugin.cpp index ce4909600..726d113cb 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputplugin.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1outputplugin.cpp @@ -135,7 +135,7 @@ PluginInstanceGUI* Bladerf1OutputPlugin::createSampleSinkPluginInstanceGUI( } #endif -DeviceSampleSink* Bladerf1OutputPlugin::createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI) +DeviceSampleSink* Bladerf1OutputPlugin::createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI) { if(sinkId == m_deviceTypeID) { diff --git a/plugins/samplesink/bladerf1output/bladerf1outputplugin.h b/plugins/samplesink/bladerf1output/bladerf1outputplugin.h index 739e78db7..bd7b50e27 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputplugin.h +++ b/plugins/samplesink/bladerf1output/bladerf1outputplugin.h @@ -43,7 +43,7 @@ public: QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI); + virtual DeviceSampleSink* createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesink/bladerf2output/bladerf2output.cpp b/plugins/samplesink/bladerf2output/bladerf2output.cpp index c1d60b498..b87c45860 100644 --- a/plugins/samplesink/bladerf2output/bladerf2output.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2output.cpp @@ -51,6 +51,7 @@ BladeRF2Output::BladeRF2Output(DeviceAPI *deviceAPI) : m_running(false) { openDevice(); + m_deviceAPI->setNbSinkStreams(1); m_networkManager = new QNetworkAccessManager(); connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } @@ -140,7 +141,7 @@ bool BladeRF2Output::openDevice() } } - m_deviceShared.m_channel = m_deviceAPI->getItemIndex(); // publicly allocate channel + m_deviceShared.m_channel = m_deviceAPI->getDeviceItemIndex(); // publicly allocate channel m_deviceShared.m_sink = this; m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API return true; @@ -264,7 +265,7 @@ bool BladeRF2Output::start() return false; } - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); BladeRF2OutputThread *bladeRF2OutputThread = findThread(); bool needsStart = false; @@ -376,7 +377,7 @@ void BladeRF2Output::stop() return; } - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); BladeRF2OutputThread *bladeRF2OutputThread = findThread(); if (bladeRF2OutputThread == 0) { // no thread allocated @@ -610,7 +611,7 @@ bool BladeRF2Output::handleMessage(const Message& message) if (dev) // The BladeRF device must have been open to do so { - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); if (report.getRxElseTx()) // Rx buddy change: check for sample rate change only { @@ -703,7 +704,7 @@ bool BladeRF2Output::applySettings(const BladeRF2OutputSettings& settings, bool QList reverseAPIKeys; struct bladerf *dev = m_deviceShared.m_dev->getDev(); - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); int nbChannels = getNbChannels(); qint64 deviceCenterFrequency = settings.m_centerFrequency; deviceCenterFrequency -= settings.m_transverterMode ? settings.m_transverterDeltaFrequency : 0; diff --git a/plugins/samplesink/bladerf2output/bladerf2outputplugin.cpp b/plugins/samplesink/bladerf2output/bladerf2outputplugin.cpp index 5ca3322a8..359a772ae 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputplugin.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2outputplugin.cpp @@ -139,7 +139,7 @@ PluginInstanceGUI* BladeRF2OutputPlugin::createSampleSinkPluginInstanceGUI( } #endif -DeviceSampleSink* BladeRF2OutputPlugin::createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI) +DeviceSampleSink* BladeRF2OutputPlugin::createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI) { if(sinkId == m_deviceTypeID) { diff --git a/plugins/samplesink/bladerf2output/bladerf2outputplugin.h b/plugins/samplesink/bladerf2output/bladerf2outputplugin.h index 6036c94cf..18e9343ec 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputplugin.h +++ b/plugins/samplesink/bladerf2output/bladerf2outputplugin.h @@ -43,7 +43,7 @@ public: QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI); + virtual DeviceSampleSink* createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesink/filesink/filesinkoutput.cpp b/plugins/samplesink/filesink/filesinkoutput.cpp index 567072f46..d0d4bda51 100644 --- a/plugins/samplesink/filesink/filesinkoutput.cpp +++ b/plugins/samplesink/filesink/filesinkoutput.cpp @@ -49,6 +49,7 @@ FileSinkOutput::FileSinkOutput(DeviceAPI *deviceAPI) : m_startingTimeStamp(0), m_masterTimer(deviceAPI->getMasterTimer()) { + m_deviceAPI->setNbSinkStreams(1); } FileSinkOutput::~FileSinkOutput() diff --git a/plugins/samplesink/filesink/filesinkplugin.cpp b/plugins/samplesink/filesink/filesinkplugin.cpp index ca13bec86..13fd34c1e 100644 --- a/plugins/samplesink/filesink/filesinkplugin.cpp +++ b/plugins/samplesink/filesink/filesinkplugin.cpp @@ -99,7 +99,7 @@ PluginInstanceGUI* FileSinkPlugin::createSampleSinkPluginInstanceGUI( } #endif -DeviceSampleSink* FileSinkPlugin::createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI) +DeviceSampleSink* FileSinkPlugin::createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI) { if(sinkId == m_deviceTypeID) { diff --git a/plugins/samplesink/filesink/filesinkplugin.h b/plugins/samplesink/filesink/filesinkplugin.h index 0e726ce87..805ff7d94 100644 --- a/plugins/samplesink/filesink/filesinkplugin.h +++ b/plugins/samplesink/filesink/filesinkplugin.h @@ -42,7 +42,7 @@ public: const QString& sinkId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI); + virtual DeviceSampleSink* createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesink/hackrfoutput/hackrfoutput.cpp b/plugins/samplesink/hackrfoutput/hackrfoutput.cpp index f014bc30b..6f80f555e 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutput.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutput.cpp @@ -46,6 +46,7 @@ HackRFOutput::HackRFOutput(DeviceAPI *deviceAPI) : m_running(false) { openDevice(); + m_deviceAPI->setNbSinkStreams(1); m_deviceAPI->setBuddySharedPtr(&m_sharedParams); m_networkManager = new QNetworkAccessManager(); connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp index f57d4d157..406f8d82a 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp @@ -148,7 +148,7 @@ PluginInstanceGUI* HackRFOutputPlugin::createSampleSinkPluginInstanceGUI( } #endif -DeviceSampleSink* HackRFOutputPlugin::createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI) +DeviceSampleSink* HackRFOutputPlugin::createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI) { if(sinkId == m_deviceTypeID) { diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.h b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.h index 201376683..2aa40cf4a 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.h @@ -41,7 +41,7 @@ public: const QString& sinkId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI); + virtual DeviceSampleSink* createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesink/limesdroutput/limesdroutput.cpp b/plugins/samplesink/limesdroutput/limesdroutput.cpp index a374bdfc1..700cfc70f 100644 --- a/plugins/samplesink/limesdroutput/limesdroutput.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutput.cpp @@ -54,6 +54,7 @@ LimeSDROutput::LimeSDROutput(DeviceAPI *deviceAPI) : m_running(false), m_channelAcquired(false) { + m_deviceAPI->setNbSinkStreams(1); m_sampleSourceFifo.resize(16*LIMESDROUTPUT_BLOCKSIZE); m_streamId.handle = 0; suspendRxBuddies(); @@ -88,7 +89,7 @@ void LimeSDROutput::destroy() bool LimeSDROutput::openDevice() { - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); // look for Tx buddies and get reference to common parameters // if there is a channel left take the first available diff --git a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp index 7988caf86..776340871 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp @@ -156,7 +156,7 @@ bool LimeSDROutputPlugin::findSerial(const char *lmsInfoStr, std::string& serial } } -DeviceSampleSink* LimeSDROutputPlugin::createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI) +DeviceSampleSink* LimeSDROutputPlugin::createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI) { if(sinkId == m_deviceTypeID) { diff --git a/plugins/samplesink/limesdroutput/limesdroutputplugin.h b/plugins/samplesink/limesdroutput/limesdroutputplugin.h index 3f41b8a24..ad856e151 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputplugin.h +++ b/plugins/samplesink/limesdroutput/limesdroutputplugin.h @@ -41,7 +41,7 @@ public: const QString& sinkId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI); + virtual DeviceSampleSink* createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesink/limesdroutput/readme.md b/plugins/samplesink/limesdroutput/readme.md index c9d3e7728..eecccd764 100644 --- a/plugins/samplesink/limesdroutput/readme.md +++ b/plugins/samplesink/limesdroutput/readme.md @@ -14,7 +14,6 @@ LimeSDR is a 2x2 MIMO device so it has two transmitting channels that can run co The plugin will be built only if LimeSuite is installed in your system. Please use version tagged v17.06.0 or maybe later. To build and install LimeSuite from source do: - - `sudo apt-get install libsqlite3-dev` - `git clone https://github.com/myriadrf/LimeSuite.git` - `cd LimeSuite` - `mkdir builddir` diff --git a/plugins/samplesink/localoutput/localoutput.cpp b/plugins/samplesink/localoutput/localoutput.cpp index 33f3ae3bb..89d42e157 100644 --- a/plugins/samplesink/localoutput/localoutput.cpp +++ b/plugins/samplesink/localoutput/localoutput.cpp @@ -44,7 +44,7 @@ LocalOutput::LocalOutput(DeviceAPI *deviceAPI) : m_deviceDescription("LocalOutput") { m_sampleSourceFifo.resize(96000 * 4); - + m_deviceAPI->setNbSinkStreams(1); m_networkManager = new QNetworkAccessManager(); connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } diff --git a/plugins/samplesink/localoutput/localoutputplugin.cpp b/plugins/samplesink/localoutput/localoutputplugin.cpp index c924313fa..d134edf23 100644 --- a/plugins/samplesink/localoutput/localoutputplugin.cpp +++ b/plugins/samplesink/localoutput/localoutputplugin.cpp @@ -99,7 +99,7 @@ PluginInstanceGUI* LocalOutputPlugin::createSampleSinkPluginInstanceGUI( } #endif -DeviceSampleSink *LocalOutputPlugin::createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI) +DeviceSampleSink *LocalOutputPlugin::createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI) { if (sinkId == m_deviceTypeID) { diff --git a/plugins/samplesink/localoutput/localoutputplugin.h b/plugins/samplesink/localoutput/localoutputplugin.h index 8d470a3d2..977c5876f 100644 --- a/plugins/samplesink/localoutput/localoutputplugin.h +++ b/plugins/samplesink/localoutput/localoutputplugin.h @@ -41,7 +41,7 @@ public: const QString& sinkId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI); + virtual DeviceSampleSink* createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesink/plutosdroutput/plutosdroutput.cpp b/plugins/samplesink/plutosdroutput/plutosdroutput.cpp index 970cc2da1..58da35428 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutput.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutput.cpp @@ -46,6 +46,7 @@ PlutoSDROutput::PlutoSDROutput(DeviceAPI *deviceAPI) : m_plutoTxBuffer(0), m_plutoSDROutputThread(0) { + m_deviceAPI->setNbSinkStreams(1); m_deviceSampleRates.m_addaConnvRate = 0; m_deviceSampleRates.m_bbRateHz = 0; m_deviceSampleRates.m_firRate = 0; diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp index 04cb8d847..366a9765f 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp @@ -113,7 +113,7 @@ PluginInstanceGUI* PlutoSDROutputPlugin::createSampleSinkPluginInstanceGUI( } #endif -DeviceSampleSink *PlutoSDROutputPlugin::createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI) +DeviceSampleSink *PlutoSDROutputPlugin::createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI) { if (sinkId == m_deviceTypeID) { diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h index aea40f5cb..f4c46a907 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h @@ -41,7 +41,7 @@ public: const QString& sinkId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI); + virtual DeviceSampleSink* createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesink/remoteoutput/remoteoutput.cpp b/plugins/samplesink/remoteoutput/remoteoutput.cpp index 537063e0b..527cf0e39 100644 --- a/plugins/samplesink/remoteoutput/remoteoutput.cpp +++ b/plugins/samplesink/remoteoutput/remoteoutput.cpp @@ -64,6 +64,7 @@ RemoteOutput::RemoteOutput(DeviceAPI *deviceAPI) : m_nbSamplesSinceRateCorrection(0), m_chunkSizeCorrection(0) { + m_deviceAPI->setNbSinkStreams(1); m_networkManager = new QNetworkAccessManager(); connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); connect(&m_masterTimer, SIGNAL(timeout()), this, SLOT(tick())); diff --git a/plugins/samplesink/remoteoutput/remoteoutputplugin.cpp b/plugins/samplesink/remoteoutput/remoteoutputplugin.cpp index 19ccc26ac..bdce864f6 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputplugin.cpp +++ b/plugins/samplesink/remoteoutput/remoteoutputplugin.cpp @@ -99,7 +99,7 @@ PluginInstanceGUI* RemoteOutputPlugin::createSampleSinkPluginInstanceGUI( } #endif -DeviceSampleSink* RemoteOutputPlugin::createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI) +DeviceSampleSink* RemoteOutputPlugin::createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI) { if(sinkId == m_deviceTypeID) { diff --git a/plugins/samplesink/remoteoutput/remoteoutputplugin.h b/plugins/samplesink/remoteoutput/remoteoutputplugin.h index 0d06e9ee8..840dd0f8a 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputplugin.h +++ b/plugins/samplesink/remoteoutput/remoteoutputplugin.h @@ -42,7 +42,7 @@ public: const QString& sinkId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI); + virtual DeviceSampleSink* createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesink/soapysdroutput/soapysdroutput.cpp b/plugins/samplesink/soapysdroutput/soapysdroutput.cpp index 6d8a9daab..5ab3c692f 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutput.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutput.cpp @@ -44,6 +44,7 @@ SoapySDROutput::SoapySDROutput(DeviceAPI *deviceAPI) : m_running(false), m_thread(0) { + m_deviceAPI->setNbSinkStreams(1); openDevice(); initGainSettings(m_settings); initTunableElementsSettings(m_settings); @@ -141,7 +142,7 @@ bool SoapySDROutput::openDevice() m_deviceShared.m_deviceParams = new DeviceSoapySDRParams(m_deviceShared.m_device); } - m_deviceShared.m_channel = m_deviceAPI->getItemIndex(); // publicly allocate channel + m_deviceShared.m_channel = m_deviceAPI->getDeviceItemIndex(); // publicly allocate channel m_deviceShared.m_sink = this; m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API return true; @@ -448,7 +449,7 @@ bool SoapySDROutput::start() return false; } - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); SoapySDROutputThread *soapySDROutputThread = findThread(); bool needsStart = false; @@ -545,7 +546,7 @@ void SoapySDROutput::stop() return; } - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); SoapySDROutputThread *soapySDROutputThread = findThread(); if (soapySDROutputThread == 0) { // no thread allocated @@ -790,7 +791,7 @@ bool SoapySDROutput::handleMessage(const Message& message) } else if (DeviceSoapySDRShared::MsgReportBuddyChange::match(message)) { - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); //DeviceSoapySDRShared::MsgReportBuddyChange& report = (DeviceSoapySDRShared::MsgReportBuddyChange&) message; SoapySDROutputSettings settings = m_settings; //bool fromRxBuddy = report.getRxElseTx(); @@ -861,7 +862,7 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool SoapySDR::Device *dev = m_deviceShared.m_device; SoapySDROutputThread *outputThread = findThread(); - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); qint64 xlatedDeviceCenterFrequency = settings.m_centerFrequency; xlatedDeviceCenterFrequency -= settings.m_transverterMode ? settings.m_transverterDeltaFrequency : 0; xlatedDeviceCenterFrequency = xlatedDeviceCenterFrequency < 0 ? 0 : xlatedDeviceCenterFrequency; diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputplugin.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputplugin.cpp index b3c6508f4..55005eace 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputplugin.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputplugin.cpp @@ -116,7 +116,7 @@ PluginInstanceGUI* SoapySDROutputPlugin::createSampleSinkPluginInstanceGUI( } #endif -DeviceSampleSink* SoapySDROutputPlugin::createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI) +DeviceSampleSink* SoapySDROutputPlugin::createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI) { if(sinkId == m_deviceTypeID) { diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputplugin.h b/plugins/samplesink/soapysdroutput/soapysdroutputplugin.h index e919a95b3..e8189bab3 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputplugin.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutputplugin.h @@ -43,7 +43,7 @@ public: QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI); + virtual DeviceSampleSink* createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputthread.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputthread.cpp index da7328e44..ce54d6846 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputthread.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputthread.cpp @@ -169,7 +169,7 @@ void SoapySDROutputThread::run() break; case InterpolatorFloat: default: - // TODO + callbackSOIF((float*) buffs[0], numElems); break; } } @@ -270,7 +270,7 @@ void SoapySDROutputThread::callbackMO(std::vector& buffs, qint32 samples break; case InterpolatorFloat: default: - // TODO + std::fill((float*) buffs[ichan], (float*) buffs[ichan] + 2*samplesPerChannel, 0.0f); break; } } @@ -437,3 +437,57 @@ void SoapySDROutputThread::callbackSO16(qint16* buf, qint32 len, unsigned int ch std::fill(buf, buf+2*len, 0); } } + + +void SoapySDROutputThread::callbackSOIF(float* buf, qint32 len, unsigned int channel) +{ + if (m_channels[channel].m_sampleFifo) + { + float bal = m_channels[channel].m_sampleFifo->getRWBalance(); + + if (bal < -0.25) { + qDebug("SoapySDROutputThread::callbackSO16: read lags: %f", bal); + } else if (bal > 0.25) { + qDebug("SoapySDROutputThread::callbackSO16: read leads: %f", bal); + } + + SampleVector::iterator beginRead; + m_channels[channel].m_sampleFifo->readAdvance(beginRead, len/(1< m_interpolators8; Interpolators m_interpolators12; Interpolators m_interpolators16; + InterpolatorsIF m_interpolatorsIF; Channel() : m_sampleFifo(0), @@ -90,6 +92,7 @@ private: void callbackSO8(qint8* buf, qint32 len, unsigned int channel = 0); void callbackSO12(qint16* buf, qint32 len, unsigned int channel = 0); void callbackSO16(qint16* buf, qint32 len, unsigned int channel = 0); + void callbackSOIF(float* buf, qint32 len, unsigned int channel = 0); void callbackMO(std::vector& buffs, qint32 samplesPerChannel); }; diff --git a/plugins/samplesink/xtrxoutput/xtrxoutput.cpp b/plugins/samplesink/xtrxoutput/xtrxoutput.cpp index 0c47fe5bc..4ee6b9bf3 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutput.cpp +++ b/plugins/samplesink/xtrxoutput/xtrxoutput.cpp @@ -53,7 +53,7 @@ XTRXOutput::XTRXOutput(DeviceAPI *deviceAPI) : m_running(false) { openDevice(); - + m_deviceAPI->setNbSinkStreams(1); m_networkManager = new QNetworkAccessManager(); connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } @@ -143,7 +143,7 @@ bool XTRXOutput::openDevice() } } - m_deviceShared.m_channel = m_deviceAPI->getItemIndex(); // publicly allocate channel + m_deviceShared.m_channel = m_deviceAPI->getDeviceItemIndex(); // publicly allocate channel m_deviceShared.m_sink = this; m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API return true; @@ -262,7 +262,7 @@ bool XTRXOutput::start() return false; } - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); XTRXOutputThread *xtrxOutputThread = findThread(); bool needsStart = false; @@ -361,7 +361,7 @@ void XTRXOutput::stop() return; } - int removedChannel = m_deviceAPI->getItemIndex(); // channel to remove + int removedChannel = m_deviceAPI->getDeviceItemIndex(); // channel to remove int requestedChannel = removedChannel ^ 1; // channel to keep (opposite channel) XTRXOutputThread *xtrxOutputThread = findThread(); @@ -756,7 +756,7 @@ bool XTRXOutput::handleMessage(const Message& message) bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, bool forceNCOFrequency) { - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); XTRXOutputThread *outputThread = findThread(); QList reverseAPIKeys; diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputplugin.cpp b/plugins/samplesink/xtrxoutput/xtrxoutputplugin.cpp index 7533ff75f..41513f255 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputplugin.cpp +++ b/plugins/samplesink/xtrxoutput/xtrxoutputplugin.cpp @@ -112,7 +112,7 @@ PluginInstanceGUI* XTRXOutputPlugin::createSampleSinkPluginInstanceGUI( } #endif -DeviceSampleSink* XTRXOutputPlugin::createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI) +DeviceSampleSink* XTRXOutputPlugin::createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI) { if(sinkId == m_deviceTypeID) { diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputplugin.h b/plugins/samplesink/xtrxoutput/xtrxoutputplugin.h index a5c985657..2d464768d 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputplugin.h +++ b/plugins/samplesink/xtrxoutput/xtrxoutputplugin.h @@ -41,7 +41,7 @@ public: const QString& sinkId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceAPI *deviceAPI); + virtual DeviceSampleSink* createSampleSinkPluginInstance(const QString& sinkId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/airspy/airspyinput.cpp b/plugins/samplesource/airspy/airspyinput.cpp index a0ac7b223..e7d1f7ee9 100644 --- a/plugins/samplesource/airspy/airspyinput.cpp +++ b/plugins/samplesource/airspy/airspyinput.cpp @@ -55,6 +55,7 @@ AirspyInput::AirspyInput(DeviceAPI *deviceAPI) : { openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); diff --git a/plugins/samplesource/airspy/airspyplugin.cpp b/plugins/samplesource/airspy/airspyplugin.cpp index 086542ad7..e01dd4fb6 100644 --- a/plugins/samplesource/airspy/airspyplugin.cpp +++ b/plugins/samplesource/airspy/airspyplugin.cpp @@ -155,7 +155,7 @@ PluginInstanceGUI* AirspyPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *AirspyPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *AirspyPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/airspy/airspyplugin.h b/plugins/samplesource/airspy/airspyplugin.h index ea7ba5aee..842bfeeb1 100644 --- a/plugins/samplesource/airspy/airspyplugin.h +++ b/plugins/samplesource/airspy/airspyplugin.h @@ -41,7 +41,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/airspyhf/airspyhfinput.cpp b/plugins/samplesource/airspyhf/airspyhfinput.cpp index a36c8c351..dd8772ed9 100644 --- a/plugins/samplesource/airspyhf/airspyhfinput.cpp +++ b/plugins/samplesource/airspyhf/airspyhfinput.cpp @@ -57,6 +57,7 @@ AirspyHFInput::AirspyHFInput(DeviceAPI *deviceAPI) : { openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); diff --git a/plugins/samplesource/airspyhf/airspyhfplugin.cpp b/plugins/samplesource/airspyhf/airspyhfplugin.cpp index 3085847a9..f713b593b 100644 --- a/plugins/samplesource/airspyhf/airspyhfplugin.cpp +++ b/plugins/samplesource/airspyhf/airspyhfplugin.cpp @@ -125,7 +125,7 @@ PluginInstanceGUI* AirspyHFPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *AirspyHFPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *AirspyHFPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/airspyhf/airspyhfplugin.h b/plugins/samplesource/airspyhf/airspyhfplugin.h index efe4b367d..9081bcc00 100644 --- a/plugins/samplesource/airspyhf/airspyhfplugin.h +++ b/plugins/samplesource/airspyhf/airspyhfplugin.h @@ -41,7 +41,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/bladerf1input/bladerf1input.cpp b/plugins/samplesource/bladerf1input/bladerf1input.cpp index 8642aa04b..d66773084 100644 --- a/plugins/samplesource/bladerf1input/bladerf1input.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1input.cpp @@ -50,6 +50,7 @@ Bladerf1Input::Bladerf1Input(DeviceAPI *deviceAPI) : { openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_deviceAPI->setBuddySharedPtr(&m_sharedParams); diff --git a/plugins/samplesource/bladerf1input/bladerf1inputplugin.cpp b/plugins/samplesource/bladerf1input/bladerf1inputplugin.cpp index 923de349f..1c670e24e 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputplugin.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1inputplugin.cpp @@ -134,7 +134,7 @@ PluginInstanceGUI* Blderf1InputPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *Blderf1InputPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *Blderf1InputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/bladerf1input/bladerf1inputplugin.h b/plugins/samplesource/bladerf1input/bladerf1inputplugin.h index 793bfa850..a9d5d381d 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputplugin.h +++ b/plugins/samplesource/bladerf1input/bladerf1inputplugin.h @@ -43,7 +43,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/bladerf2input/bladerf2input.cpp b/plugins/samplesource/bladerf2input/bladerf2input.cpp index beb691c8b..7245de741 100644 --- a/plugins/samplesource/bladerf2input/bladerf2input.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2input.cpp @@ -66,6 +66,7 @@ BladeRF2Input::BladeRF2Input(DeviceAPI *deviceAPI) : } m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); @@ -166,7 +167,7 @@ bool BladeRF2Input::openDevice() } } - m_deviceShared.m_channel = m_deviceAPI->getItemIndex(); // publicly allocate channel + m_deviceShared.m_channel = m_deviceAPI->getDeviceItemIndex(); // publicly allocate channel m_deviceShared.m_source = this; m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API return true; @@ -291,7 +292,7 @@ bool BladeRF2Input::start() return false; } - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); BladeRF2InputThread *bladerf2InputThread = findThread(); bool needsStart = false; @@ -406,7 +407,7 @@ void BladeRF2Input::stop() return; } - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); BladeRF2InputThread *bladerf2InputThread = findThread(); if (bladerf2InputThread == 0) { // no thread allocated @@ -629,7 +630,7 @@ bool BladeRF2Input::handleMessage(const Message& message) if (dev) // The BladeRF device must have been open to do so { - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); if (report.getRxElseTx()) // Rx buddy change: check for: frequency, LO correction, gain mode and value, bias tee, sample rate, bandwidth { @@ -771,7 +772,7 @@ bool BladeRF2Input::applySettings(const BladeRF2InputSettings& settings, bool fo bool forwardChangeTxBuddies = false; struct bladerf *dev = m_deviceShared.m_dev->getDev(); - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); qint64 xlatedDeviceCenterFrequency = settings.m_centerFrequency; xlatedDeviceCenterFrequency -= settings.m_transverterMode ? settings.m_transverterDeltaFrequency : 0; xlatedDeviceCenterFrequency = xlatedDeviceCenterFrequency < 0 ? 0 : xlatedDeviceCenterFrequency; diff --git a/plugins/samplesource/bladerf2input/bladerf2inputplugin.cpp b/plugins/samplesource/bladerf2input/bladerf2inputplugin.cpp index 4668d8bc5..c23755a45 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputplugin.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputplugin.cpp @@ -139,7 +139,7 @@ PluginInstanceGUI* Blderf2InputPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *Blderf2InputPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *Blderf2InputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/bladerf2input/bladerf2inputplugin.h b/plugins/samplesource/bladerf2input/bladerf2inputplugin.h index 2f7291870..c605da99b 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputplugin.h +++ b/plugins/samplesource/bladerf2input/bladerf2inputplugin.h @@ -43,7 +43,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/fcdpro/fcdproinput.cpp b/plugins/samplesource/fcdpro/fcdproinput.cpp index b5c8309df..6698788f0 100644 --- a/plugins/samplesource/fcdpro/fcdproinput.cpp +++ b/plugins/samplesource/fcdpro/fcdproinput.cpp @@ -50,6 +50,7 @@ FCDProInput::FCDProInput(DeviceAPI *deviceAPI) : m_fcdFIFO.setSize(20*fcd_traits::convBufSize); openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); diff --git a/plugins/samplesource/fcdpro/fcdproplugin.cpp b/plugins/samplesource/fcdpro/fcdproplugin.cpp index e2b55cc70..2b504cb53 100644 --- a/plugins/samplesource/fcdpro/fcdproplugin.cpp +++ b/plugins/samplesource/fcdpro/fcdproplugin.cpp @@ -107,7 +107,7 @@ PluginInstanceGUI* FCDProPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *FCDProPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *FCDProPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == fcd_traits::interfaceIID) { diff --git a/plugins/samplesource/fcdpro/fcdproplugin.h b/plugins/samplesource/fcdpro/fcdproplugin.h index fd376f2a5..726c0c9fb 100644 --- a/plugins/samplesource/fcdpro/fcdproplugin.h +++ b/plugins/samplesource/fcdpro/fcdproplugin.h @@ -24,7 +24,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); private: static const PluginDescriptor m_pluginDescriptor; diff --git a/plugins/samplesource/fcdproplus/fcdproplusinput.cpp b/plugins/samplesource/fcdproplus/fcdproplusinput.cpp index 39ed2b3ac..fc4bbdddc 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusinput.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusinput.cpp @@ -50,6 +50,7 @@ FCDProPlusInput::FCDProPlusInput(DeviceAPI *deviceAPI) : m_fcdFIFO.setSize(20*fcd_traits::convBufSize); openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); diff --git a/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp b/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp index 81c4fe1b1..6dc3abfc8 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp @@ -109,7 +109,7 @@ PluginInstanceGUI* FCDProPlusPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *FCDProPlusPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *FCDProPlusPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if(sourceId == fcd_traits::interfaceIID) { diff --git a/plugins/samplesource/fcdproplus/fcdproplusplugin.h b/plugins/samplesource/fcdproplus/fcdproplusplugin.h index c4bdd40db..c20032a37 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusplugin.h +++ b/plugins/samplesource/fcdproplus/fcdproplusplugin.h @@ -24,7 +24,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_deviceTypeID; diff --git a/plugins/samplesource/filesource/filesourceinput.cpp b/plugins/samplesource/filesource/filesourceinput.cpp index 0c701975d..f456da78a 100644 --- a/plugins/samplesource/filesource/filesourceinput.cpp +++ b/plugins/samplesource/filesource/filesourceinput.cpp @@ -60,18 +60,21 @@ FileSourceInput::FileSourceInput(DeviceAPI *deviceAPI) : m_sampleSize(0), m_centerFrequency(0), m_recordLength(0), - m_startingTimeStamp(0), - m_masterTimer(deviceAPI->getMasterTimer()) + m_startingTimeStamp(0) { + m_deviceAPI->setNbSourceStreams(1); qDebug("FileSourceInput::FileSourceInput: device source engine: %p", m_deviceAPI->getDeviceSourceEngine()); qDebug("FileSourceInput::FileSourceInput: device source engine message queue: %p", m_deviceAPI->getDeviceEngineInputMessageQueue()); qDebug("FileSourceInput::FileSourceInput: device source: %p", m_deviceAPI->getDeviceSourceEngine()->getSource()); m_networkManager = new QNetworkAccessManager(); connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + m_masterTimer.setTimerType(Qt::PreciseTimer); + m_masterTimer.start(50); } FileSourceInput::~FileSourceInput() { + m_masterTimer.stop(); disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); delete m_networkManager; @@ -91,7 +94,11 @@ void FileSourceInput::openFileStream() m_ifstream.close(); } +#ifdef Q_OS_WIN + m_ifstream.open(m_fileName.toStdWString().c_str(), std::ios::binary | std::ios::ate); +#else m_ifstream.open(m_fileName.toStdString().c_str(), std::ios::binary | std::ios::ate); +#endif quint64 fileSize = m_ifstream.tellg(); if (fileSize > sizeof(FileRecord::Header)) diff --git a/plugins/samplesource/filesource/filesourceinput.h b/plugins/samplesource/filesource/filesourceinput.h index 0bde89328..c7f872809 100644 --- a/plugins/samplesource/filesource/filesourceinput.h +++ b/plugins/samplesource/filesource/filesourceinput.h @@ -332,7 +332,7 @@ public: quint64 m_centerFrequency; quint64 m_recordLength; //!< record length in seconds computed from file size quint64 m_startingTimeStamp; - const QTimer& m_masterTimer; + QTimer m_masterTimer; QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; diff --git a/plugins/samplesource/filesource/filesourceplugin.cpp b/plugins/samplesource/filesource/filesourceplugin.cpp index 73db3551a..f42702856 100644 --- a/plugins/samplesource/filesource/filesourceplugin.cpp +++ b/plugins/samplesource/filesource/filesourceplugin.cpp @@ -99,7 +99,7 @@ PluginInstanceGUI* FileSourcePlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *FileSourcePlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *FileSourcePlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/filesource/filesourceplugin.h b/plugins/samplesource/filesource/filesourceplugin.h index 7e10e1079..9b70c77a6 100644 --- a/plugins/samplesource/filesource/filesourceplugin.h +++ b/plugins/samplesource/filesource/filesourceplugin.h @@ -41,7 +41,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/hackrfinput/hackrfinput.cpp b/plugins/samplesource/hackrfinput/hackrfinput.cpp index c27eac1c3..6c808d028 100644 --- a/plugins/samplesource/hackrfinput/hackrfinput.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinput.cpp @@ -52,6 +52,7 @@ HackRFInput::HackRFInput(DeviceAPI *deviceAPI) : openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_deviceAPI->setBuddySharedPtr(&m_sharedParams); diff --git a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp index 07d978707..c6028338f 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp @@ -150,7 +150,7 @@ PluginInstanceGUI* HackRFInputPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *HackRFInputPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *HackRFInputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/hackrfinput/hackrfinputplugin.h b/plugins/samplesource/hackrfinput/hackrfinputplugin.h index 8a9858d25..407a39399 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputplugin.h +++ b/plugins/samplesource/hackrfinput/hackrfinputplugin.h @@ -41,7 +41,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/limesdrinput/limesdrinput.cpp b/plugins/samplesource/limesdrinput/limesdrinput.cpp index 46b90c2aa..dcef3de24 100644 --- a/plugins/samplesource/limesdrinput/limesdrinput.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinput.cpp @@ -64,6 +64,7 @@ LimeSDRInput::LimeSDRInput(DeviceAPI *deviceAPI) : resumeRxBuddies(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); @@ -105,7 +106,7 @@ bool LimeSDRInput::openDevice() qDebug("LimeSDRInput::openDevice: allocated SampleFifo"); } - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); // look for Rx buddies and get reference to common parameters // if there is a channel left take the first available diff --git a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp index 472387e6d..1e51d8d87 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp @@ -155,7 +155,7 @@ bool LimeSDRInputPlugin::findSerial(const char *lmsInfoStr, std::string& serial) } } -DeviceSampleSource *LimeSDRInputPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *LimeSDRInputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/limesdrinput/limesdrinputplugin.h b/plugins/samplesource/limesdrinput/limesdrinputplugin.h index 23518c254..a04e9da40 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputplugin.h +++ b/plugins/samplesource/limesdrinput/limesdrinputplugin.h @@ -41,7 +41,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/localinput/localinput.cpp b/plugins/samplesource/localinput/localinput.cpp index 72e913aea..a5e0b7805 100644 --- a/plugins/samplesource/localinput/localinput.cpp +++ b/plugins/samplesource/localinput/localinput.cpp @@ -48,6 +48,7 @@ LocalInput::LocalInput(DeviceAPI *deviceAPI) : m_sampleFifo.setSize(96000 * 4); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); diff --git a/plugins/samplesource/localinput/localinputplugin.cpp b/plugins/samplesource/localinput/localinputplugin.cpp index 145cbbfad..afa13b6d7 100644 --- a/plugins/samplesource/localinput/localinputplugin.cpp +++ b/plugins/samplesource/localinput/localinputplugin.cpp @@ -99,7 +99,7 @@ PluginInstanceGUI* LocalInputPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *LocalInputPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *LocalInputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/localinput/localinputplugin.h b/plugins/samplesource/localinput/localinputplugin.h index fb76031ed..94ec3a2ac 100644 --- a/plugins/samplesource/localinput/localinputplugin.h +++ b/plugins/samplesource/localinput/localinputplugin.h @@ -41,7 +41,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/perseus/perseusinput.cpp b/plugins/samplesource/perseus/perseusinput.cpp index 9b43017ab..72fcacc60 100644 --- a/plugins/samplesource/perseus/perseusinput.cpp +++ b/plugins/samplesource/perseus/perseusinput.cpp @@ -47,6 +47,7 @@ PerseusInput::PerseusInput(DeviceAPI *deviceAPI) : { openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); diff --git a/plugins/samplesource/perseus/perseusplugin.cpp b/plugins/samplesource/perseus/perseusplugin.cpp index 4bc899db8..3f4279ff2 100644 --- a/plugins/samplesource/perseus/perseusplugin.cpp +++ b/plugins/samplesource/perseus/perseusplugin.cpp @@ -115,7 +115,7 @@ PluginInstanceGUI* PerseusPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *PerseusPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *PerseusPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/perseus/perseusplugin.h b/plugins/samplesource/perseus/perseusplugin.h index 0ffd8b6da..f66ffe418 100644 --- a/plugins/samplesource/perseus/perseusplugin.h +++ b/plugins/samplesource/perseus/perseusplugin.h @@ -41,7 +41,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/plutosdrinput/plutosdrinput.cpp b/plugins/samplesource/plutosdrinput/plutosdrinput.cpp index bd90df412..97bc9d714 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinput.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinput.cpp @@ -60,6 +60,7 @@ PlutoSDRInput::PlutoSDRInput(DeviceAPI *deviceAPI) : resumeBuddies(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp index b96145312..cf7178269 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp @@ -115,7 +115,7 @@ PluginInstanceGUI* PlutoSDRInputPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *PlutoSDRInputPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *PlutoSDRInputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.h b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.h index c906f7622..fe5184cdd 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.h @@ -41,7 +41,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/remoteinput/remoteinput.cpp b/plugins/samplesource/remoteinput/remoteinput.cpp index 04d3ce361..5224cbf66 100644 --- a/plugins/samplesource/remoteinput/remoteinput.cpp +++ b/plugins/samplesource/remoteinput/remoteinput.cpp @@ -55,6 +55,7 @@ RemoteInput::RemoteInput(DeviceAPI *deviceAPI) : m_remoteInputUDPHandler = new RemoteInputUDPHandler(&m_sampleFifo, m_deviceAPI); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); diff --git a/plugins/samplesource/remoteinput/remoteinputplugin.cpp b/plugins/samplesource/remoteinput/remoteinputplugin.cpp index 2ecea04c1..f2d9032db 100644 --- a/plugins/samplesource/remoteinput/remoteinputplugin.cpp +++ b/plugins/samplesource/remoteinput/remoteinputplugin.cpp @@ -99,7 +99,7 @@ PluginInstanceGUI* RemoteInputPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *RemoteInputPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *RemoteInputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/remoteinput/remoteinputplugin.h b/plugins/samplesource/remoteinput/remoteinputplugin.h index 8d082981a..f41a2014b 100644 --- a/plugins/samplesource/remoteinput/remoteinputplugin.h +++ b/plugins/samplesource/remoteinput/remoteinputplugin.h @@ -41,7 +41,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/rtlsdr/rtlsdrinput.cpp b/plugins/samplesource/rtlsdr/rtlsdrinput.cpp index f976a07e0..9a3229c24 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrinput.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrinput.cpp @@ -60,6 +60,7 @@ RTLSDRInput::RTLSDRInput(DeviceAPI *deviceAPI) : openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); diff --git a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp index 263b5e7ff..74b2f507c 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp @@ -92,7 +92,7 @@ PluginInstanceGUI* RTLSDRPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *RTLSDRPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *RTLSDRPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/rtlsdr/rtlsdrplugin.h b/plugins/samplesource/rtlsdr/rtlsdrplugin.h index 32c1a4387..a121bf498 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrplugin.h +++ b/plugins/samplesource/rtlsdr/rtlsdrplugin.h @@ -24,7 +24,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/sdrplay/sdrplayinput.cpp b/plugins/samplesource/sdrplay/sdrplayinput.cpp index 787effa76..afd948273 100644 --- a/plugins/samplesource/sdrplay/sdrplayinput.cpp +++ b/plugins/samplesource/sdrplay/sdrplayinput.cpp @@ -54,6 +54,7 @@ SDRPlayInput::SDRPlayInput(DeviceAPI *deviceAPI) : { openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); diff --git a/plugins/samplesource/sdrplay/sdrplayplugin.cpp b/plugins/samplesource/sdrplay/sdrplayplugin.cpp index 0b1bb5fad..79ae48200 100644 --- a/plugins/samplesource/sdrplay/sdrplayplugin.cpp +++ b/plugins/samplesource/sdrplay/sdrplayplugin.cpp @@ -118,7 +118,7 @@ PluginInstanceGUI* SDRPlayPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *SDRPlayPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *SDRPlayPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/sdrplay/sdrplayplugin.h b/plugins/samplesource/sdrplay/sdrplayplugin.h index fce4655e5..2dc617aad 100644 --- a/plugins/samplesource/sdrplay/sdrplayplugin.h +++ b/plugins/samplesource/sdrplay/sdrplayplugin.h @@ -41,7 +41,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/soapysdrinput/soapysdrinput.cpp b/plugins/samplesource/soapysdrinput/soapysdrinput.cpp index b7c144600..46a227d15 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinput.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinput.cpp @@ -55,6 +55,7 @@ SoapySDRInput::SoapySDRInput(DeviceAPI *deviceAPI) : initDeviceArgSettings(m_settings); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); @@ -159,7 +160,7 @@ bool SoapySDRInput::openDevice() m_deviceShared.m_deviceParams = new DeviceSoapySDRParams(m_deviceShared.m_device); } - m_deviceShared.m_channel = m_deviceAPI->getItemIndex(); // publicly allocate channel + m_deviceShared.m_channel = m_deviceAPI->getDeviceItemIndex(); // publicly allocate channel m_deviceShared.m_source = this; m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API return true; @@ -481,7 +482,7 @@ bool SoapySDRInput::start() return false; } - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); SoapySDRInputThread *soapySDRInputThread = findThread(); bool needsStart = false; @@ -587,7 +588,7 @@ void SoapySDRInput::stop() return; } - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); SoapySDRInputThread *soapySDRInputThread = findThread(); if (soapySDRInputThread == 0) { // no thread allocated @@ -841,7 +842,7 @@ bool SoapySDRInput::handleMessage(const Message& message) } else if (DeviceSoapySDRShared::MsgReportBuddyChange::match(message)) { - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); DeviceSoapySDRShared::MsgReportBuddyChange& report = (DeviceSoapySDRShared::MsgReportBuddyChange&) message; SoapySDRInputSettings settings = m_settings; settings.m_fcPos = (SoapySDRInputSettings::fcPos_t) report.getFcPos(); @@ -918,7 +919,7 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo SoapySDR::Device *dev = m_deviceShared.m_device; SoapySDRInputThread *inputThread = findThread(); - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); qint64 xlatedDeviceCenterFrequency = settings.m_centerFrequency; xlatedDeviceCenterFrequency -= settings.m_transverterMode ? settings.m_transverterDeltaFrequency : 0; xlatedDeviceCenterFrequency = xlatedDeviceCenterFrequency < 0 ? 0 : xlatedDeviceCenterFrequency; diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputplugin.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputplugin.cpp index c15eb10f7..bb60b3161 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputplugin.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputplugin.cpp @@ -115,7 +115,7 @@ PluginInstanceGUI* SoapySDRInputPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *SoapySDRInputPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *SoapySDRInputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputplugin.h b/plugins/samplesource/soapysdrinput/soapysdrinputplugin.h index 64633db4b..cebcbda0f 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputplugin.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinputplugin.h @@ -43,7 +43,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/testsource/testsourceinput.cpp b/plugins/samplesource/testsource/testsourceinput.cpp index db6841655..6e3901731 100644 --- a/plugins/samplesource/testsource/testsourceinput.cpp +++ b/plugins/samplesource/testsource/testsourceinput.cpp @@ -47,6 +47,7 @@ TestSourceInput::TestSourceInput(DeviceAPI *deviceAPI) : m_masterTimer(deviceAPI->getMasterTimer()) { m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); if (!m_sampleFifo.setSize(96000 * 4)) { diff --git a/plugins/samplesource/testsource/testsourceplugin.cpp b/plugins/samplesource/testsource/testsourceplugin.cpp index 4f1f7eff4..082b3965e 100644 --- a/plugins/samplesource/testsource/testsourceplugin.cpp +++ b/plugins/samplesource/testsource/testsourceplugin.cpp @@ -96,7 +96,7 @@ PluginInstanceGUI* TestSourcePlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *TestSourcePlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *TestSourcePlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/testsource/testsourceplugin.h b/plugins/samplesource/testsource/testsourceplugin.h index 893730697..6309d3fe9 100644 --- a/plugins/samplesource/testsource/testsourceplugin.h +++ b/plugins/samplesource/testsource/testsourceplugin.h @@ -41,7 +41,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/plugins/samplesource/testsource/testsourcethread.cpp b/plugins/samplesource/testsource/testsourcethread.cpp index e16c4a85c..7bd364216 100644 --- a/plugins/samplesource/testsource/testsourcethread.cpp +++ b/plugins/samplesource/testsource/testsourcethread.cpp @@ -63,7 +63,8 @@ TestSourceThread::TestSourceThread(SampleSinkFifo* sampleFifo, QObject* parent) m_fcPosShift(0), m_throttlems(TESTSOURCE_THROTTLE_MS), m_throttleToggle(false), - m_mutex(QMutex::Recursive) + m_mutex(QMutex::Recursive), + m_histoCounter(0) { connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); } @@ -74,6 +75,7 @@ TestSourceThread::~TestSourceThread() void TestSourceThread::startWork() { + m_timer.setTimerType(Qt::PreciseTimer); connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); m_timer.start(50); m_startWaitMutex.lock(); @@ -392,6 +394,25 @@ void TestSourceThread::tick() { qint64 throttlems = m_elapsedTimer.restart(); + std::map::iterator it; + it = m_timerHistogram.find(throttlems); + + if (it == m_timerHistogram.end()) { + m_timerHistogram[throttlems] = 1; + } else { + it->second++; + } + + if (m_histoCounter < 49) { + m_histoCounter++; + } else { + qDebug("TestSourceThread::tick: -----------"); + for (std::map::iterator it = m_timerHistogram.begin(); it != m_timerHistogram.end(); ++it) { + qDebug("TestSourceThread::tick: %d: %d", it->first, it->second); + } + m_histoCounter = 0; + } + if ((throttlems > 45) && (throttlems < 55) && (throttlems != m_throttlems)) { QMutexLocker mutexLocker(&m_mutex); diff --git a/plugins/samplesource/testsource/testsourcethread.h b/plugins/samplesource/testsource/testsourcethread.h index 309a08dc6..110013fbe 100644 --- a/plugins/samplesource/testsource/testsourcethread.h +++ b/plugins/samplesource/testsource/testsourcethread.h @@ -18,6 +18,8 @@ #ifndef _TESTSOURCE_TESTSOURCETHREAD_H_ #define _TESTSOURCE_TESTSOURCETHREAD_H_ +#include + #include #include #include @@ -133,6 +135,9 @@ private: Decimators m_decimators_12; Decimators m_decimators_16; + std::map m_timerHistogram; + uint32_t m_histoCounter; + void startWork(); void stopWork(); void run(); diff --git a/plugins/samplesource/xtrxinput/xtrxinput.cpp b/plugins/samplesource/xtrxinput/xtrxinput.cpp index 9755bd242..c8a41c266 100644 --- a/plugins/samplesource/xtrxinput/xtrxinput.cpp +++ b/plugins/samplesource/xtrxinput/xtrxinput.cpp @@ -57,6 +57,7 @@ XTRXInput::XTRXInput(DeviceAPI *deviceAPI) : openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->setNbSourceStreams(1); m_deviceAPI->addAncillarySink(m_fileSink); m_networkManager = new QNetworkAccessManager(); @@ -158,7 +159,7 @@ bool XTRXInput::openDevice() } } - m_deviceShared.m_channel = m_deviceAPI->getItemIndex(); // publicly allocate channel + m_deviceShared.m_channel = m_deviceAPI->getDeviceItemIndex(); // publicly allocate channel m_deviceShared.m_source = this; m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API return true; @@ -277,7 +278,7 @@ bool XTRXInput::start() return false; } - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); XTRXInputThread *xtrxInputThread = findThread(); bool needsStart = false; @@ -376,7 +377,7 @@ void XTRXInput::stop() return; } - int removedChannel = m_deviceAPI->getItemIndex(); // channel to remove + int removedChannel = m_deviceAPI->getDeviceItemIndex(); // channel to remove int requestedChannel = removedChannel ^ 1; // channel to keep (opposite channel) XTRXInputThread *xtrxInputThread = findThread(); @@ -846,7 +847,7 @@ void XTRXInput::apply_gain_pga(double gain) bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, bool forceNCOFrequency) { - int requestedChannel = m_deviceAPI->getItemIndex(); + int requestedChannel = m_deviceAPI->getDeviceItemIndex(); XTRXInputThread *inputThread = findThread(); QList reverseAPIKeys; diff --git a/plugins/samplesource/xtrxinput/xtrxinputplugin.cpp b/plugins/samplesource/xtrxinput/xtrxinputplugin.cpp index 713e132a3..0c8b54769 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputplugin.cpp +++ b/plugins/samplesource/xtrxinput/xtrxinputplugin.cpp @@ -113,7 +113,7 @@ PluginInstanceGUI* XTRXInputPlugin::createSampleSourcePluginInstanceGUI( } #endif -DeviceSampleSource *XTRXInputPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI) +DeviceSampleSource *XTRXInputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI) { if (sourceId == m_deviceTypeID) { diff --git a/plugins/samplesource/xtrxinput/xtrxinputplugin.h b/plugins/samplesource/xtrxinput/xtrxinputplugin.h index 608ee31cc..25cb5c6a9 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputplugin.h +++ b/plugins/samplesource/xtrxinput/xtrxinputplugin.h @@ -42,7 +42,7 @@ public: const QString& sourceId, QWidget **widget, DeviceUISet *deviceUISet); - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); static const QString m_hardwareID; static const QString m_deviceTypeID; diff --git a/scriptsapi/Readme.md b/scriptsapi/Readme.md index 3d0662844..b90086ee0 100644 --- a/scriptsapi/Readme.md +++ b/scriptsapi/Readme.md @@ -27,3 +27,29 @@ Normal sequence of operations: - Start `freqtracking.py` in a terminal - In SDRangel connect the Frequency Tracker plugin by clicking on the grey square at the left of the top bar of the Frequency Tracker GUI. It opens the channel settings dialog. Check the 'Reverse API' box. Next to this box is the address and port at which the channel will be connected. If you use the defaults for `freqtracking.py` you may leave it as it is else you have to adjust it to the address and port of `freqtracking.py` (options `-A` and `-P`). - In the same manner connect the channel you want to be controlled by `freqtracking.py`. You may connect any number of channels like this. When a channel is removed `freqtracking.py` will automatically remove it from its list at the first attempt to synchronize that will fail. + +

ptt_active.py

+ +PTT (Push To Talk) actively listening system. For a pair of given device set indexes it actively listens to start and stop commands on the corresponding devices to swich over to the other + +Options are: + + - `-h` or `--help` show help message and exit + - `-A` or `--address` listening IP address. Default `0.0.0.0` (all interfaces) + - `-P` or `--port` listening port. Default `8000` + - `-p` or `--port-sdr` SDRangel instance REST API listening port. Default `8091` + - `-l` or `--link` Pair of indexes of the device sets to link. Default `0 1` + - `-d` or `--delay` Switch over delay in seconds. Default `1` + - `-f` or `--freq-sync` Synchronize devices center frequencies + +Normal sequence of operations: + +In this example we have a Rx device on index 0 and a Tx device on index 1. All settings are assumed to be the default settings. + + - Start `ptt_active.py` in a terminal + - On the Rx device right click on the start/stop button and activate reverse API at address `127.0.0.1` port `8000` (default) + - On the Tx device right click on the start/stop button and activate reverse API at address `127.0.0.1` port `8000` (default) + - Start the Rx or Tx device + - Stop the running device (Rx or Tx) this will switch over automatically to the other + +Important: you should initiate switch over by stopping the active device and not by starting the other. diff --git a/swagger/sdrangel/examples/ptt_active.py b/scriptsapi/ptt_active.py old mode 100644 new mode 100755 similarity index 95% rename from swagger/sdrangel/examples/ptt_active.py rename to scriptsapi/ptt_active.py index 310f2dfc5..11b848025 --- a/swagger/sdrangel/examples/ptt_active.py +++ b/scriptsapi/ptt_active.py @@ -36,7 +36,7 @@ def start_device(device_index, sdrangel_ip, sdrangel_port): base_url = f'http://{sdrangel_ip}:{sdrangel_port}/sdrangel' dev_run_url = base_url + f'/deviceset/{device_index}/device/run' r = requests.get(url=dev_run_url) - if r.status_code / 100 == 2: + if r.status_code // 100 == 2: rj = r.json() state = rj.get("state", None) if state is not None: @@ -51,7 +51,7 @@ def start_device(device_index, sdrangel_ip, sdrangel_port): else: print(f'start_device: Cannot get device {device_index} running state') else: - print(f'start_device: Error getting device {device_index} running state') + print(f'start_device: Error {r.status_code} getting device {device_index} running state') # ====================================================================== def stop_device(device_index, sdrangel_ip, sdrangel_port): @@ -60,7 +60,7 @@ def stop_device(device_index, sdrangel_ip, sdrangel_port): base_url = f'http://{sdrangel_ip}:{sdrangel_port}/sdrangel' dev_run_url = base_url + f'/deviceset/{device_index}/device/run' r = requests.get(url=dev_run_url) - if r.status_code / 100 == 2: + if r.status_code // 100 == 2: rj = r.json() state = rj.get("state", None) if state is not None: @@ -75,7 +75,7 @@ def stop_device(device_index, sdrangel_ip, sdrangel_port): else: print(f'stop_device: Cannot get device {device_index} running state') else: - print(f'stop_device: Error getting device {device_index} running state') + print(f'stop_device: Error {r.status_code} getting device {device_index} running state') # ====================================================================== def set_focus(device_index, sdrangel_ip, sdrangel_port): @@ -84,12 +84,12 @@ def set_focus(device_index, sdrangel_ip, sdrangel_port): base_url = f'http://{sdrangel_ip}:{sdrangel_port}/sdrangel' dev_focus_url = base_url + f'/deviceset/{device_index}/focus' r = requests.patch(url=dev_focus_url) - if r.status_code / 100 == 2: + if r.status_code // 100 == 2: print(f'set_focus: Focus set on device set {device_index}') elif r.status_code == 400: print(f'set_focus: Focus on device set is not supported in a server instance') else: - print(f'set_focus: Error setting focus on device set {device_index}') + print(f'set_focus: Error {r.status_code} setting focus on device set {device_index}') # ====================================================================== def get_sdrangel_ip(request): @@ -126,7 +126,7 @@ def set_center_frequency(new_frequency, device_index, sdrangel_ip, sdrangel_port # ---------------------------------------------------------------------- base_url = f'http://{sdrangel_ip}:{sdrangel_port}/sdrangel' r = requests.get(url=base_url + f'/deviceset/{device_index}/device/settings') - if r.status_code / 100 == 2: + if r.status_code // 100 == 2: rj = r.json() frequency = get_center_frequency(rj) if new_frequency != frequency: @@ -138,11 +138,11 @@ def set_center_frequency(new_frequency, device_index, sdrangel_ip, sdrangel_port FREQ_HAS_CHANGED = True return jsonify(rj) else: - print(f'set_center_frequency: failed to change center frequency of device {device_index}. HTTP error {r.status_code}') + print(f'set_center_frequency: failed to change center frequency of device {device_index} with error {r.status_code}') else: print(f'set_center_frequency: frequency of device {device_index} is unchanged') else: - print(f'set_center_frequency: error getting settings for device {device_index}. HTTP error {r.status_code}') + print(f'set_center_frequency: error {r.status_code} getting settings for device {device_index}') return "" # ====================================================================== diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 0920d0865..378de595a 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -82,6 +82,7 @@ set(sdrbase_SOURCES dsp/dspengine.cpp dsp/dspdevicesourceengine.cpp dsp/dspdevicesinkengine.cpp + dsp/dspdevicemimoengine.cpp dsp/fftcorr.cpp dsp/fftengine.cpp dsp/fftfilt.cpp @@ -111,6 +112,7 @@ set(sdrbase_SOURCES dsp/wfir.cpp dsp/devicesamplesource.cpp dsp/devicesamplesink.cpp + dsp/devicesamplemimo.cpp device/deviceapi.cpp device/deviceenumerator.cpp @@ -180,10 +182,12 @@ set(sdrbase_HEADERS dsp/decimatorsfi.h dsp/decimatorsu.h dsp/interpolators.h + dsp/interpolatorsif.h dsp/dspcommands.h dsp/dspengine.h dsp/dspdevicesourceengine.h dsp/dspdevicesinkengine.h + dsp/dspdevicemimoengine.h dsp/dsptypes.h dsp/fftcorr.h dsp/fftengine.h @@ -233,6 +237,7 @@ set(sdrbase_HEADERS dsp/wfir.h dsp/devicesamplesource.h dsp/devicesamplesink.h + dsp/devicesamplemimo.h device/deviceapi.h device/deviceenumerator.h diff --git a/sdrbase/audio/audiodevicemanager.cpp b/sdrbase/audio/audiodevicemanager.cpp index e10c1a181..4f1c34619 100644 --- a/sdrbase/audio/audiodevicemanager.cpp +++ b/sdrbase/audio/audiodevicemanager.cpp @@ -25,7 +25,7 @@ #include #include -const float AudioDeviceManager::m_defaultAudioInputVolume = 0.15f; +const float AudioDeviceManager::m_defaultAudioInputVolume = 1.0f; const QString AudioDeviceManager::m_defaultUDPAddress = "127.0.0.1"; const QString AudioDeviceManager::m_defaultDeviceName = "System default device"; diff --git a/sdrbase/channel/channelapi.h b/sdrbase/channel/channelapi.h index e776b75b2..db469e96f 100644 --- a/sdrbase/channel/channelapi.h +++ b/sdrbase/channel/channelapi.h @@ -40,7 +40,7 @@ public: { StreamSingleSink, //!< Exposes a single sink stream (input, Rx) StreamSingleSource, //!< Exposes a single source stream (output, Tx) - StreamAny //!< May expose any number of sink and/or source streams + StreamMIMO //!< May expose any number of sink and/or source streams }; ChannelAPI(const QString& name, StreamType streamType); diff --git a/sdrbase/device/deviceapi.cpp b/sdrbase/device/deviceapi.cpp index 5f79b0436..3cf7fb95b 100644 --- a/sdrbase/device/deviceapi.cpp +++ b/sdrbase/device/deviceapi.cpp @@ -19,9 +19,11 @@ #include "plugin/plugininterface.h" #include "dsp/dspdevicesourceengine.h" #include "dsp/dspdevicesinkengine.h" +#include "dsp/dspdevicemimoengine.h" #include "dsp/dspengine.h" #include "dsp/devicesamplesource.h" #include "dsp/devicesamplesink.h" +#include "dsp/devicesamplemimo.h" #include "settings/preset.h" #include "channel/channelapi.h" @@ -31,20 +33,24 @@ DeviceAPI::DeviceAPI( StreamType streamType, int deviceTabIndex, DSPDeviceSourceEngine *deviceSourceEngine, - DSPDeviceSinkEngine *deviceSinkEngine + DSPDeviceSinkEngine *deviceSinkEngine, + DSPDeviceMIMOEngine *deviceMIMOEngine ) : m_streamType(streamType), m_deviceTabIndex(deviceTabIndex), - m_nbItems(1), - m_itemIndex(0), - m_pluginInterface(0), + m_deviceNbItems(1), + m_deviceItemIndex(0), + m_nbSourceStreams(0), + m_nbSinkStreams(0), + m_pluginInterface(nullptr), m_masterTimer(DSPEngine::instance()->getMasterTimer()), m_samplingDeviceSequence(0), m_samplingDevicePluginInstanceUI(0), - m_buddySharedPtr(0), + m_buddySharedPtr(nullptr), m_isBuddyLeader(false), m_deviceSourceEngine(deviceSourceEngine), - m_deviceSinkEngine(deviceSinkEngine) + m_deviceSinkEngine(deviceSinkEngine), + m_deviceMIMOEngine(deviceMIMOEngine) { } @@ -52,6 +58,34 @@ DeviceAPI::~DeviceAPI() { } +void DeviceAPI::addSourceStream() +{ + if (m_deviceMIMOEngine) { + m_deviceMIMOEngine->addSourceStream(); + } +} + +void DeviceAPI::removeLastSourceStream() +{ + if (m_deviceMIMOEngine) { + m_deviceMIMOEngine->removeLastSourceStream(); + } +} + +void DeviceAPI::addSinkStream() +{ + if (m_deviceMIMOEngine) { + m_deviceMIMOEngine->addSinkStream(); + } +} + +void DeviceAPI::removeLastSinkStream() +{ + if (m_deviceMIMOEngine) { + m_deviceMIMOEngine->removeLastSinkStream(); + } +} + void DeviceAPI::addAncillarySink(BasebandSampleSink *sink) { if (m_deviceSourceEngine) { @@ -70,12 +104,19 @@ void DeviceAPI::removeAncillarySink(BasebandSampleSink* sink) } } +void DeviceAPI::setSpectrumSinkInput(bool sourceElseSink, unsigned int index) +{ + if (m_deviceMIMOEngine) { // In practice this is only used in the MIMO case + m_deviceMIMOEngine->setSpectrumSinkInput(sourceElseSink, index); + } +} + void DeviceAPI::addChannelSink(ThreadedBasebandSampleSink* sink, int streamIndex) { - (void) streamIndex; - if (m_deviceSourceEngine) { m_deviceSourceEngine->addThreadedSink(sink); + } else if (m_deviceMIMOEngine) { + m_deviceMIMOEngine->addChannelSink(sink, streamIndex); } } @@ -156,6 +197,13 @@ void DeviceAPI::setSampleSink(DeviceSampleSink* sink) } } +void DeviceAPI::setSampleMIMO(DeviceSampleMIMO* mimo) +{ + if (m_deviceMIMOEngine) { + m_deviceMIMOEngine->setMIMO(mimo); + } +} + DeviceSampleSource *DeviceAPI::getSampleSource() { if (m_deviceSourceEngine) { @@ -174,14 +222,25 @@ DeviceSampleSink *DeviceAPI::getSampleSink() } } +DeviceSampleMIMO *DeviceAPI::getSampleMIMO() +{ + if (m_deviceMIMOEngine) { + return m_deviceMIMOEngine->getMIMO(); + } else { + return nullptr; + } +} + bool DeviceAPI::initDeviceEngine() { if (m_deviceSourceEngine) { return m_deviceSourceEngine->initAcquisition(); } else if (m_deviceSinkEngine) { return m_deviceSinkEngine->initGeneration(); + } else if (m_deviceMIMOEngine) { + return m_deviceMIMOEngine->initProcess(); } else { - return false; // TODO: not implemented + return false; } } @@ -191,8 +250,10 @@ bool DeviceAPI::startDeviceEngine() return m_deviceSourceEngine->startAcquisition(); } else if (m_deviceSinkEngine) { return m_deviceSinkEngine->startGeneration(); + } else if (m_deviceMIMOEngine) { + return m_deviceMIMOEngine->startProcess(); } else { - return false; // TODO: not implemented + return false; } } @@ -202,6 +263,8 @@ void DeviceAPI::stopDeviceEngine() m_deviceSourceEngine->stopAcquistion(); } else if (m_deviceSinkEngine) { m_deviceSinkEngine->stopGeneration(); + } else if (m_deviceMIMOEngine) { + m_deviceMIMOEngine->stopProcess(); } } @@ -211,8 +274,10 @@ DeviceAPI::EngineState DeviceAPI::state() const return (DeviceAPI::EngineState) m_deviceSourceEngine->state(); } else if (m_deviceSinkEngine) { return (DeviceAPI::EngineState) m_deviceSinkEngine->state(); + } else if (m_deviceMIMOEngine) { + return (DeviceAPI::EngineState) m_deviceMIMOEngine->state(); } else { - return StError; // TODO: not implemented + return StError; } } @@ -222,8 +287,10 @@ QString DeviceAPI::errorMessage() return m_deviceSourceEngine->errorMessage(); } else if (m_deviceSinkEngine) { return m_deviceSinkEngine->errorMessage(); + } else if (m_deviceMIMOEngine) { + return m_deviceMIMOEngine->errorMessage(); } else { - return "Not implemented"; // TODO: not implemented + return "Not implemented"; } } @@ -233,8 +300,10 @@ uint DeviceAPI::getDeviceUID() const return m_deviceSourceEngine->getUID(); } else if (m_deviceSinkEngine) { return m_deviceSinkEngine->getUID(); + } else if (m_deviceMIMOEngine) { + return m_deviceMIMOEngine->getUID(); } else { - return 0; // TODO: not implemented + return 0; } } @@ -244,8 +313,10 @@ MessageQueue *DeviceAPI::getDeviceEngineInputMessageQueue() return m_deviceSourceEngine->getInputMessageQueue(); } else if (m_deviceSinkEngine) { return m_deviceSinkEngine->getInputMessageQueue(); + } else if (m_deviceMIMOEngine) { + return m_deviceMIMOEngine->getInputMessageQueue(); } else { - return nullptr; // TODO: not implemented + return nullptr; } } @@ -255,8 +326,10 @@ MessageQueue *DeviceAPI::getSamplingDeviceInputMessageQueue() return m_deviceSourceEngine->getSource()->getInputMessageQueue(); } else if (m_deviceSinkEngine) { return m_deviceSinkEngine->getSink()->getInputMessageQueue(); + } else if (m_deviceMIMOEngine) { + return m_deviceMIMOEngine->getMIMO()->getInputMessageQueue(); } else { - return nullptr; // TODO: not implemented + return nullptr; } } @@ -266,17 +339,19 @@ MessageQueue *DeviceAPI::getSamplingDeviceGUIMessageQueue() return m_deviceSourceEngine->getSource()->getMessageQueueToGUI(); } else if (m_deviceSinkEngine) { return m_deviceSinkEngine->getSink()->getMessageQueueToGUI(); + } else if (m_deviceMIMOEngine) { + return m_deviceMIMOEngine->getMIMO()->getMessageQueueToGUI(); } else { - return nullptr; // TODO: not implemented + return nullptr; } } void DeviceAPI::configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection, int streamIndex) { - (void) streamIndex; - if (m_deviceSourceEngine) { m_deviceSourceEngine->configureCorrections(dcOffsetCorrection, iqImbalanceCorrection); + } else if (m_deviceMIMOEngine) { + m_deviceMIMOEngine->configureCorrections(dcOffsetCorrection, iqImbalanceCorrection, streamIndex); } } @@ -285,14 +360,14 @@ void DeviceAPI::setHardwareId(const QString& id) m_hardwareId = id; } -void DeviceAPI::setNbItems(uint32_t nbItems) +void DeviceAPI::setDeviceNbItems(uint32_t nbItems) { - m_nbItems = nbItems; + m_deviceNbItems = nbItems; } -void DeviceAPI::setItemIndex(uint32_t index) +void DeviceAPI::setDeviceItemIndex(uint32_t index) { - m_itemIndex = index; + m_deviceItemIndex = index; } void DeviceAPI::setSamplingDevicePluginInterface(PluginInterface *iface) diff --git a/sdrbase/device/deviceapi.h b/sdrbase/device/deviceapi.h index 2c801a5ab..6d3547c1d 100644 --- a/sdrbase/device/deviceapi.h +++ b/sdrbase/device/deviceapi.h @@ -30,11 +30,13 @@ class ThreadedBasebandSampleSource; class ChannelAPI; class DeviceSampleSink; class DeviceSampleSource; +class DeviceSampleMIMO; class MessageQueue; class PluginInterface; class PluginInstanceGUI; class DSPDeviceSourceEngine; class DSPDeviceSinkEngine; +class DSPDeviceMIMOEngine; class Preset; class SDRBASE_API DeviceAPI : public QObject { @@ -44,7 +46,7 @@ public: { StreamSingleRx, //!< Exposes a single input stream that can be one of the streams of a physical device StreamSingleTx, //!< Exposes a single output stream that can be one of the streams of a physical device - StreamAny //!< May expose any number of input and/or output streams + StreamMIMO //!< May expose any number of input and/or output streams }; enum EngineState { @@ -59,12 +61,20 @@ public: StreamType streamType, int deviceTabIndex, DSPDeviceSourceEngine *deviceSourceEngine, - DSPDeviceSinkEngine *deviceSinkEngine + DSPDeviceSinkEngine *deviceSinkEngine, + DSPDeviceMIMOEngine *deviceMIMOEngine ); ~DeviceAPI(); + // MIMO Engine baseband / channel lists management + void addSourceStream(); + void removeLastSourceStream(); + void addSinkStream(); + void removeLastSinkStream(); + void addAncillarySink(BasebandSampleSink* sink); //!< Adds a sink to receive full baseband and that is not a channel (e.g. spectrum) void removeAncillarySink(BasebandSampleSink* sink); //!< Removes it + void setSpectrumSinkInput(bool sourceElseSink = true, unsigned int index = 0); //!< Used in the MIMO case to select which stream is used as input to main spectrum void addChannelSink(ThreadedBasebandSampleSink* sink, int streamIndex = 0); //!< Add a channel sink (Rx) void removeChannelSink(ThreadedBasebandSampleSink* sink, int streamIndex = 0); //!< Remove a channel sink (Rx) @@ -78,8 +88,10 @@ public: void setSampleSource(DeviceSampleSource* source); //!< Set the device sample source (single Rx) void setSampleSink(DeviceSampleSink* sink); //!< Set the device sample sink (single Tx) + void setSampleMIMO(DeviceSampleMIMO* mimo); //!< Set the device sample MIMO DeviceSampleSource *getSampleSource(); //!< Return pointer to the device sample source (single Rx) or nullptr DeviceSampleSink *getSampleSink(); //!< Return pointer to the device sample sink (single Tx) or nullptr + DeviceSampleMIMO *getSampleMIMO(); //!< Return pointer to the device sample MIMO or nullptr bool initDeviceEngine(); //!< Init the device engine corresponding to the stream type bool startDeviceEngine(); //!< Start the device engine corresponding to the stream type @@ -114,8 +126,6 @@ public: void setSamplingDeviceSequence(int sequence) { m_samplingDeviceSequence = sequence; } // void setSampleSourceSequence(int sequence); // void setSampleSinkSequence(int sequence); - void setNbItems(uint32_t nbItems); - void setItemIndex(uint32_t index); void setSamplingDevicePluginInterface(PluginInterface *iface); // void setSampleSourcePluginInterface(PluginInterface *iface); // void setSampleSinkPluginInterface(PluginInterface *iface); @@ -137,8 +147,11 @@ public: // uint32_t getSampleSourceSequence() const { return m_sampleSourceSequence; } // uint32_t getSampleSinkSequence() const { return m_sampleSinkSequence; } - uint32_t getNbItems() const { return m_nbItems; } - uint32_t getItemIndex() const { return m_itemIndex; } + void setDeviceNbItems(uint32_t nbItems); + void setDeviceItemIndex(uint32_t index); + uint32_t getDeviceNbItems() const { return m_deviceNbItems; } + uint32_t getDeviceItemIndex() const { return m_deviceItemIndex; } + int getDeviceSetIndex() const { return m_deviceTabIndex; } PluginInterface *getPluginInterface() { return m_pluginInterface; } @@ -176,6 +189,11 @@ public: const std::vector& getSourceBuddies() const { return m_sourceBuddies; } const std::vector& getSinkBuddies() const { return m_sinkBuddies; } + void setNbSourceStreams(uint32_t nbSourceStreams) { m_nbSourceStreams = nbSourceStreams; } + void setNbSinkStreams(uint32_t nbSinkStreams) { m_nbSinkStreams = nbSinkStreams; } + uint32_t getNbSourceStreams() const { return m_nbSourceStreams; } + uint32_t getNbSinkStreams() const { return m_nbSinkStreams; } + const QTimer& getMasterTimer() const { return m_masterTimer; } //!< This is the DSPEngine master timer protected: @@ -184,8 +202,10 @@ protected: StreamType m_streamType; int m_deviceTabIndex; //!< This is the tab index in the GUI and also the device set index QString m_hardwareId; //!< The internal id that identifies the type of hardware (i.e. HackRF, BladeRF, ...) - uint32_t m_nbItems; //!< Number of items or streams in the device. Can be >1 for NxM devices (i.e. 2 for LimeSDR) - uint32_t m_itemIndex; //!< The Rx stream index. Can be >0 for NxM devices (i.e. 0 or 1 for LimeSDR) + uint32_t m_deviceNbItems; //!< Number of items in the physical device either Rx or Tx. Can be >1 for NxM devices (i.e. 2 for LimeSDR) + uint32_t m_deviceItemIndex; //!< The item index inb the Rx or Tx side of the physical device. Can be >0 for NxM devices (i.e. 0 or 1 for LimeSDR) + uint32_t m_nbSourceStreams; //!< The number of source streams in the logical device. 1 for Single Rx (SI) can be 0 or more for MIMO + uint32_t m_nbSinkStreams; //!< The number of sink streams in the logical device. 1 for Single Tx (SO) can be 0 or more for MIMO PluginInterface* m_pluginInterface; const QTimer& m_masterTimer; //!< This is the DSPEngine master timer QString m_samplingDeviceId; //!< The internal plugin ID corresponding to the device (i.e. for HackRF input, for HackRF output ...) @@ -211,6 +231,10 @@ protected: DSPDeviceSinkEngine *m_deviceSinkEngine; QList m_channelSourceAPIs; + // MIMO + + DSPDeviceMIMOEngine *m_deviceMIMOEngine; + private: void renumerateChannels(); }; diff --git a/sdrbase/device/deviceenumerator.cpp b/sdrbase/device/deviceenumerator.cpp index d8ef40728..ded8c95ce 100644 --- a/sdrbase/device/deviceenumerator.cpp +++ b/sdrbase/device/deviceenumerator.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// Copyright (C) 2016-2019 Edouard Griffiths, F4EXB // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // @@ -80,6 +80,30 @@ void DeviceEnumerator::enumerateTxDevices(PluginManager *pluginManager) } } +void DeviceEnumerator::enumerateMIMODevices(PluginManager *pluginManager) +{ + m_mimoEnumeration.clear(); + PluginAPI::SamplingDeviceRegistrations& mimoDeviceRegistrations = pluginManager->getMIMODeviceRegistrations(); + int index = 0; + + for (int i = 0; i < mimoDeviceRegistrations.count(); i++) + { + PluginInterface::SamplingDevices samplingDevices = mimoDeviceRegistrations[i].m_plugin->enumSampleMIMO(); + + for (int j = 0; j < samplingDevices.count(); j++) + { + m_mimoEnumeration.push_back( + DeviceEnumeration( + samplingDevices[j], + mimoDeviceRegistrations[i].m_plugin, + index + ) + ); + index++; + } + } +} + void DeviceEnumerator::listRxDeviceNames(QList& list, std::vector& indexes) const { for (DevicesEnumeration::const_iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it) @@ -104,6 +128,18 @@ void DeviceEnumerator::listTxDeviceNames(QList& list, std::vector& } } +void DeviceEnumerator::listMIMODeviceNames(QList& list, std::vector& indexes) const +{ + for (DevicesEnumeration::const_iterator it = m_mimoEnumeration.begin(); it != m_mimoEnumeration.end(); ++it) + { + if ((it->m_samplingDevice.claimed < 0) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice)) + { + list.append(it->m_samplingDevice.displayedName); + indexes.push_back(it->m_index); + } + } +} + void DeviceEnumerator::changeRxSelection(int tabIndex, int deviceIndex) { for (DevicesEnumeration::iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it) @@ -130,6 +166,19 @@ void DeviceEnumerator::changeTxSelection(int tabIndex, int deviceIndex) } } +void DeviceEnumerator::changeMIMOSelection(int tabIndex, int deviceIndex) +{ + for (DevicesEnumeration::iterator it = m_mimoEnumeration.begin(); it != m_mimoEnumeration.end(); ++it) + { + if (it->m_samplingDevice.claimed == tabIndex) { + it->m_samplingDevice.claimed = -1; + } + if (it->m_index == deviceIndex) { + it->m_samplingDevice.claimed = tabIndex; + } + } +} + void DeviceEnumerator::removeRxSelection(int tabIndex) { for (DevicesEnumeration::iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it) @@ -150,6 +199,16 @@ void DeviceEnumerator::removeTxSelection(int tabIndex) } } +void DeviceEnumerator::removeMIMOSelection(int tabIndex) +{ + for (DevicesEnumeration::iterator it = m_mimoEnumeration.begin(); it != m_mimoEnumeration.end(); ++it) + { + if (it->m_samplingDevice.claimed == tabIndex) { + it->m_samplingDevice.claimed = -1; + } + } +} + int DeviceEnumerator::getFileSourceDeviceIndex() const { for (DevicesEnumeration::const_iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it) @@ -174,6 +233,18 @@ int DeviceEnumerator::getFileSinkDeviceIndex() const return -1; } +int DeviceEnumerator::getTestMIMODeviceIndex() const +{ + for (DevicesEnumeration::const_iterator it = m_mimoEnumeration.begin(); it != m_mimoEnumeration.end(); ++it) + { + if (it->m_samplingDevice.id == PluginManager::getTestMIMODeviceId()) { + return it->m_index; + } + } + + return -1; +} + int DeviceEnumerator::getRxSamplingDeviceIndex(const QString& deviceId, int sequence) { for (DevicesEnumeration::iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it) @@ -198,3 +269,14 @@ int DeviceEnumerator::getTxSamplingDeviceIndex(const QString& deviceId, int sequ return -1; } +int DeviceEnumerator::getMIMOSamplingDeviceIndex(const QString& deviceId, int sequence) +{ + for (DevicesEnumeration::iterator it = m_mimoEnumeration.begin(); it != m_mimoEnumeration.end(); ++it) + { + if ((it->m_samplingDevice.id == deviceId) && (it->m_samplingDevice.sequence == sequence)) { + return it->m_index; + } + } + + return -1; +} diff --git a/sdrbase/device/deviceenumerator.h b/sdrbase/device/deviceenumerator.h index 9062007d5..1c61f41b1 100644 --- a/sdrbase/device/deviceenumerator.h +++ b/sdrbase/device/deviceenumerator.h @@ -35,22 +35,31 @@ public: void enumerateRxDevices(PluginManager *pluginManager); void enumerateTxDevices(PluginManager *pluginManager); + void enumerateMIMODevices(PluginManager *pluginManager); void listRxDeviceNames(QList& list, std::vector& indexes) const; void listTxDeviceNames(QList& list, std::vector& indexes) const; + void listMIMODeviceNames(QList& list, std::vector& indexes) const; void changeRxSelection(int tabIndex, int deviceIndex); void changeTxSelection(int tabIndex, int deviceIndex); + void changeMIMOSelection(int tabIndex, int deviceIndex); void removeRxSelection(int tabIndex); void removeTxSelection(int tabIndex); + void removeMIMOSelection(int tabIndex); int getNbRxSamplingDevices() const { return m_rxEnumeration.size(); } int getNbTxSamplingDevices() const { return m_txEnumeration.size(); } + int getNbMIMOSamplingDevices() const { return m_mimoEnumeration.size(); } const PluginInterface::SamplingDevice* getRxSamplingDevice(int deviceIndex) const { return &m_rxEnumeration[deviceIndex].m_samplingDevice; } const PluginInterface::SamplingDevice* getTxSamplingDevice(int deviceIndex) const { return &m_txEnumeration[deviceIndex].m_samplingDevice; } + const PluginInterface::SamplingDevice* getMIMOSamplingDevice(int deviceIndex) const { return &m_mimoEnumeration[deviceIndex].m_samplingDevice; } PluginInterface *getRxPluginInterface(int deviceIndex) { return m_rxEnumeration[deviceIndex].m_pluginInterface; } PluginInterface *getTxPluginInterface(int deviceIndex) { return m_txEnumeration[deviceIndex].m_pluginInterface; } - int getFileSourceDeviceIndex() const; - int getFileSinkDeviceIndex() const; + PluginInterface *getMIMOPluginInterface(int deviceIndex) { return m_mimoEnumeration[deviceIndex].m_pluginInterface; } + int getFileSourceDeviceIndex() const; //!< Get Rx default device + int getFileSinkDeviceIndex() const; //!< Get Tx default device + int getTestMIMODeviceIndex() const; //!< Get MIMO default device int getRxSamplingDeviceIndex(const QString& deviceId, int sequence); int getTxSamplingDeviceIndex(const QString& deviceId, int sequence); + int getMIMOSamplingDeviceIndex(const QString& deviceId, int sequence); private: struct DeviceEnumeration @@ -70,6 +79,7 @@ private: DevicesEnumeration m_rxEnumeration; DevicesEnumeration m_txEnumeration; + DevicesEnumeration m_mimoEnumeration; }; #endif /* SDRBASE_DEVICE_DEVICEENUMERATOR_H_ */ diff --git a/sdrbase/dsp/devicesamplemimo.cpp b/sdrbase/dsp/devicesamplemimo.cpp new file mode 100644 index 000000000..08a0e933b --- /dev/null +++ b/sdrbase/dsp/devicesamplemimo.cpp @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "devicesamplemimo.h" + +DeviceSampleMIMO::DeviceSampleMIMO() : + m_guiMessageQueue(0) +{ + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +DeviceSampleMIMO::~DeviceSampleMIMO() +{ +} + +void DeviceSampleMIMO::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != 0) + { + if (handleMessage(*message)) + { + delete message; + } + } +} + +SampleSourceFifo* DeviceSampleMIMO::getSampleSourceFifo(unsigned int index) +{ + if (index >= m_sampleSourceFifos.size()) { + return nullptr; + } else { + return &m_sampleSourceFifos[index]; + } +} + +SampleSinkFifo* DeviceSampleMIMO::getSampleSinkFifo(unsigned int index) +{ + if (index >= m_sampleSinkFifos.size()) { + return nullptr; + } else { + return &m_sampleSinkFifos[index]; + } +} diff --git a/sdrbase/dsp/devicesamplemimo.h b/sdrbase/dsp/devicesamplemimo.h new file mode 100644 index 000000000..54dcbc0ab --- /dev/null +++ b/sdrbase/dsp/devicesamplemimo.h @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_DSP_DEVICESAMPLEMIMO_H_ +#define SDRBASE_DSP_DEVICESAMPLEMIMO_H_ + +#include + +#include "samplesourcefifo.h" +#include "samplesinkfifo.h" +#include "util/message.h" +#include "util/messagequeue.h" +#include "export.h" + +namespace SWGSDRangel +{ + class SWGDeviceSettings; + class SWGDeviceState; + class SWGDeviceReport; +} + +class SDRBASE_API DeviceSampleMIMO : public QObject { + Q_OBJECT +public: + typedef enum { + FC_POS_INFRA = 0, + FC_POS_SUPRA, + FC_POS_CENTER + } fcPos_t; + + DeviceSampleMIMO(); + virtual ~DeviceSampleMIMO(); + virtual void destroy() = 0; + + virtual void init() = 0; //!< initializations to be done when all collaborating objects are created and possibly connected + virtual bool start() = 0; + virtual void stop() = 0; + + virtual QByteArray serialize() const = 0; + virtual bool deserialize(const QByteArray& data) = 0; + + virtual const QString& getDeviceDescription() const = 0; + + virtual int getSinkSampleRate(int index) const = 0; //!< Sample rate exposed by the sink at index + virtual void setSinkSampleRate(int sampleRate, int index) = 0; //!< For when the sink sample rate is set externally + virtual quint64 getSinkCenterFrequency(int index) const = 0; //!< Center frequency exposed by the sink at index + virtual void setSinkCenterFrequency(qint64 centerFrequency, int index) = 0; + + virtual int getSourceSampleRate(int index) const = 0; //!< Sample rate exposed by the source at index + virtual void setSourceSampleRate(int sampleRate, int index) = 0; //!< For when the source sample rate is set externally + virtual quint64 getSourceCenterFrequency(int index) const = 0; //!< Center frequency exposed by the source at index + virtual void setSourceCenterFrequency(qint64 centerFrequency, int index) = 0; + + virtual bool handleMessage(const Message& message) = 0; + + virtual int webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage) + { + (void) response; + errorMessage = "Not implemented"; + return 501; + } + + virtual int webapiSettingsPutPatch( + bool force, //!< true to force settings = put + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage) + { + (void) force; + (void) deviceSettingsKeys; + (void) response; + errorMessage = "Not implemented"; + return 501; + } + + virtual int webapiRunGet( + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) + { + (void) response; + errorMessage = "Not implemented"; + return 501; + } + + virtual int webapiRun(bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) + { + (void) run; + (void) response; + errorMessage = "Not implemented"; + return 501; + } + + virtual int webapiReportGet( + SWGSDRangel::SWGDeviceReport& response, + QString& errorMessage) + { + (void) response; + errorMessage = "Not implemented"; + return 501; + } + + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setMessageQueueToGUI(MessageQueue *queue) = 0; // pure virtual so that child classes must have to deal with this + MessageQueue *getMessageQueueToGUI() { return m_guiMessageQueue; } + + unsigned int getNbSourceFifos() const { return m_sampleSourceFifos.size(); } //!< Get the number of Tx FIFOs + unsigned int getNbSinkFifos() const { return m_sampleSinkFifos.size(); } //!< Get the number of Rx FIFOs + SampleSourceFifo* getSampleSourceFifo(unsigned int index); //!< Get Tx FIFO at index + SampleSinkFifo* getSampleSinkFifo(unsigned int index); //!< Get Rx FIFO at index + // Streams and FIFOs are in opposed source/sink type whick makes it confusing when stream direction is involved: + // Rx: source stream -> sink FIFO -> channel sinks + // Tx: sink stream <- source FIFO <- channel sources + unsigned int getNbSourceStreams() const { return m_sampleSinkFifos.size(); } //!< Commodity function same as getNbSinkFifos (Rx or source streams) + unsigned int getNbSinkStreams() const { return m_sampleSourceFifos.size(); } //!< Commodity function same as getNbSourceFifos (Tx or sink streams) + +protected slots: + void handleInputMessages(); + +protected: + std::vector m_sampleSourceFifos; //!< Tx FIFOs + std::vector m_sampleSinkFifos; //!< Rx FIFOs + MessageQueue m_inputMessageQueue; //!< Input queue to the sink + MessageQueue *m_guiMessageQueue; //!< Input message queue to the GUI +}; + +#endif // SDRBASE_DSP_DEVICESAMPLEMIMO_H_ \ No newline at end of file diff --git a/sdrbase/dsp/dspdevicemimoengine.cpp b/sdrbase/dsp/dspdevicemimoengine.cpp new file mode 100644 index 000000000..3f6390d31 --- /dev/null +++ b/sdrbase/dsp/dspdevicemimoengine.cpp @@ -0,0 +1,940 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "dsp/dspcommands.h" +#include "threadedbasebandsamplesource.h" +#include "threadedbasebandsamplesink.h" +#include "devicesamplemimo.h" + +#include "dspdevicemimoengine.h" + +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::SetSampleMIMO, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::AddSourceStream, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::RemoveLastSourceStream, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::AddSinkStream, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::RemoveLastSinkStream, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::AddThreadedBasebandSampleSource, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::RemoveThreadedBasebandSampleSource, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::AddThreadedBasebandSampleSink, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::RemoveThreadedBasebandSampleSink, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::AddBasebandSampleSink, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::RemoveBasebandSampleSink, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::AddSpectrumSink, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::RemoveSpectrumSink, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::GetErrorMessage, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::GetMIMODeviceDescription, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::ConfigureCorrection, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::SignalNotification, Message) +MESSAGE_CLASS_DEFINITION(DSPDeviceMIMOEngine::SetSpectrumSinkInput, Message) + +DSPDeviceMIMOEngine::DSPDeviceMIMOEngine(uint32_t uid, QObject* parent) : + QThread(parent), + m_uid(uid), + m_state(StNotStarted), + m_deviceSampleMIMO(nullptr), + m_spectrumInputSourceElseSink(true), + m_spectrumInputIndex(0) +{ + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); + connect(&m_syncMessenger, SIGNAL(messageSent()), this, SLOT(handleSynchronousMessages()), Qt::QueuedConnection); + + moveToThread(this); +} + +DSPDeviceMIMOEngine::~DSPDeviceMIMOEngine() +{ + stop(); + wait(); +} + +void DSPDeviceMIMOEngine::run() +{ + qDebug() << "DSPDeviceMIMOEngine::run"; + m_state = StIdle; + exec(); +} + +void DSPDeviceMIMOEngine::start() +{ + qDebug() << "DSPDeviceMIMOEngine::start"; + QThread::start(); +} + +void DSPDeviceMIMOEngine::stop() +{ + qDebug() << "DSPDeviceMIMOEngine::stop"; + gotoIdle(); + m_state = StNotStarted; + QThread::exit(); +} + +bool DSPDeviceMIMOEngine::initProcess() +{ + qDebug() << "DSPDeviceMIMOEngine::initGeneration"; + DSPGenerationInit cmd; + + return m_syncMessenger.sendWait(cmd) == StReady; +} + +bool DSPDeviceMIMOEngine::startProcess() +{ + qDebug() << "DSPDeviceMIMOEngine::startGeneration"; + DSPGenerationStart cmd; + + return m_syncMessenger.sendWait(cmd) == StRunning; +} + +void DSPDeviceMIMOEngine::stopProcess() +{ + qDebug() << "DSPDeviceMIMOEngine::stopGeneration"; + DSPGenerationStop cmd; + m_syncMessenger.storeMessage(cmd); + handleSynchronousMessages(); +} + +void DSPDeviceMIMOEngine::setMIMO(DeviceSampleMIMO* mimo) +{ + qDebug() << "DSPDeviceMIMOEngine::setMIMO"; + SetSampleMIMO cmd(mimo); + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::setMIMOSequence(int sequence) +{ + qDebug("DSPDeviceMIMOEngine::setSinkSequence: seq: %d", sequence); + m_sampleMIMOSequence = sequence; +} + +void DSPDeviceMIMOEngine::addSourceStream() +{ + qDebug("DSPDeviceMIMOEngine::addSourceStream"); + AddSourceStream cmd; + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::removeLastSourceStream() +{ + qDebug("DSPDeviceMIMOEngine::removeLastSourceStream"); + RemoveLastSourceStream cmd; + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::addSinkStream() +{ + qDebug("DSPDeviceMIMOEngine::addSinkStream"); + AddSinkStream cmd; + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::removeLastSinkStream() +{ + qDebug("DSPDeviceMIMOEngine::removeLastSinkStream"); + RemoveLastSourceStream cmd; + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::addChannelSource(ThreadedBasebandSampleSource* source, int index) +{ + qDebug() << "DSPDeviceMIMOEngine::addThreadedSource: " + << source->objectName().toStdString().c_str() + << " at: " + << index; + AddThreadedBasebandSampleSource cmd(source, index); + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::removeChannelSource(ThreadedBasebandSampleSource* source, int index) +{ + qDebug() << "DSPDeviceMIMOEngine::removeThreadedSource: " + << source->objectName().toStdString().c_str() + << " at: " + << index; + RemoveThreadedBasebandSampleSource cmd(source, index); + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::addChannelSink(ThreadedBasebandSampleSink* sink, int index) +{ + qDebug() << "DSPDeviceMIMOEngine::addThreadedSink: " + << sink->objectName().toStdString().c_str() + << " at: " + << index; + AddThreadedBasebandSampleSink cmd(sink, index); + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::removeChannelSink(ThreadedBasebandSampleSink* sink, int index) +{ + qDebug() << "DSPDeviceMIMOEngine::removeThreadedSink: " + << sink->objectName().toStdString().c_str() + << " at: " + << index; + RemoveThreadedBasebandSampleSink cmd(sink, index); + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::addAncillarySink(BasebandSampleSink* sink, int index) +{ + qDebug() << "DSPDeviceMIMOEngine::addSink: " + << sink->objectName().toStdString().c_str() + << " at: " + << index; + AddBasebandSampleSink cmd(sink, index); + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::removeAncillarySink(BasebandSampleSink* sink, int index) +{ + qDebug() << "DSPDeviceMIMOEngine::removeSink: " + << sink->objectName().toStdString().c_str() + << " at: " + << index; + RemoveBasebandSampleSink cmd(sink, index); + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::addSpectrumSink(BasebandSampleSink* spectrumSink) +{ + qDebug() << "DSPDeviceMIMOEngine::addSpectrumSink: " << spectrumSink->objectName().toStdString().c_str(); + AddSpectrumSink cmd(spectrumSink); + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::removeSpectrumSink(BasebandSampleSink* spectrumSink) +{ + qDebug() << "DSPDeviceSinkEngine::removeSpectrumSink: " << spectrumSink->objectName().toStdString().c_str(); + DSPRemoveSpectrumSink cmd(spectrumSink); + m_syncMessenger.sendWait(cmd); +} + +void DSPDeviceMIMOEngine::setSpectrumSinkInput(bool sourceElseSink, int index) +{ + qDebug() << "DSPDeviceSinkEngine::setSpectrumSinkInput: " + << " sourceElseSink: " << sourceElseSink + << " index: " << index; + SetSpectrumSinkInput cmd(sourceElseSink, index); + m_syncMessenger.sendWait(cmd); +} + +QString DSPDeviceMIMOEngine::errorMessage() +{ + qDebug() << "DSPDeviceMIMOEngine::errorMessage"; + GetErrorMessage cmd; + m_syncMessenger.sendWait(cmd); + return cmd.getErrorMessage(); +} + +QString DSPDeviceMIMOEngine::deviceDescription() +{ + qDebug() << "DSPDeviceMIMOEngine::deviceDescription"; + GetMIMODeviceDescription cmd; + m_syncMessenger.sendWait(cmd); + return cmd.getDeviceDescription(); +} + +/** + * Routes samples from device source FIFO to sink channels that are registered for the FIFO + * Routes samples from source channels registered for the FIFO to the device sink FIFO + */ +void DSPDeviceMIMOEngine::work(int nbWriteSamples) +{ + (void) nbWriteSamples; + // Sources + for (unsigned int isource = 0; isource < m_deviceSampleMIMO->getNbSourceStreams(); isource++) + { + SampleSinkFifo* sampleFifo = m_deviceSampleMIMO->getSampleSinkFifo(isource); // sink FIFO is for Rx + int samplesDone = 0; + bool positiveOnly = false; + + while ((sampleFifo->fill() > 0) && (m_inputMessageQueue.size() == 0) && (samplesDone < m_deviceSampleMIMO->getSourceSampleRate(isource))) + { + SampleVector::iterator part1begin; + SampleVector::iterator part1end; + SampleVector::iterator part2begin; + SampleVector::iterator part2end; + + std::size_t count = sampleFifo->readBegin(sampleFifo->fill(), &part1begin, &part1end, &part2begin, &part2end); + + // first part of FIFO data + if (part1begin != part1end) + { + // TODO: DC and IQ corrections + + // feed data to direct sinks + if (isource < m_basebandSampleSinks.size()) + { + for (BasebandSampleSinks::const_iterator it = m_basebandSampleSinks[isource].begin(); it != m_basebandSampleSinks[isource].end(); ++it) { + (*it)->feed(part1begin, part1end, positiveOnly); + } + } + + // possibly feed data to spectrum sink + if ((m_spectrumSink) && (m_spectrumInputSourceElseSink) && (isource == m_spectrumInputIndex)) { + m_spectrumSink->feed(part1begin, part1end, positiveOnly); + } + + // feed data to threaded sinks + if (isource < m_threadedBasebandSampleSinks.size()) + { + for (ThreadedBasebandSampleSinks::const_iterator it = m_threadedBasebandSampleSinks[isource].begin(); it != m_threadedBasebandSampleSinks[isource].end(); ++it) { + (*it)->feed(part1begin, part1end, positiveOnly); + } + } + } + + // second part of FIFO data (used when block wraps around) + if(part2begin != part2end) + { + // TODO: DC and IQ corrections + + // feed data to direct sinks + if (isource < m_basebandSampleSinks.size()) + { + for (BasebandSampleSinks::const_iterator it = m_basebandSampleSinks[isource].begin(); it != m_basebandSampleSinks[isource].end(); ++it) { + (*it)->feed(part2begin, part2end, positiveOnly); + } + } + + // possibly feed data to spectrum sink + if ((m_spectrumSink) && (m_spectrumInputSourceElseSink) && (isource == m_spectrumInputIndex)) { + m_spectrumSink->feed(part2begin, part2end, positiveOnly); + } + + // feed data to threaded sinks + if (isource < m_threadedBasebandSampleSinks.size()) + { + for (ThreadedBasebandSampleSinks::const_iterator it = m_threadedBasebandSampleSinks[isource].begin(); it != m_threadedBasebandSampleSinks[isource].end(); ++it) { + (*it)->feed(part2begin, part2end, positiveOnly); + } + } + } + + // adjust FIFO pointers + sampleFifo->readCommit((unsigned int) count); + samplesDone += count; + } // while stream FIFO + } // for stream source + + // TODO: sinks +} + +// notStarted -> idle -> init -> running -+ +// ^ | +// +-----------------------+ + +DSPDeviceMIMOEngine::State DSPDeviceMIMOEngine::gotoIdle() +{ + qDebug() << "DSPDeviceMIMOEngine::gotoIdle"; + + switch(m_state) { + case StNotStarted: + return StNotStarted; + + case StIdle: + case StError: + return StIdle; + + case StReady: + case StRunning: + break; + } + + if (!m_deviceSampleMIMO) { + return StIdle; + } + + // stop everything + + std::vector::const_iterator vbit = m_basebandSampleSinks.begin(); + + for (; vbit != m_basebandSampleSinks.end(); ++vbit) + { + for (BasebandSampleSinks::const_iterator it = vbit->begin(); it != vbit->end(); ++it) { + (*it)->stop(); + } + } + + std::vector::const_iterator vtit = m_threadedBasebandSampleSinks.begin(); + + for (; vtit != m_threadedBasebandSampleSinks.end(); vtit++) + { + for (ThreadedBasebandSampleSinks::const_iterator it = vtit->begin(); it != vtit->end(); ++it) { + (*it)->stop(); + } + } + + m_deviceSampleMIMO->stop(); + m_deviceDescription.clear(); + + return StIdle; +} + +DSPDeviceMIMOEngine::State DSPDeviceMIMOEngine::gotoInit() +{ + switch(m_state) { + case StNotStarted: + return StNotStarted; + + case StRunning: // FIXME: assumes it goes first through idle state. Could we get back to init from running directly? + return StRunning; + + case StReady: + return StReady; + + case StIdle: + case StError: + break; + } + + if (!m_deviceSampleMIMO) { + return gotoError("No sample MIMO configured"); + } + + // init: pass sample rate and center frequency to all sample rate and/or center frequency dependent sinks and wait for completion + + + m_deviceDescription = m_deviceSampleMIMO->getDeviceDescription(); + + qDebug() << "DSPDeviceMIMOEngine::gotoInit: " + << " m_deviceDescription: " << m_deviceDescription.toStdString().c_str(); + + // Rx + + for (unsigned int isource = 0; isource < m_deviceSampleMIMO->getNbSinkFifos(); isource++) + { + if (isource < m_sourcesCorrections.size()) + { + m_sourcesCorrections[isource].m_iOffset = 0; + m_sourcesCorrections[isource].m_qOffset = 0; + m_sourcesCorrections[isource].m_iRange = 1 << 16; + m_sourcesCorrections[isource].m_qRange = 1 << 16; + } + + quint64 sourceCenterFrequency = m_deviceSampleMIMO->getSourceCenterFrequency(isource); + int sourceStreamSampleRate = m_deviceSampleMIMO->getSourceSampleRate(isource); + + qDebug("DSPDeviceMIMOEngine::gotoInit: m_sourceCenterFrequencies[%d] = %llu", isource, sourceCenterFrequency); + qDebug("DSPDeviceMIMOEngine::gotoInit: m_sourceStreamSampleRates[%d] = %d", isource, sourceStreamSampleRate); + + DSPSignalNotification notif(sourceStreamSampleRate, sourceCenterFrequency); + + if (isource < m_basebandSampleSinks.size()) + { + for (BasebandSampleSinks::const_iterator it = m_basebandSampleSinks[isource].begin(); it != m_basebandSampleSinks[isource].end(); ++it) + { + qDebug() << "DSPDeviceMIMOEngine::gotoInit: initializing " << (*it)->objectName().toStdString().c_str(); + (*it)->handleMessage(notif); + } + } + + if (isource < m_threadedBasebandSampleSinks.size()) + { + for (ThreadedBasebandSampleSinks::const_iterator it = m_threadedBasebandSampleSinks[isource].begin(); it != m_threadedBasebandSampleSinks[isource].end(); ++it) + { + qDebug() << "DSPDeviceMIMOEngine::gotoInit: initializing ThreadedSampleSink(" << (*it)->getSampleSinkObjectName().toStdString().c_str() << ")"; + (*it)->handleSinkMessage(notif); + } + } + + // Probably not necessary + // // possibly forward to spectrum sink + // if ((m_spectrumSink) && (m_spectrumInputSourceElseSink) && (isource == m_spectrumInputIndex)) { + // m_spectrumSink->handleMessage(notif); + // } + + // // forward changes to MIMO GUI input queue + // MessageQueue *guiMessageQueue = m_deviceSampleMIMO->getMessageQueueToGUI(); + + // if (guiMessageQueue) { + // SignalNotification* rep = new SignalNotification(sourceStreamSampleRate, sourceCenterFrequency, true, isource); // make a copy for the MIMO GUI + // guiMessageQueue->push(rep); + // } + } + + //TODO: Tx + + return StReady; +} + +DSPDeviceMIMOEngine::State DSPDeviceMIMOEngine::gotoRunning() +{ + qDebug() << "DSPDeviceMIMOEngine::gotoRunning"; + + switch(m_state) + { + case StNotStarted: + return StNotStarted; + + case StIdle: + return StIdle; + + case StRunning: + return StRunning; + + case StReady: + case StError: + break; + } + + if (!m_deviceSampleMIMO) { + return gotoError("DSPDeviceMIMOEngine::gotoRunning: No sample source configured"); + } + + qDebug() << "DSPDeviceMIMOEngine::gotoRunning: " << m_deviceDescription.toStdString().c_str() << " started"; + + // Start everything + + if (!m_deviceSampleMIMO->start()) { + return gotoError("Could not start sample source"); + } + + std::vector::const_iterator vbit = m_basebandSampleSinks.begin(); + + for (; vbit != m_basebandSampleSinks.end(); ++vbit) + { + for (BasebandSampleSinks::const_iterator it = vbit->begin(); it != vbit->end(); ++it) + { + qDebug() << "DSPDeviceMIMOEngine::gotoRunning: starting " << (*it)->objectName().toStdString().c_str(); + (*it)->start(); + } + } + + std::vector::const_iterator vtit = m_threadedBasebandSampleSinks.begin(); + + for (; vtit != m_threadedBasebandSampleSinks.end(); vtit++) + { + for (ThreadedBasebandSampleSinks::const_iterator it = vtit->begin(); it != vtit->end(); ++it) + { + qDebug() << "DSPDeviceMIMOEngine::gotoRunning: starting ThreadedSampleSink(" << (*it)->getSampleSinkObjectName().toStdString().c_str() << ")"; + (*it)->start(); + } + } + + qDebug() << "DSPDeviceMIMOEngine::gotoRunning:input message queue pending: " << m_inputMessageQueue.size(); + + return StRunning; +} + +DSPDeviceMIMOEngine::State DSPDeviceMIMOEngine::gotoError(const QString& errorMessage) +{ + qDebug() << "DSPDeviceMIMOEngine::gotoError: " << errorMessage; + + m_errorMessage = errorMessage; + m_deviceDescription.clear(); + m_state = StError; + return StError; +} + +void DSPDeviceMIMOEngine::handleData() +{ + if(m_state == StRunning) + { + work(0); // TODO: implement Tx side + } +} + +void DSPDeviceMIMOEngine::handleSetMIMO(DeviceSampleMIMO* mimo) +{ + m_deviceSampleMIMO = mimo; + + if (mimo && (mimo->getNbSinkFifos() > 0)) + { + // if there is at least one Rx then the first Rx drives the FIFOs + qDebug("DSPDeviceMIMOEngine::handleSetMIMO: set %s", qPrintable(mimo->getDeviceDescription())); + connect(m_deviceSampleMIMO->getSampleSinkFifo(0), SIGNAL(dataReady()), this, SLOT(handleData()), Qt::QueuedConnection); + } + + // TODO: only Tx +} + +void DSPDeviceMIMOEngine::handleSynchronousMessages() +{ + Message *message = m_syncMessenger.getMessage(); + qDebug() << "DSPDeviceMIMOEngine::handleSynchronousMessages: " << message->getIdentifier(); + + if (DSPGenerationInit::match(*message)) + { + m_state = gotoIdle(); + + if(m_state == StIdle) { + m_state = gotoInit(); // State goes ready if init is performed + } + } + else if (DSPGenerationStart::match(*message)) + { + if(m_state == StReady) { + m_state = gotoRunning(); + } + } + else if (DSPGenerationStop::match(*message)) + { + m_state = gotoIdle(); + } + else if (GetMIMODeviceDescription::match(*message)) + { + ((GetMIMODeviceDescription*) message)->setDeviceDescription(m_deviceDescription); + } + else if (DSPGetErrorMessage::match(*message)) + { + ((DSPGetErrorMessage*) message)->setErrorMessage(m_errorMessage); + } + else if (SetSampleMIMO::match(*message)) { + handleSetMIMO(((SetSampleMIMO*) message)->getSampleMIMO()); + } + else if (AddSourceStream::match(*message)) + { + m_basebandSampleSinks.push_back(BasebandSampleSinks()); + m_threadedBasebandSampleSinks.push_back(ThreadedBasebandSampleSinks()); + m_sourcesCorrections.push_back(SourceCorrection()); + } + else if (RemoveLastSourceStream::match(*message)) + { + m_basebandSampleSinks.pop_back(); + m_threadedBasebandSampleSinks.pop_back(); + } + else if (AddSinkStream::match(*message)) + { + m_threadedBasebandSampleSources.push_back(ThreadedBasebandSampleSources()); + } + else if (RemoveLastSinkStream::match(*message)) + { + m_threadedBasebandSampleSources.pop_back(); + } + else if (AddBasebandSampleSink::match(*message)) + { + const AddBasebandSampleSink *msg = (AddBasebandSampleSink *) message; + BasebandSampleSink* sink = msg->getSampleSink(); + unsigned int isource = msg->getIndex(); + + if (isource < m_basebandSampleSinks.size()) + { + m_basebandSampleSinks[isource].push_back(sink); + // initialize sample rate and center frequency in the sink: + int sourceStreamSampleRate = m_deviceSampleMIMO->getSourceSampleRate(isource); + quint64 sourceCenterFrequency = m_deviceSampleMIMO->getSourceCenterFrequency(isource); + DSPSignalNotification msg(sourceStreamSampleRate, sourceCenterFrequency); + sink->handleMessage(msg); + // start the sink: + if(m_state == StRunning) { + sink->start(); + } + } + } + else if (RemoveBasebandSampleSink::match(*message)) + { + const RemoveBasebandSampleSink *msg = (RemoveBasebandSampleSink *) message; + BasebandSampleSink* sink = ((DSPRemoveBasebandSampleSink*) message)->getSampleSink(); + unsigned int isource = msg->getIndex(); + + if (isource < m_basebandSampleSinks.size()) + { + if(m_state == StRunning) { + sink->stop(); + } + + m_basebandSampleSinks[isource].remove(sink); + } + } + else if (AddThreadedBasebandSampleSink::match(*message)) + { + const AddThreadedBasebandSampleSink *msg = (AddThreadedBasebandSampleSink *) message; + ThreadedBasebandSampleSink *threadedSink = msg->getThreadedSampleSink(); + unsigned int isource = msg->getIndex(); + + if (isource < m_threadedBasebandSampleSinks.size()) + { + m_threadedBasebandSampleSinks[isource].push_back(threadedSink); + // initialize sample rate and center frequency in the sink: + int sourceStreamSampleRate = m_deviceSampleMIMO->getSourceSampleRate(isource); + quint64 sourceCenterFrequency = m_deviceSampleMIMO->getSourceCenterFrequency(isource); + DSPSignalNotification msg(sourceStreamSampleRate, sourceCenterFrequency); + threadedSink->handleSinkMessage(msg); + // start the sink: + if(m_state == StRunning) { + threadedSink->start(); + } + } + } + else if (RemoveThreadedBasebandSampleSink::match(*message)) + { + const RemoveThreadedBasebandSampleSink *msg = (RemoveThreadedBasebandSampleSink *) message; + ThreadedBasebandSampleSink* threadedSink = msg->getThreadedSampleSink(); + unsigned int isource = msg->getIndex(); + + if (isource < m_threadedBasebandSampleSinks.size()) + { + threadedSink->stop(); + m_threadedBasebandSampleSinks[isource].remove(threadedSink); + } + } + else if (AddThreadedBasebandSampleSource::match(*message)) + { + const AddThreadedBasebandSampleSource *msg = (AddThreadedBasebandSampleSource *) message; + ThreadedBasebandSampleSource *threadedSource = msg->getThreadedSampleSource(); + unsigned int isink = msg->getIndex(); + + if (isink < m_threadedBasebandSampleSources.size()) + { + m_threadedBasebandSampleSources[isink].push_back(threadedSource); + // initialize sample rate and center frequency in the sink: + int sinkStreamSampleRate = m_deviceSampleMIMO->getSinkSampleRate(isink); + quint64 sinkCenterFrequency = m_deviceSampleMIMO->getSinkCenterFrequency(isink); + DSPSignalNotification msg(sinkStreamSampleRate, sinkCenterFrequency); + threadedSource->handleSourceMessage(msg); + // start the sink: + if(m_state == StRunning) { + threadedSource->start(); + } + } + } + else if (RemoveThreadedBasebandSampleSource::match(*message)) + { + const RemoveThreadedBasebandSampleSource *msg = (RemoveThreadedBasebandSampleSource *) message; + ThreadedBasebandSampleSource* threadedSource = msg->getThreadedSampleSource(); + unsigned int isink = msg->getIndex(); + + if (isink < m_threadedBasebandSampleSources.size()) + { + threadedSource->stop(); + m_threadedBasebandSampleSources[isink].remove(threadedSource); + } + } + else if (AddSpectrumSink::match(*message)) + { + m_spectrumSink = ((AddSpectrumSink*) message)->getSampleSink(); + } + else if (RemoveSpectrumSink::match(*message)) + { + BasebandSampleSink* spectrumSink = ((DSPRemoveSpectrumSink*) message)->getSampleSink(); + spectrumSink->stop(); + + if (!m_spectrumInputSourceElseSink && m_deviceSampleMIMO && (m_spectrumInputIndex < m_deviceSampleMIMO->getNbSinkStreams())) + { + SampleSourceFifo *inputFIFO = m_deviceSampleMIMO->getSampleSourceFifo(m_spectrumInputIndex); + disconnect(inputFIFO, SIGNAL(dataRead(int)), this, SLOT(handleForwardToSpectrumSink(int))); + } + + m_spectrumSink = nullptr; + } + else if (SetSpectrumSinkInput::match(*message)) + { + const SetSpectrumSinkInput *msg = (SetSpectrumSinkInput *) message; + bool spectrumInputSourceElseSink = msg->getSourceElseSink(); + unsigned int spectrumInputIndex = msg->getIndex(); + + if ((spectrumInputSourceElseSink != m_spectrumInputSourceElseSink) || (spectrumInputIndex != m_spectrumInputIndex)) + { + if (!m_spectrumInputSourceElseSink) // remove the source listener + { + SampleSourceFifo *inputFIFO = m_deviceSampleMIMO->getSampleSourceFifo(m_spectrumInputIndex); + disconnect(inputFIFO, SIGNAL(dataRead(int)), this, SLOT(handleForwardToSpectrumSink(int))); + } + + if ((!spectrumInputSourceElseSink) && (spectrumInputIndex < m_deviceSampleMIMO->getNbSinkStreams())) // add the source listener + { + SampleSourceFifo *inputFIFO = m_deviceSampleMIMO->getSampleSourceFifo(spectrumInputIndex); + connect(inputFIFO, SIGNAL(dataRead(int)), this, SLOT(handleForwardToSpectrumSink(int))); + + if (m_spectrumSink) + { + DSPSignalNotification notif( + m_deviceSampleMIMO->getSinkSampleRate(spectrumInputIndex), + m_deviceSampleMIMO->getSinkCenterFrequency(spectrumInputIndex)); + m_spectrumSink->handleMessage(notif); + } + } + + if (m_spectrumSink && (spectrumInputSourceElseSink) && (spectrumInputIndex < m_deviceSampleMIMO->getNbSinkFifos())) + { + DSPSignalNotification notif( + m_deviceSampleMIMO->getSourceSampleRate(spectrumInputIndex), + m_deviceSampleMIMO->getSourceCenterFrequency(spectrumInputIndex)); + m_spectrumSink->handleMessage(notif); + } + + m_spectrumInputSourceElseSink = spectrumInputSourceElseSink; + m_spectrumInputIndex = spectrumInputIndex; + } + } + + m_syncMessenger.done(m_state); +} + +void DSPDeviceMIMOEngine::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != 0) + { + qDebug("DSPDeviceMIMOEngine::handleInputMessages: message: %s", message->getIdentifier()); + + if (ConfigureCorrection::match(*message)) + { + ConfigureCorrection* conf = (ConfigureCorrection*) message; + unsigned int isource = conf->getIndex(); + + if (isource < m_sourcesCorrections.size()) + { + m_sourcesCorrections[isource].m_iqImbalanceCorrection = conf->getIQImbalanceCorrection(); + + if (m_sourcesCorrections[isource].m_dcOffsetCorrection != conf->getDCOffsetCorrection()) + { + m_sourcesCorrections[isource].m_dcOffsetCorrection = conf->getDCOffsetCorrection(); + m_sourcesCorrections[isource].m_iOffset = 0; + m_sourcesCorrections[isource].m_qOffset = 0; + + if (m_sourcesCorrections[isource].m_iqImbalanceCorrection != conf->getIQImbalanceCorrection()) + { + m_sourcesCorrections[isource].m_iqImbalanceCorrection = conf->getIQImbalanceCorrection(); + m_sourcesCorrections[isource].m_iRange = 1 << 16; + m_sourcesCorrections[isource].m_qRange = 1 << 16; + m_sourcesCorrections[isource].m_imbalance = 65536; + } + } + m_sourcesCorrections[isource].m_iBeta.reset(); + m_sourcesCorrections[isource].m_qBeta.reset(); + m_sourcesCorrections[isource].m_avgAmp.reset(); + m_sourcesCorrections[isource].m_avgII.reset(); + m_sourcesCorrections[isource].m_avgII2.reset(); + m_sourcesCorrections[isource].m_avgIQ.reset(); + m_sourcesCorrections[isource].m_avgPhi.reset(); + m_sourcesCorrections[isource].m_avgQQ2.reset(); + m_sourcesCorrections[isource].m_iBeta.reset(); + m_sourcesCorrections[isource].m_qBeta.reset(); + } + + delete message; + } + else if (SignalNotification::match(*message)) + { + SignalNotification *notif = (SignalNotification *) message; + + // update DSP values + + bool sourceElseSink = notif->getSourceOrSink(); + unsigned int istream = notif->getIndex(); + int sampleRate = notif->getSampleRate(); + qint64 centerFrequency = notif->getCenterFrequency(); + + qDebug() << "DeviceMIMOEngine::handleInputMessages: SignalNotification:" + << " sourceElseSink: " << sourceElseSink + << " istream: " << istream + << " sampleRate: " << sampleRate + << " centerFrequency: " << centerFrequency; + + + if (sourceElseSink) + { + if ((istream < m_deviceSampleMIMO->getNbSourceStreams())) + { + DSPSignalNotification *message = new DSPSignalNotification(sampleRate, centerFrequency); + + // forward source changes to ancillary sinks with immediate execution (no queuing) + if (istream < m_basebandSampleSinks.size()) + { + for (BasebandSampleSinks::const_iterator it = m_basebandSampleSinks[istream].begin(); it != m_basebandSampleSinks[istream].end(); ++it) + { + qDebug() << "DSPDeviceMIMOEngine::gotoRunning: starting " << (*it)->objectName().toStdString().c_str(); + (*it)->handleMessage(*message); + } + } + + // forward source changes to channel sinks with immediate execution (no queuing) + if (istream < m_threadedBasebandSampleSinks.size()) + { + for (ThreadedBasebandSampleSinks::const_iterator it = m_threadedBasebandSampleSinks[istream].begin(); it != m_threadedBasebandSampleSinks[istream].end(); ++it) + { + qDebug() << "DSPDeviceMIMOEngine::handleSourceMessages: forward message to ThreadedSampleSink(" << (*it)->getSampleSinkObjectName().toStdString().c_str() << ")"; + (*it)->handleSinkMessage(*message); + } + } + + // forward changes to MIMO GUI input queue + MessageQueue *guiMessageQueue = m_deviceSampleMIMO->getMessageQueueToGUI(); + qDebug("DeviceMIMOEngine::handleInputMessages: SignalNotification: guiMessageQueue: %p", guiMessageQueue); + + if (guiMessageQueue) { + SignalNotification* rep = new SignalNotification(*notif); // make a copy for the MIMO GUI + guiMessageQueue->push(rep); + } + + if (m_spectrumSink && m_spectrumInputSourceElseSink && (m_spectrumInputIndex == istream)) + { + DSPSignalNotification spectrumNotif(sampleRate, centerFrequency); + m_spectrumSink->handleMessage(spectrumNotif); + } + } + } + else + { + if ((istream < m_deviceSampleMIMO->getNbSinkStreams())) + { + DSPSignalNotification *message = new DSPSignalNotification(sampleRate, centerFrequency); + + // forward source changes to channel sources with immediate execution (no queuing) + if (istream < m_threadedBasebandSampleSources.size()) + { + for (ThreadedBasebandSampleSources::const_iterator it = m_threadedBasebandSampleSources[istream].begin(); it != m_threadedBasebandSampleSources[istream].end(); ++it) + { + qDebug() << "DSPDeviceMIMOEngine::handleSinkMessages: forward message to ThreadedSampleSource(" << (*it)->getSampleSourceObjectName().toStdString().c_str() << ")"; + (*it)->handleSourceMessage(*message); + } + } + + // forward changes to MIMO GUI input queue + MessageQueue *guiMessageQueue = m_deviceSampleMIMO->getMessageQueueToGUI(); + qDebug("DSPDeviceMIMOEngine::handleInputMessages: DSPSignalNotification: guiMessageQueue: %p", guiMessageQueue); + + if (guiMessageQueue) { + SignalNotification* rep = new SignalNotification(*notif); // make a copy for the source GUI + guiMessageQueue->push(rep); + } + + if (m_spectrumSink && !m_spectrumInputSourceElseSink && (m_spectrumInputIndex == istream)) + { + DSPSignalNotification spectrumNotif(sampleRate, centerFrequency); + m_spectrumSink->handleMessage(spectrumNotif); + } + } + } + + delete message; + } + } +} + +void DSPDeviceMIMOEngine::configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection, int isource) +{ + qDebug() << "DSPDeviceMIMOEngine::configureCorrections"; + ConfigureCorrection* cmd = new ConfigureCorrection(dcOffsetCorrection, iqImbalanceCorrection, isource); + m_inputMessageQueue.push(cmd); +} + +// This is used for the Tx (sink streams) side +void DSPDeviceMIMOEngine::handleForwardToSpectrumSink(int nbSamples) +{ + if ((m_spectrumSink) && (m_spectrumInputIndex < m_deviceSampleMIMO->getNbSinkStreams())) + { + SampleSourceFifo* sampleFifo = m_deviceSampleMIMO->getSampleSourceFifo(m_spectrumInputIndex); + SampleVector::iterator readUntil; + sampleFifo->getReadIterator(readUntil); + m_spectrumSink->feed(readUntil - nbSamples, readUntil, false); + } +} diff --git a/sdrbase/dsp/dspdevicemimoengine.h b/sdrbase/dsp/dspdevicemimoengine.h new file mode 100644 index 000000000..fd7f3a299 --- /dev/null +++ b/sdrbase/dsp/dspdevicemimoengine.h @@ -0,0 +1,388 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_DSP_DSPDEVICEMIMOENGINE_H_ +#define SDRBASE_DSP_DSPDEVICEMIMOENGINE_H_ + +#include + +#include "util/message.h" +#include "util/messagequeue.h" +#include "util/syncmessenger.h" +#include "util/movingaverage.h" +#include "export.h" + +class DeviceSampleMIMO; +class ThreadedBasebandSampleSource; +class ThreadedBasebandSampleSink; +class BasebandSampleSink; + +class SDRBASE_API DSPDeviceMIMOEngine : public QThread { + Q_OBJECT + +public: + class SetSampleMIMO : public Message { + MESSAGE_CLASS_DECLARATION + public: + SetSampleMIMO(DeviceSampleMIMO* sampleMIMO) : Message(), m_sampleMIMO(sampleMIMO) { } + DeviceSampleMIMO* getSampleMIMO() const { return m_sampleMIMO; } + private: + DeviceSampleMIMO* m_sampleMIMO; + }; + + class AddThreadedBasebandSampleSource : public Message { + MESSAGE_CLASS_DECLARATION + public: + AddThreadedBasebandSampleSource(ThreadedBasebandSampleSource* threadedSampleSource, unsigned int index) : + Message(), + m_threadedSampleSource(threadedSampleSource), + m_index(index) + { } + ThreadedBasebandSampleSource* getThreadedSampleSource() const { return m_threadedSampleSource; } + unsigned int getIndex() const { return m_index; } + private: + ThreadedBasebandSampleSource* m_threadedSampleSource; + unsigned int m_index; + }; + + class AddSourceStream : public Message { + MESSAGE_CLASS_DECLARATION + }; + + class RemoveLastSourceStream : public Message { + MESSAGE_CLASS_DECLARATION + }; + + class AddSinkStream : public Message { + MESSAGE_CLASS_DECLARATION + }; + + class RemoveLastSinkStream : public Message { + MESSAGE_CLASS_DECLARATION + }; + + class RemoveThreadedBasebandSampleSource : public Message { + MESSAGE_CLASS_DECLARATION + + public: + RemoveThreadedBasebandSampleSource(ThreadedBasebandSampleSource* threadedSampleSource, unsigned int index) : + Message(), + m_threadedSampleSource(threadedSampleSource), + m_index(index) + { } + ThreadedBasebandSampleSource* getThreadedSampleSource() const { return m_threadedSampleSource; } + unsigned int getIndex() const { return m_index; } + private: + ThreadedBasebandSampleSource* m_threadedSampleSource; + unsigned int m_index; + }; + + class AddThreadedBasebandSampleSink : public Message { + MESSAGE_CLASS_DECLARATION + public: + AddThreadedBasebandSampleSink(ThreadedBasebandSampleSink* threadedSampleSink, unsigned int index) : + Message(), + m_threadedSampleSink(threadedSampleSink), + m_index(index) + { } + ThreadedBasebandSampleSink* getThreadedSampleSink() const { return m_threadedSampleSink; } + unsigned int getIndex() const { return m_index; } + private: + ThreadedBasebandSampleSink* m_threadedSampleSink; + unsigned int m_index; + }; + + class RemoveThreadedBasebandSampleSink : public Message { + MESSAGE_CLASS_DECLARATION + public: + RemoveThreadedBasebandSampleSink(ThreadedBasebandSampleSink* threadedSampleSink, unsigned int index) : + Message(), + m_threadedSampleSink(threadedSampleSink), + m_index(index) + { } + ThreadedBasebandSampleSink* getThreadedSampleSink() const { return m_threadedSampleSink; } + unsigned int getIndex() const { return m_index; } + private: + ThreadedBasebandSampleSink* m_threadedSampleSink; + unsigned int m_index; + }; + + class AddBasebandSampleSink : public Message { + MESSAGE_CLASS_DECLARATION + public: + AddBasebandSampleSink(BasebandSampleSink* sampleSink, unsigned int index) : + Message(), + m_sampleSink(sampleSink), + m_index(index) + { } + BasebandSampleSink* getSampleSink() const { return m_sampleSink; } + unsigned int getIndex() const { return m_index; } + private: + BasebandSampleSink* m_sampleSink; + unsigned int m_index; + }; + + class RemoveBasebandSampleSink : public Message { + MESSAGE_CLASS_DECLARATION + public: + RemoveBasebandSampleSink(BasebandSampleSink* sampleSink, unsigned int index) : + Message(), + m_sampleSink(sampleSink), + m_index(index) + { } + BasebandSampleSink* getSampleSink() const { return m_sampleSink; } + unsigned int getIndex() const { return m_index; } + private: + BasebandSampleSink* m_sampleSink; + unsigned int m_index; + }; + + class AddSpectrumSink : public Message { + MESSAGE_CLASS_DECLARATION + public: + AddSpectrumSink(BasebandSampleSink* sampleSink) : Message(), m_sampleSink(sampleSink) { } + BasebandSampleSink* getSampleSink() const { return m_sampleSink; } + private: + BasebandSampleSink* m_sampleSink; + }; + + class RemoveSpectrumSink : public Message { + MESSAGE_CLASS_DECLARATION + public: + RemoveSpectrumSink(BasebandSampleSink* sampleSink) : Message(), m_sampleSink(sampleSink) { } + BasebandSampleSink* getSampleSink() const { return m_sampleSink; } + private: + BasebandSampleSink* m_sampleSink; + }; + + class GetErrorMessage : public Message { + MESSAGE_CLASS_DECLARATION + public: + void setErrorMessage(const QString& text) { m_errorMessage = text; } + const QString& getErrorMessage() const { return m_errorMessage; } + private: + QString m_errorMessage; + }; + + class GetMIMODeviceDescription : public Message { + MESSAGE_CLASS_DECLARATION + public: + void setDeviceDescription(const QString& text) { m_deviceDescription = text; } + const QString& getDeviceDescription() const { return m_deviceDescription; } + private: + QString m_deviceDescription; + }; + + class ConfigureCorrection : public Message { + MESSAGE_CLASS_DECLARATION + public: + ConfigureCorrection(bool dcOffsetCorrection, bool iqImbalanceCorrection, unsigned int index) : + Message(), + m_dcOffsetCorrection(dcOffsetCorrection), + m_iqImbalanceCorrection(iqImbalanceCorrection), + m_index(index) + { } + bool getDCOffsetCorrection() const { return m_dcOffsetCorrection; } + bool getIQImbalanceCorrection() const { return m_iqImbalanceCorrection; } + unsigned int getIndex() const { return m_index; } + private: + bool m_dcOffsetCorrection; + bool m_iqImbalanceCorrection; + unsigned int m_index; + }; + + class SignalNotification : public Message { + MESSAGE_CLASS_DECLARATION + public: + SignalNotification(int samplerate, qint64 centerFrequency, bool sourceOrSink, unsigned int index) : + Message(), + m_sampleRate(samplerate), + m_centerFrequency(centerFrequency), + m_sourceOrSink(sourceOrSink), + m_index(index) + { } + int getSampleRate() const { return m_sampleRate; } + qint64 getCenterFrequency() const { return m_centerFrequency; } + bool getSourceOrSink() const { return m_sourceOrSink; } + unsigned int getIndex() const { return m_index; } + private: + int m_sampleRate; + qint64 m_centerFrequency; + bool m_sourceOrSink; + unsigned int m_index; + }; + + class SetSpectrumSinkInput : public Message { + MESSAGE_CLASS_DECLARATION + public: + SetSpectrumSinkInput(bool sourceElseSink, int index) : + m_sourceElseSink(sourceElseSink), + m_index(index) + { } + bool getSourceElseSink() const { return m_sourceElseSink; } + int getIndex() const { return m_index; } + private: + bool m_sourceElseSink; + int m_index; + }; + + enum State { + StNotStarted, //!< engine is before initialization + StIdle, //!< engine is idle + StReady, //!< engine is ready to run + StRunning, //!< engine is running + StError //!< engine is in error + }; + + DSPDeviceMIMOEngine(uint32_t uid, QObject* parent = nullptr); + ~DSPDeviceMIMOEngine(); + + MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; } + + void start(); //!< This thread start + void stop(); //!< This thread stop + + bool initProcess(); //!< Initialize process sequence + bool startProcess(); //!< Start process sequence + void stopProcess(); //!< Stop process sequence + + void setMIMO(DeviceSampleMIMO* mimo); //!< Set the sample MIMO type + DeviceSampleMIMO *getMIMO() { return m_deviceSampleMIMO; } + void setMIMOSequence(int sequence); //!< Set the sample MIMO sequence in type + uint getUID() const { return m_uid; } + + void addSourceStream(); + void removeLastSourceStream(); + void addSinkStream(); + void removeLastSinkStream(); + + void addChannelSource(ThreadedBasebandSampleSource* source, int index = 0); //!< Add a channel source that will run on its own thread + void removeChannelSource(ThreadedBasebandSampleSource* source, int index = 0); //!< Remove a channel source that runs on its own thread + void addChannelSink(ThreadedBasebandSampleSink* sink, int index = 0); //!< Add a channel sink that will run on its own thread + void removeChannelSink(ThreadedBasebandSampleSink* sink, int index = 0); //!< Remove a channel sink that runs on its own thread + + void addAncillarySink(BasebandSampleSink* sink, int index = 0); //!< Add an ancillary sink like a I/Q recorder + void removeAncillarySink(BasebandSampleSink* sink, int index = 0); //!< Remove an ancillary sample sink + + void addSpectrumSink(BasebandSampleSink* spectrumSink); //!< Add a spectrum vis baseband sample sink + void removeSpectrumSink(BasebandSampleSink* spectrumSink); //!< Add a spectrum vis baseband sample sink + void setSpectrumSinkInput(bool sourceElseSink, int index); + + State state() const { return m_state; } //!< Return DSP engine current state + + QString errorMessage(); //!< Return the current error message + QString deviceDescription(); //!< Return the device description + + void configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection, int isource); //!< Configure source DSP corrections + +private: + struct SourceCorrection + { + bool m_dcOffsetCorrection; + bool m_iqImbalanceCorrection; + double m_iOffset; + double m_qOffset; + int m_iRange; + int m_qRange; + int m_imbalance; + MovingAverageUtil m_iBeta; + MovingAverageUtil m_qBeta; +#if IMBALANCE_INT + // Fixed point DC + IQ corrections + MovingAverageUtil m_avgII; + MovingAverageUtil m_avgIQ; + MovingAverageUtil m_avgPhi; + MovingAverageUtil m_avgII2; + MovingAverageUtil m_avgQQ2; + MovingAverageUtil m_avgAmp; +#else + // Floating point DC + IQ corrections + MovingAverageUtil m_avgII; + MovingAverageUtil m_avgIQ; + MovingAverageUtil m_avgII2; + MovingAverageUtil m_avgQQ2; + MovingAverageUtil m_avgPhi; + MovingAverageUtil m_avgAmp; +#endif + SourceCorrection() + { + m_dcOffsetCorrection = false; + m_iqImbalanceCorrection = false; + m_iOffset = 0; + m_qOffset = 0; + m_iRange = 1 << 16; + m_qRange = 1 << 16; + m_imbalance = 65536; + m_iBeta.reset(); + m_qBeta.reset(); + m_avgAmp.reset(); + m_avgII.reset(); + m_avgII2.reset(); + m_avgIQ.reset(); + m_avgPhi.reset(); + m_avgQQ2.reset(); + m_iBeta.reset(); + m_qBeta.reset(); + } + }; + + uint32_t m_uid; //!< unique ID + State m_state; + + QString m_errorMessage; + QString m_deviceDescription; + + DeviceSampleMIMO* m_deviceSampleMIMO; + int m_sampleMIMOSequence; + + MessageQueue m_inputMessageQueue; // BasebandSampleSinks; + std::vector m_basebandSampleSinks; //!< ancillary sample sinks on main thread (per input stream) + + typedef std::list ThreadedBasebandSampleSinks; + std::vector m_threadedBasebandSampleSinks; //!< channel sample sinks on their own thread (per input stream) + + typedef std::list ThreadedBasebandSampleSources; + std::vector m_threadedBasebandSampleSources; //!< channel sample sources on their own threads (per output stream) + + std::vector m_sourcesCorrections; + + BasebandSampleSink *m_spectrumSink; //!< The spectrum sink + bool m_spectrumInputSourceElseSink; //!< Source else sink stream to be used as spectrum sink input + unsigned int m_spectrumInputIndex; //!< Index of the stream to be used as spectrum sink input + + void run(); + void work(int nbWriteSamples); //!< transfer samples if in running state + + State gotoIdle(); //!< Go to the idle state + State gotoInit(); //!< Go to the acquisition init state from idle + State gotoRunning(); //!< Go to the running state from ready state + State gotoError(const QString& errorMsg); //!< Go to an error state + + void handleSetMIMO(DeviceSampleMIMO* mimo); //!< Manage MIMO device setting + +private slots: + void handleData(); //!< Handle data when samples have to be processed + void handleSynchronousMessages(); //!< Handle synchronous messages with the thread + void handleInputMessages(); //!< Handle input message queue + void handleForwardToSpectrumSink(int nbSamples); +}; + +#endif // SDRBASE_DSP_DSPDEVICEMIMOENGINE_H_ \ No newline at end of file diff --git a/sdrbase/dsp/dspdevicesinkengine.cpp b/sdrbase/dsp/dspdevicesinkengine.cpp index 8cb999c22..5cfdd641b 100644 --- a/sdrbase/dsp/dspdevicesinkengine.cpp +++ b/sdrbase/dsp/dspdevicesinkengine.cpp @@ -33,10 +33,10 @@ DSPDeviceSinkEngine::DSPDeviceSinkEngine(uint32_t uid, QObject* parent) : QThread(parent), m_uid(uid), m_state(StNotStarted), - m_deviceSampleSink(0), + m_deviceSampleSink(nullptr), m_sampleSinkSequence(0), m_basebandSampleSources(), - m_spectrumSink(0), + m_spectrumSink(nullptr), m_sampleRate(0), m_centerFrequency(0), m_multipleSourcesDivisionFactor(1) @@ -391,12 +391,9 @@ void DSPDeviceSinkEngine::handleSetSink(DeviceSampleSink* sink) m_deviceSampleSink = sink; - if(m_deviceSampleSink != 0) - { + if(m_deviceSampleSink != 0) { qDebug("DSPDeviceSinkEngine::handleSetSink: set %s", qPrintable(sink->getDeviceDescription())); - } - else - { + } else { qDebug("DSPDeviceSinkEngine::handleSetSource: set none"); } } diff --git a/sdrbase/dsp/dspdevicesourceengine.cpp b/sdrbase/dsp/dspdevicesourceengine.cpp index e0e7c9f0c..6f6d43d50 100644 --- a/sdrbase/dsp/dspdevicesourceengine.cpp +++ b/sdrbase/dsp/dspdevicesourceengine.cpp @@ -32,7 +32,7 @@ DSPDeviceSourceEngine::DSPDeviceSourceEngine(uint uid, QObject* parent) : QThread(parent), m_uid(uid), m_state(StNotStarted), - m_deviceSampleSource(0), + m_deviceSampleSource(nullptr), m_sampleSourceSequence(0), m_basebandSampleSinks(), m_sampleRate(0), diff --git a/sdrbase/dsp/dspengine.cpp b/sdrbase/dsp/dspengine.cpp index 9c4960887..4917d520c 100644 --- a/sdrbase/dsp/dspengine.cpp +++ b/sdrbase/dsp/dspengine.cpp @@ -22,15 +22,17 @@ #include "dsp/dspengine.h" #include "dsp/dspdevicesourceengine.h" #include "dsp/dspdevicesinkengine.h" - +#include "dsp/dspdevicemimoengine.h" DSPEngine::DSPEngine() : m_deviceSourceEnginesUIDSequence(0), m_deviceSinkEnginesUIDSequence(0), + m_deviceMIMOEnginesUIDSequence(0), m_audioInputDeviceIndex(-1), // default device m_audioOutputDeviceIndex(-1) // default device { m_dvSerialSupport = false; + m_mimoSupport = false; m_masterTimer.start(50); } @@ -87,21 +89,38 @@ void DSPEngine::removeLastDeviceSinkEngine() } } +DSPDeviceMIMOEngine *DSPEngine::addDeviceMIMOEngine() +{ + m_deviceMIMOEngines.push_back(new DSPDeviceMIMOEngine(m_deviceMIMOEnginesUIDSequence)); + m_deviceMIMOEnginesUIDSequence++; + return m_deviceMIMOEngines.back(); +} + +void DSPEngine::removeLastDeviceMIMOEngine() +{ + if (m_deviceMIMOEngines.size() > 0) + { + DSPDeviceMIMOEngine *lastDeviceEngine = m_deviceMIMOEngines.back(); + delete lastDeviceEngine; + m_deviceMIMOEngines.pop_back(); + m_deviceMIMOEnginesUIDSequence--; + } +} + DSPDeviceSourceEngine *DSPEngine::getDeviceSourceEngineByUID(uint uid) { std::vector::iterator it = m_deviceSourceEngines.begin(); while (it != m_deviceSourceEngines.end()) { - if ((*it)->getUID() == uid) - { + if ((*it)->getUID() == uid) { return *it; } ++it; } - return 0; + return nullptr; } DSPDeviceSinkEngine *DSPEngine::getDeviceSinkEngineByUID(uint uid) @@ -110,15 +129,30 @@ DSPDeviceSinkEngine *DSPEngine::getDeviceSinkEngineByUID(uint uid) while (it != m_deviceSinkEngines.end()) { - if ((*it)->getUID() == uid) - { + if ((*it)->getUID() == uid) { return *it; } ++it; } - return 0; + return nullptr; +} + +DSPDeviceMIMOEngine *DSPEngine::getDeviceMIMOEngineByUID(uint uid) +{ + std::vector::iterator it = m_deviceMIMOEngines.begin(); + + while (it != m_deviceMIMOEngines.end()) + { + if ((*it)->getUID() == uid) { + return *it; + } + + ++it; + } + + return nullptr; } #ifdef DSD_USE_SERIALDV diff --git a/sdrbase/dsp/dspengine.h b/sdrbase/dsp/dspengine.h index e509cf858..1d5d044ae 100644 --- a/sdrbase/dsp/dspengine.h +++ b/sdrbase/dsp/dspengine.h @@ -33,6 +33,7 @@ class DSPDeviceSourceEngine; class DSPDeviceSinkEngine; +class DSPDeviceMIMOEngine; class SDRBASE_API DSPEngine : public QObject { Q_OBJECT @@ -50,6 +51,9 @@ public: DSPDeviceSinkEngine *addDeviceSinkEngine(); void removeLastDeviceSinkEngine(); + DSPDeviceMIMOEngine *addDeviceMIMOEngine(); + void removeLastDeviceMIMOEngine(); + AudioDeviceManager *getAudioDeviceManager() { return &m_audioDeviceManager; } uint32_t getDeviceSourceEnginesNumber() const { return m_deviceSourceEngines.size(); } @@ -60,6 +64,10 @@ public: DSPDeviceSinkEngine *getDeviceSinkEngineByIndex(uint deviceIndex) { return m_deviceSinkEngines[deviceIndex]; } DSPDeviceSinkEngine *getDeviceSinkEngineByUID(uint uid); + uint32_t getDeviceMIMOEnginesNumber() const { return m_deviceMIMOEngines.size(); } + DSPDeviceMIMOEngine *getDeviceMIMOEngineByIndex(uint deviceIndex) { return m_deviceMIMOEngines[deviceIndex]; } + DSPDeviceMIMOEngine *getDeviceMIMOEngineByUID(uint uid); + // Serial DV methods: bool hasDVSerialSupport(); @@ -75,17 +83,22 @@ public: AudioFifo *audioFifo); const QTimer& getMasterTimer() const { return m_masterTimer; } + void setMIMOSupport(bool mimoSupport) { m_mimoSupport = mimoSupport; } + bool getMIMOSupport() const { return m_mimoSupport; } private: std::vector m_deviceSourceEngines; uint m_deviceSourceEnginesUIDSequence; std::vector m_deviceSinkEngines; uint m_deviceSinkEnginesUIDSequence; + std::vector m_deviceMIMOEngines; + uint m_deviceMIMOEnginesUIDSequence; AudioDeviceManager m_audioDeviceManager; int m_audioInputDeviceIndex; int m_audioOutputDeviceIndex; QTimer m_masterTimer; bool m_dvSerialSupport; + bool m_mimoSupport; #ifdef DSD_USE_SERIALDV DVSerialEngine m_dvSerialEngine; #endif diff --git a/sdrbase/dsp/interpolatorsif.h b/sdrbase/dsp/interpolatorsif.h new file mode 100644 index 000000000..a51f0f37d --- /dev/null +++ b/sdrbase/dsp/interpolatorsif.h @@ -0,0 +1,1386 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_SDRBASE_DSP_INTERPOLATORSIF_H_ +#define INCLUDE_SDRBASE_DSP_INTERPOLATORSIF_H_ + +#include "dsp/dsptypes.h" +#ifdef USE_SSE4_1 +#include "dsp/inthalfbandfiltereo1.h" +#else +#include "dsp/inthalfbandfilterdb.h" +#endif + +#define INTERPOLATORS_HB_FILTER_ORDER_FIRST 64 +#define INTERPOLATORS_HB_FILTER_ORDER_SECOND 32 +#define INTERPOLATORS_HB_FILTER_ORDER_NEXT 16 + +template +struct interpolation_shifts_float +{ + static constexpr float post1 = 1.0f; + static const uint pre2 = 0; + static constexpr float post2 = 1.0f; + static const uint pre4 = 0; + static constexpr float post4 = 1.0f; + static const uint pre8 = 0; + static constexpr float post8 = 1.0f; + static const uint pre16 = 0; + static constexpr float post16 = 1.0f; + static const uint pre32 = 0; + static constexpr float post32 = 1.0f; + static const uint pre64 = 0; + static constexpr float post64 = 1.0f; +}; + +template<> +struct interpolation_shifts_float<16, 16> +{ + static constexpr float post1 = (1<<0)*32768.0f; + static const uint pre2 = 1; + static constexpr float post2 = (1<<1)*32768.0f; + static const uint pre4 = 2; + static constexpr float post4 = (1<<2)*32768.0f; + static const uint pre8 = 3; + static constexpr float post8 = (1<<3)*32768.0f; + static const uint pre16 = 3; + static constexpr float post16 = (1<<3)*32768.0f; + static const uint pre32 = 3; + static constexpr float post32 = (1<<3)*32768.0f; + static const uint pre64 = 3; + static constexpr float post64 = (1<<3)*32768.0f; +}; + +template<> +struct interpolation_shifts_float<16, 12> // never used, just an example +{ + static constexpr float post1 = (1<<4)*2048.0f; + static const uint pre2 = 1; + static constexpr float post2 = (1<<5)*2048.0f; + static const uint pre4 = 2; + static constexpr float post4 = (1<<6)*2048.0f; + static const uint pre8 = 3; + static constexpr float post8 = (1<<7)*2048.0f; + static const uint pre16 = 3; + static constexpr float post16 = (1<<7)*2048.0f; + static const uint pre32 = 3; + static constexpr float post32 = (1<<7)*2048.0f; + static const uint pre64 = 3; + static constexpr float post64 = (1<<7)*2048.0f; +}; + +template +class InterpolatorsIF +{ +public: + // interleaved I/Q input buffer + void interpolate1(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + + void interpolate2_cen(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + void interpolate2_inf(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + void interpolate2_sup(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + + void interpolate4_cen(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + void interpolate4_inf(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + void interpolate4_sup(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + + void interpolate8_cen(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + void interpolate8_inf(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + void interpolate8_sup(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + + void interpolate16_cen(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + void interpolate16_inf(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + void interpolate16_sup(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + + void interpolate32_cen(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + void interpolate32_inf(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + void interpolate32_sup(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + + void interpolate64_cen(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + void interpolate64_inf(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + void interpolate64_sup(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ = false); + +private: +#ifdef USE_SSE4_1 + IntHalfbandFilterEO1 m_interpolator2; // 1st stages + IntHalfbandFilterEO1 m_interpolator4; // 2nd stages + IntHalfbandFilterEO1 m_interpolator8; // 3rd stages + IntHalfbandFilterEO1 m_interpolator16; // 4th stages + IntHalfbandFilterEO1 m_interpolator32; // 5th stages + IntHalfbandFilterEO1 m_interpolator64; // 6th stages +#else + IntHalfbandFilterDB m_interpolator2; // 1st stages + IntHalfbandFilterDB m_interpolator4; // 2nd stages + IntHalfbandFilterDB m_interpolator8; // 3rd stages + IntHalfbandFilterDB m_interpolator16; // 4th stages + IntHalfbandFilterDB m_interpolator32; // 5th stages + IntHalfbandFilterDB m_interpolator64; // 6th stages +#endif +}; + +template +void InterpolatorsIF::interpolate1(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + if (invertIQ) + { + for (int pos = 0; pos < len - 1; pos += 2) + { + buf[pos+1] = (**it).m_real / interpolation_shifts::post1; + buf[pos+0] = (**it).m_imag / interpolation_shifts::post1; + ++(*it); + } + } + else + { + for (int pos = 0; pos < len - 1; pos += 2) + { + buf[pos+0] = (**it).m_real / interpolation_shifts::post1; + buf[pos+1] = (**it).m_imag / interpolation_shifts::post1; + ++(*it); + } + } +} + +template +void InterpolatorsIF::interpolate2_cen(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[4]; + qint32 *bufI, *bufQ; + + if (invertIQ) + { + bufI = &intbuf[1]; + bufQ = &intbuf[0]; + } + else + { + bufI = &intbuf[0]; + bufQ = &intbuf[1]; + } + + for (int pos = 0; pos < len - 3; pos += 4) + { + *bufI = (**it).m_real << interpolation_shifts::pre2; + *bufQ = (**it).m_imag << interpolation_shifts::pre2; +// intbuf[2] = 0; +// intbuf[3] = 0; + + m_interpolator2.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3]); + + buf[pos+0] = intbuf[0] / interpolation_shifts::post2; + buf[pos+1] = intbuf[1] / interpolation_shifts::post2; + buf[pos+2] = intbuf[2] / interpolation_shifts::post2; + buf[pos+3] = intbuf[3] / interpolation_shifts::post2; + + ++(*it); + } +} + +template +void InterpolatorsIF::interpolate2_inf(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[8]; + qint32 *bufI0, *bufQ0, *bufI1, *bufQ1; + + if (invertIQ) + { + bufI0 = &intbuf[1]; + bufQ0 = &intbuf[0]; + bufI1 = &intbuf[5]; + bufQ1 = &intbuf[4]; + } + else + { + bufI0 = &intbuf[0]; + bufQ0 = &intbuf[1]; + bufI1 = &intbuf[4]; + bufQ1 = &intbuf[5]; + } + + for (int pos = 0; pos < len - 7; pos += 8) + { + memset(intbuf, 0, 8*sizeof(qint32)); + + *bufI0 = (**it).m_real << interpolation_shifts::pre2; + *bufQ0 = (**it).m_imag << interpolation_shifts::pre2; + ++(*it); + *bufI1 = (**it).m_real << interpolation_shifts::pre2; + *bufQ1 = (**it).m_imag << interpolation_shifts::pre2; + ++(*it); + + m_interpolator2.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3], &intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + + buf[pos+0] = intbuf[0] / interpolation_shifts::post2; + buf[pos+1] = intbuf[1] / interpolation_shifts::post2; + buf[pos+2] = intbuf[2] / interpolation_shifts::post2; + buf[pos+3] = intbuf[3] / interpolation_shifts::post2; + buf[pos+4] = intbuf[4] / interpolation_shifts::post2; + buf[pos+5] = intbuf[5] / interpolation_shifts::post2; + buf[pos+6] = intbuf[6] / interpolation_shifts::post2; + buf[pos+7] = intbuf[7] / interpolation_shifts::post2; + } +} + +template +void InterpolatorsIF::interpolate2_sup(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[8]; + qint32 *bufI0, *bufQ0, *bufI1, *bufQ1; + + if (invertIQ) + { + bufI0 = &intbuf[1]; + bufQ0 = &intbuf[0]; + bufI1 = &intbuf[5]; + bufQ1 = &intbuf[4]; + } + else + { + bufI0 = &intbuf[0]; + bufQ0 = &intbuf[1]; + bufI1 = &intbuf[4]; + bufQ1 = &intbuf[5]; + } + + for (int pos = 0; pos < len - 7; pos += 8) + { + memset(intbuf, 0, 8*sizeof(qint32)); + + *bufI0 = (**it).m_real << interpolation_shifts::pre2; + *bufQ0 = (**it).m_imag << interpolation_shifts::pre2; + ++(*it); + *bufI1 = (**it).m_real << interpolation_shifts::pre2; + *bufQ1 = (**it).m_imag << interpolation_shifts::pre2; + ++(*it); + + m_interpolator2.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3], &intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + + buf[pos+0] = intbuf[0] / interpolation_shifts::post2; + buf[pos+1] = intbuf[1] / interpolation_shifts::post2; + buf[pos+2] = intbuf[2] / interpolation_shifts::post2; + buf[pos+3] = intbuf[3] / interpolation_shifts::post2; + buf[pos+4] = intbuf[4] / interpolation_shifts::post2; + buf[pos+5] = intbuf[5] / interpolation_shifts::post2; + buf[pos+6] = intbuf[6] / interpolation_shifts::post2; + buf[pos+7] = intbuf[7] / interpolation_shifts::post2; + } +} + +template +void InterpolatorsIF::interpolate4_cen(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[8]; + qint32 *bufI, *bufQ; + + if (invertIQ) + { + bufI = &intbuf[1]; + bufQ = &intbuf[0]; + } + else + { + bufI = &intbuf[0]; + bufQ = &intbuf[1]; + } + + for (int pos = 0; pos < len - 7; pos += 8) + { + memset(intbuf, 0, 8*sizeof(qint32)); + *bufI = (**it).m_real << interpolation_shifts::pre4; + *bufQ = (**it).m_imag << interpolation_shifts::pre4; + + m_interpolator2.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5]); + + m_interpolator4.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3]); + m_interpolator4.myInterpolate(&intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + + buf[pos+0] = intbuf[0] / interpolation_shifts::post4; + buf[pos+1] = intbuf[1] / interpolation_shifts::post4; + buf[pos+2] = intbuf[2] / interpolation_shifts::post4; + buf[pos+3] = intbuf[3] / interpolation_shifts::post4; + buf[pos+4] = intbuf[4] / interpolation_shifts::post4; + buf[pos+5] = intbuf[5] / interpolation_shifts::post4; + buf[pos+6] = intbuf[6] / interpolation_shifts::post4; + buf[pos+7] = intbuf[7] / interpolation_shifts::post4; + + ++(*it); + } +} + +template +void InterpolatorsIF::interpolate4_inf(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[16]; + qint32 *bufI0, *bufQ0, *bufI1, *bufQ1; + + if (invertIQ) + { + bufI0 = &intbuf[1]; + bufQ0 = &intbuf[0]; + bufI1 = &intbuf[9]; + bufQ1 = &intbuf[8]; + } + else + { + bufI0 = &intbuf[0]; + bufQ0 = &intbuf[1]; + bufI1 = &intbuf[8]; + bufQ1 = &intbuf[9]; + } + + for (int pos = 0; pos < len - 15; pos += 16) + { + memset(intbuf, 0, 16*sizeof(qint32)); + *bufI0 = (**it).m_real << interpolation_shifts::pre4; + *bufQ0 = (**it).m_imag << interpolation_shifts::pre4; + ++(*it); + *bufI1 = (**it).m_real << interpolation_shifts::pre4; + *bufQ1 = (**it).m_imag << interpolation_shifts::pre4; + ++(*it); + + m_interpolator2.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5], &intbuf[8], &intbuf[9], &intbuf[12], &intbuf[13]); + + m_interpolator4.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3], &intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + m_interpolator4.myInterpolateInf(&intbuf[8], &intbuf[9], &intbuf[10], &intbuf[11], &intbuf[12], &intbuf[13], &intbuf[14], &intbuf[15]); + + for (int i = 0; i < 16; i++) { + buf[pos+i] = intbuf[i] / interpolation_shifts::post4; + } + } +} + +template +void InterpolatorsIF::interpolate4_sup(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[16]; + qint32 *bufI0, *bufQ0, *bufI1, *bufQ1; + + if (invertIQ) + { + bufI0 = &intbuf[1]; + bufQ0 = &intbuf[0]; + bufI1 = &intbuf[9]; + bufQ1 = &intbuf[8]; + } + else + { + bufI0 = &intbuf[0]; + bufQ0 = &intbuf[1]; + bufI1 = &intbuf[8]; + bufQ1 = &intbuf[9]; + } + + for (int pos = 0; pos < len - 15; pos += 16) + { + memset(intbuf, 0, 16*sizeof(qint32)); + *bufI0 = (**it).m_real << interpolation_shifts::pre4; + *bufQ0 = (**it).m_imag << interpolation_shifts::pre4; + ++(*it); + *bufI1 = (**it).m_real << interpolation_shifts::pre4; + *bufQ1 = (**it).m_imag << interpolation_shifts::pre4; + ++(*it); + + m_interpolator2.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5], &intbuf[8], &intbuf[9], &intbuf[12], &intbuf[13]); + + m_interpolator4.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3], &intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + m_interpolator4.myInterpolateSup(&intbuf[8], &intbuf[9], &intbuf[10], &intbuf[11], &intbuf[12], &intbuf[13], &intbuf[14], &intbuf[15]); + + for (int i = 0; i < 16; i++) { + buf[pos+i] = intbuf[i] / interpolation_shifts::post4; + } + } +} + +template +void InterpolatorsIF::interpolate8_cen(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[16]; + qint32 *bufI, *bufQ; + + if (invertIQ) + { + bufI = &intbuf[1]; + bufQ = &intbuf[0]; + } + else + { + bufI = &intbuf[0]; + bufQ = &intbuf[1]; + } + + for (int pos = 0; pos < len - 15; pos += 16) + { + memset(intbuf, 0, 16*sizeof(qint32)); + *bufI = (**it).m_real << interpolation_shifts::pre8; + *bufQ = (**it).m_imag << interpolation_shifts::pre8; + + m_interpolator2.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[8], &intbuf[9]); + + m_interpolator4.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5]); + m_interpolator4.myInterpolate(&intbuf[8], &intbuf[9], &intbuf[12], &intbuf[13]); + + m_interpolator8.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3]); + m_interpolator8.myInterpolate(&intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + m_interpolator8.myInterpolate(&intbuf[8], &intbuf[9], &intbuf[10], &intbuf[11]); + m_interpolator8.myInterpolate(&intbuf[12], &intbuf[13], &intbuf[14], &intbuf[15]); + + buf[pos+0] = intbuf[0] / interpolation_shifts::post8; + buf[pos+1] = intbuf[1] / interpolation_shifts::post8; + buf[pos+2] = intbuf[2] / interpolation_shifts::post8; + buf[pos+3] = intbuf[3] / interpolation_shifts::post8; + buf[pos+4] = intbuf[4] / interpolation_shifts::post8; + buf[pos+5] = intbuf[5] / interpolation_shifts::post8; + buf[pos+6] = intbuf[6] / interpolation_shifts::post8; + buf[pos+7] = intbuf[7] / interpolation_shifts::post8; + buf[pos+8] = intbuf[8] / interpolation_shifts::post8; + buf[pos+9] = intbuf[9] / interpolation_shifts::post8; + buf[pos+10] = intbuf[10] / interpolation_shifts::post8; + buf[pos+11] = intbuf[11] / interpolation_shifts::post8; + buf[pos+12] = intbuf[12] / interpolation_shifts::post8; + buf[pos+13] = intbuf[13] / interpolation_shifts::post8; + buf[pos+14] = intbuf[14] / interpolation_shifts::post8; + buf[pos+15] = intbuf[15] / interpolation_shifts::post8; + + ++(*it); + } +} + +template +void InterpolatorsIF::interpolate8_inf(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[32]; + qint32 *bufI0, *bufQ0, *bufI1, *bufQ1; + + if (invertIQ) + { + bufI0 = &intbuf[1]; + bufQ0 = &intbuf[0]; + bufI1 = &intbuf[17]; + bufQ1 = &intbuf[16]; + } + else + { + bufI0 = &intbuf[0]; + bufQ0 = &intbuf[1]; + bufI1 = &intbuf[16]; + bufQ1 = &intbuf[17]; + } + + for (int pos = 0; pos < len - 31; pos += 32) + { + memset(intbuf, 0, 32*sizeof(qint32)); + *bufI0 = (**it).m_real << interpolation_shifts::pre8; + *bufQ0 = (**it).m_imag << interpolation_shifts::pre8; + ++(*it); + *bufI1 = (**it).m_real << interpolation_shifts::pre8; + *bufQ1 = (**it).m_imag << interpolation_shifts::pre8; + ++(*it); + + m_interpolator2.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[8], &intbuf[9], &intbuf[16], &intbuf[17], &intbuf[24], &intbuf[25]); + + m_interpolator4.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5], &intbuf[8], &intbuf[9], &intbuf[12], &intbuf[13]); + m_interpolator4.myInterpolateInf(&intbuf[16], &intbuf[17], &intbuf[20], &intbuf[21], &intbuf[24], &intbuf[25], &intbuf[28], &intbuf[29]); + + m_interpolator8.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3], &intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + m_interpolator8.myInterpolateInf(&intbuf[8], &intbuf[9], &intbuf[10], &intbuf[11], &intbuf[12], &intbuf[13], &intbuf[14], &intbuf[15]); + m_interpolator8.myInterpolateInf(&intbuf[16], &intbuf[17], &intbuf[18], &intbuf[19], &intbuf[20], &intbuf[21], &intbuf[22], &intbuf[23]); + m_interpolator8.myInterpolateInf(&intbuf[24], &intbuf[25], &intbuf[26], &intbuf[27], &intbuf[28], &intbuf[29], &intbuf[30], &intbuf[31]); + + for (int i = 0; i < 32; i++) { + buf[pos+i] = intbuf[i] / interpolation_shifts::post8; + } + } +} + +template +void InterpolatorsIF::interpolate8_sup(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[32]; + qint32 *bufI0, *bufQ0, *bufI1, *bufQ1; + + if (invertIQ) + { + bufI0 = &intbuf[1]; + bufQ0 = &intbuf[0]; + bufI1 = &intbuf[17]; + bufQ1 = &intbuf[16]; + } + else + { + bufI0 = &intbuf[0]; + bufQ0 = &intbuf[1]; + bufI1 = &intbuf[16]; + bufQ1 = &intbuf[17]; + } + + for (int pos = 0; pos < len - 31; pos += 32) + { + memset(intbuf, 0, 32*sizeof(qint32)); + *bufI0 = (**it).m_real << interpolation_shifts::pre8; + *bufQ0 = (**it).m_imag << interpolation_shifts::pre8; + ++(*it); + *bufI1 = (**it).m_real << interpolation_shifts::pre8; + *bufQ1 = (**it).m_imag << interpolation_shifts::pre8; + ++(*it); + + m_interpolator2.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[8], &intbuf[9], &intbuf[16], &intbuf[17], &intbuf[24], &intbuf[25]); + + m_interpolator4.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5], &intbuf[8], &intbuf[9], &intbuf[12], &intbuf[13]); + m_interpolator4.myInterpolateSup(&intbuf[16], &intbuf[17], &intbuf[20], &intbuf[21], &intbuf[24], &intbuf[25], &intbuf[28], &intbuf[29]); + + m_interpolator8.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3], &intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + m_interpolator8.myInterpolateSup(&intbuf[8], &intbuf[9], &intbuf[10], &intbuf[11], &intbuf[12], &intbuf[13], &intbuf[14], &intbuf[15]); + m_interpolator8.myInterpolateSup(&intbuf[16], &intbuf[17], &intbuf[18], &intbuf[19], &intbuf[20], &intbuf[21], &intbuf[22], &intbuf[23]); + m_interpolator8.myInterpolateSup(&intbuf[24], &intbuf[25], &intbuf[26], &intbuf[27], &intbuf[28], &intbuf[29], &intbuf[30], &intbuf[31]); + + for (int i = 0; i < 32; i++) { + buf[pos+i] = intbuf[i] / interpolation_shifts::post8; + } + } +} + +template +void InterpolatorsIF::interpolate16_cen(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[32]; + qint32 *bufI, *bufQ; + + if (invertIQ) + { + bufI = &intbuf[1]; + bufQ = &intbuf[0]; + } + else + { + bufI = &intbuf[0]; + bufQ = &intbuf[1]; + } + + for (int pos = 0; pos < len - 31; pos += 32) + { + memset(intbuf, 0, 32*sizeof(qint32)); + *bufI = (**it).m_real << interpolation_shifts::pre16; + *bufQ = (**it).m_imag << interpolation_shifts::pre16; + + m_interpolator2.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[16], &intbuf[17]); + + m_interpolator4.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[8], &intbuf[9]); + m_interpolator4.myInterpolate(&intbuf[16], &intbuf[17], &intbuf[24], &intbuf[25]); + + m_interpolator8.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5]); + m_interpolator8.myInterpolate(&intbuf[8], &intbuf[9], &intbuf[12], &intbuf[13]); + m_interpolator8.myInterpolate(&intbuf[16], &intbuf[17], &intbuf[20], &intbuf[21]); + m_interpolator8.myInterpolate(&intbuf[24], &intbuf[25], &intbuf[28], &intbuf[29]); + + m_interpolator16.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3]); + m_interpolator16.myInterpolate(&intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + m_interpolator16.myInterpolate(&intbuf[8], &intbuf[9], &intbuf[10], &intbuf[11]); + m_interpolator16.myInterpolate(&intbuf[12], &intbuf[13], &intbuf[14], &intbuf[15]); + m_interpolator16.myInterpolate(&intbuf[16], &intbuf[17], &intbuf[18], &intbuf[19]); + m_interpolator16.myInterpolate(&intbuf[20], &intbuf[21], &intbuf[22], &intbuf[23]); + m_interpolator16.myInterpolate(&intbuf[24], &intbuf[25], &intbuf[26], &intbuf[27]); + m_interpolator16.myInterpolate(&intbuf[28], &intbuf[29], &intbuf[30], &intbuf[31]); + + buf[pos+0] = intbuf[0] / interpolation_shifts::post16; + buf[pos+1] = intbuf[1] / interpolation_shifts::post16; + buf[pos+2] = intbuf[2] / interpolation_shifts::post16; + buf[pos+3] = intbuf[3] / interpolation_shifts::post16; + buf[pos+4] = intbuf[4] / interpolation_shifts::post16; + buf[pos+5] = intbuf[5] / interpolation_shifts::post16; + buf[pos+6] = intbuf[6] / interpolation_shifts::post16; + buf[pos+7] = intbuf[7] / interpolation_shifts::post16; + buf[pos+8] = intbuf[8] / interpolation_shifts::post16; + buf[pos+9] = intbuf[9] / interpolation_shifts::post16; + buf[pos+10] = intbuf[10] / interpolation_shifts::post16; + buf[pos+11] = intbuf[11] / interpolation_shifts::post16; + buf[pos+12] = intbuf[12] / interpolation_shifts::post16; + buf[pos+13] = intbuf[13] / interpolation_shifts::post16; + buf[pos+14] = intbuf[14] / interpolation_shifts::post16; + buf[pos+15] = intbuf[15] / interpolation_shifts::post16; + buf[pos+16] = intbuf[16] / interpolation_shifts::post16; + buf[pos+17] = intbuf[17] / interpolation_shifts::post16; + buf[pos+18] = intbuf[18] / interpolation_shifts::post16; + buf[pos+19] = intbuf[19] / interpolation_shifts::post16; + buf[pos+20] = intbuf[20] / interpolation_shifts::post16; + buf[pos+21] = intbuf[21] / interpolation_shifts::post16; + buf[pos+22] = intbuf[22] / interpolation_shifts::post16; + buf[pos+23] = intbuf[23] / interpolation_shifts::post16; + buf[pos+24] = intbuf[24] / interpolation_shifts::post16; + buf[pos+25] = intbuf[25] / interpolation_shifts::post16; + buf[pos+26] = intbuf[26] / interpolation_shifts::post16; + buf[pos+27] = intbuf[27] / interpolation_shifts::post16; + buf[pos+28] = intbuf[28] / interpolation_shifts::post16; + buf[pos+29] = intbuf[29] / interpolation_shifts::post16; + buf[pos+30] = intbuf[30] / interpolation_shifts::post16; + buf[pos+31] = intbuf[31] / interpolation_shifts::post16; + + ++(*it); + } +} + +template +void InterpolatorsIF::interpolate16_inf(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[64]; + qint32 *bufI0, *bufQ0, *bufI1, *bufQ1; + + if (invertIQ) + { + bufI0 = &intbuf[1]; + bufQ0 = &intbuf[0]; + bufI1 = &intbuf[33]; + bufQ1 = &intbuf[32]; + } + else + { + bufI0 = &intbuf[0]; + bufQ0 = &intbuf[1]; + bufI1 = &intbuf[32]; + bufQ1 = &intbuf[33]; + } + + for (int pos = 0; pos < len - 63; pos += 64) + { + memset(intbuf, 0, 64*sizeof(qint32)); + *bufI0 = (**it).m_real << interpolation_shifts::pre16; + *bufQ0 = (**it).m_imag << interpolation_shifts::pre16; + ++(*it); + *bufI1 = (**it).m_real << interpolation_shifts::pre16; + *bufQ1 = (**it).m_imag << interpolation_shifts::pre16; + ++(*it); + + m_interpolator2.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[16], &intbuf[17], &intbuf[32], &intbuf[33], &intbuf[48], &intbuf[49]); + + m_interpolator4.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[8], &intbuf[9], &intbuf[16], &intbuf[17], &intbuf[24], &intbuf[25]); + m_interpolator4.myInterpolateSup(&intbuf[32], &intbuf[33], &intbuf[40], &intbuf[41], &intbuf[48], &intbuf[49], &intbuf[56], &intbuf[57]); + + m_interpolator8.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5], &intbuf[8], &intbuf[9], &intbuf[12], &intbuf[13]); + m_interpolator8.myInterpolateInf(&intbuf[16], &intbuf[17], &intbuf[20], &intbuf[21], &intbuf[24], &intbuf[25], &intbuf[28], &intbuf[29]); + m_interpolator8.myInterpolateInf(&intbuf[32], &intbuf[33], &intbuf[36], &intbuf[37], &intbuf[40], &intbuf[41], &intbuf[44], &intbuf[45]); + m_interpolator8.myInterpolateInf(&intbuf[48], &intbuf[49], &intbuf[52], &intbuf[53], &intbuf[56], &intbuf[57], &intbuf[60], &intbuf[61]); + + m_interpolator16.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3], &intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + m_interpolator16.myInterpolateInf(&intbuf[8], &intbuf[9], &intbuf[10], &intbuf[11], &intbuf[12], &intbuf[13], &intbuf[14], &intbuf[15]); + m_interpolator16.myInterpolateInf(&intbuf[16], &intbuf[17], &intbuf[18], &intbuf[19], &intbuf[20], &intbuf[21], &intbuf[22], &intbuf[23]); + m_interpolator16.myInterpolateInf(&intbuf[24], &intbuf[25], &intbuf[26], &intbuf[27], &intbuf[28], &intbuf[29], &intbuf[30], &intbuf[31]); + m_interpolator16.myInterpolateInf(&intbuf[32], &intbuf[33], &intbuf[34], &intbuf[35], &intbuf[36], &intbuf[37], &intbuf[38], &intbuf[39]); + m_interpolator16.myInterpolateInf(&intbuf[40], &intbuf[41], &intbuf[42], &intbuf[43], &intbuf[44], &intbuf[45], &intbuf[46], &intbuf[47]); + m_interpolator16.myInterpolateInf(&intbuf[48], &intbuf[49], &intbuf[50], &intbuf[51], &intbuf[52], &intbuf[53], &intbuf[54], &intbuf[55]); + m_interpolator16.myInterpolateInf(&intbuf[56], &intbuf[57], &intbuf[58], &intbuf[59], &intbuf[60], &intbuf[61], &intbuf[62], &intbuf[63]); + + for (int i = 0; i < 64; i++) { + buf[pos+i] = intbuf[i] / interpolation_shifts::post16; + } + } +} + +template +void InterpolatorsIF::interpolate16_sup(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[64]; + qint32 *bufI0, *bufQ0, *bufI1, *bufQ1; + + if (invertIQ) + { + bufI0 = &intbuf[1]; + bufQ0 = &intbuf[0]; + bufI1 = &intbuf[33]; + bufQ1 = &intbuf[32]; + } + else + { + bufI0 = &intbuf[0]; + bufQ0 = &intbuf[1]; + bufI1 = &intbuf[32]; + bufQ1 = &intbuf[33]; + } + + for (int pos = 0; pos < len - 63; pos += 64) + { + memset(intbuf, 0, 64*sizeof(qint32)); + *bufI0 = (**it).m_real << interpolation_shifts::pre16; + *bufQ0 = (**it).m_imag << interpolation_shifts::pre16; + ++(*it); + *bufI1 = (**it).m_real << interpolation_shifts::pre16; + *bufQ1 = (**it).m_imag << interpolation_shifts::pre16; + ++(*it); + + m_interpolator2.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[16], &intbuf[17], &intbuf[32], &intbuf[33], &intbuf[48], &intbuf[49]); + + m_interpolator4.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[8], &intbuf[9], &intbuf[16], &intbuf[17], &intbuf[24], &intbuf[25]); + m_interpolator4.myInterpolateInf(&intbuf[32], &intbuf[33], &intbuf[40], &intbuf[41], &intbuf[48], &intbuf[49], &intbuf[56], &intbuf[57]); + + m_interpolator8.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5], &intbuf[8], &intbuf[9], &intbuf[12], &intbuf[13]); + m_interpolator8.myInterpolateSup(&intbuf[16], &intbuf[17], &intbuf[20], &intbuf[21], &intbuf[24], &intbuf[25], &intbuf[28], &intbuf[29]); + m_interpolator8.myInterpolateSup(&intbuf[32], &intbuf[33], &intbuf[36], &intbuf[37], &intbuf[40], &intbuf[41], &intbuf[44], &intbuf[45]); + m_interpolator8.myInterpolateSup(&intbuf[48], &intbuf[49], &intbuf[52], &intbuf[53], &intbuf[56], &intbuf[57], &intbuf[60], &intbuf[61]); + + m_interpolator16.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3], &intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + m_interpolator16.myInterpolateSup(&intbuf[8], &intbuf[9], &intbuf[10], &intbuf[11], &intbuf[12], &intbuf[13], &intbuf[14], &intbuf[15]); + m_interpolator16.myInterpolateSup(&intbuf[16], &intbuf[17], &intbuf[18], &intbuf[19], &intbuf[20], &intbuf[21], &intbuf[22], &intbuf[23]); + m_interpolator16.myInterpolateSup(&intbuf[24], &intbuf[25], &intbuf[26], &intbuf[27], &intbuf[28], &intbuf[29], &intbuf[30], &intbuf[31]); + m_interpolator16.myInterpolateSup(&intbuf[32], &intbuf[33], &intbuf[34], &intbuf[35], &intbuf[36], &intbuf[37], &intbuf[38], &intbuf[39]); + m_interpolator16.myInterpolateSup(&intbuf[40], &intbuf[41], &intbuf[42], &intbuf[43], &intbuf[44], &intbuf[45], &intbuf[46], &intbuf[47]); + m_interpolator16.myInterpolateSup(&intbuf[48], &intbuf[49], &intbuf[50], &intbuf[51], &intbuf[52], &intbuf[53], &intbuf[54], &intbuf[55]); + m_interpolator16.myInterpolateSup(&intbuf[56], &intbuf[57], &intbuf[58], &intbuf[59], &intbuf[60], &intbuf[61], &intbuf[62], &intbuf[63]); + + for (int i = 0; i < 64; i++) { + buf[pos+i] = intbuf[i] / interpolation_shifts::post16; + } + } +} + +template +void InterpolatorsIF::interpolate32_cen(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[64]; + qint32 *bufI, *bufQ; + + if (invertIQ) + { + bufI = &intbuf[1]; + bufQ = &intbuf[0]; + } + else + { + bufI = &intbuf[0]; + bufQ = &intbuf[1]; + } + + for (int pos = 0; pos < len - 63; pos += 64) + { + memset(intbuf, 0, 64*sizeof(qint32)); + *bufI = (**it).m_real << interpolation_shifts::pre32; + *bufQ = (**it).m_imag << interpolation_shifts::pre32; + m_interpolator2.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[32], &intbuf[33]); + + m_interpolator4.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[16], &intbuf[17]); + m_interpolator4.myInterpolate(&intbuf[32], &intbuf[33], &intbuf[48], &intbuf[49]); + + m_interpolator8.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[8], &intbuf[9]); + m_interpolator8.myInterpolate(&intbuf[16], &intbuf[17], &intbuf[24], &intbuf[25]); + m_interpolator8.myInterpolate(&intbuf[32], &intbuf[33], &intbuf[40], &intbuf[41]); + m_interpolator8.myInterpolate(&intbuf[48], &intbuf[49], &intbuf[56], &intbuf[57]); + + m_interpolator16.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5]); + m_interpolator16.myInterpolate(&intbuf[8], &intbuf[9], &intbuf[12], &intbuf[13]); + m_interpolator16.myInterpolate(&intbuf[16], &intbuf[17], &intbuf[20], &intbuf[21]); + m_interpolator16.myInterpolate(&intbuf[24], &intbuf[25], &intbuf[28], &intbuf[29]); + m_interpolator16.myInterpolate(&intbuf[32], &intbuf[33], &intbuf[36], &intbuf[37]); + m_interpolator16.myInterpolate(&intbuf[40], &intbuf[41], &intbuf[44], &intbuf[45]); + m_interpolator16.myInterpolate(&intbuf[48], &intbuf[49], &intbuf[52], &intbuf[53]); + m_interpolator16.myInterpolate(&intbuf[56], &intbuf[57], &intbuf[60], &intbuf[61]); + + m_interpolator32.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3]); + m_interpolator32.myInterpolate(&intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + m_interpolator32.myInterpolate(&intbuf[8], &intbuf[9], &intbuf[10], &intbuf[11]); + m_interpolator32.myInterpolate(&intbuf[12], &intbuf[13], &intbuf[14], &intbuf[15]); + m_interpolator32.myInterpolate(&intbuf[16], &intbuf[17], &intbuf[18], &intbuf[19]); + m_interpolator32.myInterpolate(&intbuf[20], &intbuf[21], &intbuf[22], &intbuf[23]); + m_interpolator32.myInterpolate(&intbuf[24], &intbuf[25], &intbuf[26], &intbuf[27]); + m_interpolator32.myInterpolate(&intbuf[28], &intbuf[29], &intbuf[30], &intbuf[31]); + m_interpolator32.myInterpolate(&intbuf[32], &intbuf[33], &intbuf[34], &intbuf[35]); + m_interpolator32.myInterpolate(&intbuf[36], &intbuf[37], &intbuf[38], &intbuf[39]); + m_interpolator32.myInterpolate(&intbuf[40], &intbuf[41], &intbuf[42], &intbuf[43]); + m_interpolator32.myInterpolate(&intbuf[44], &intbuf[45], &intbuf[46], &intbuf[47]); + m_interpolator32.myInterpolate(&intbuf[48], &intbuf[49], &intbuf[50], &intbuf[51]); + m_interpolator32.myInterpolate(&intbuf[52], &intbuf[53], &intbuf[54], &intbuf[55]); + m_interpolator32.myInterpolate(&intbuf[56], &intbuf[57], &intbuf[58], &intbuf[59]); + m_interpolator32.myInterpolate(&intbuf[60], &intbuf[61], &intbuf[62], &intbuf[63]); + + buf[pos+0] = intbuf[0] / interpolation_shifts::post32; + buf[pos+1] = intbuf[1] / interpolation_shifts::post32; + buf[pos+2] = intbuf[2] / interpolation_shifts::post32; + buf[pos+3] = intbuf[3] / interpolation_shifts::post32; + buf[pos+4] = intbuf[4] / interpolation_shifts::post32; + buf[pos+5] = intbuf[5] / interpolation_shifts::post32; + buf[pos+6] = intbuf[6] / interpolation_shifts::post32; + buf[pos+7] = intbuf[7] / interpolation_shifts::post32; + buf[pos+8] = intbuf[8] / interpolation_shifts::post32; + buf[pos+9] = intbuf[9] / interpolation_shifts::post32; + buf[pos+10] = intbuf[10] / interpolation_shifts::post32; + buf[pos+11] = intbuf[11] / interpolation_shifts::post32; + buf[pos+12] = intbuf[12] / interpolation_shifts::post32; + buf[pos+13] = intbuf[13] / interpolation_shifts::post32; + buf[pos+14] = intbuf[14] / interpolation_shifts::post32; + buf[pos+15] = intbuf[15] / interpolation_shifts::post32; + buf[pos+16] = intbuf[16] / interpolation_shifts::post32; + buf[pos+17] = intbuf[17] / interpolation_shifts::post32; + buf[pos+18] = intbuf[18] / interpolation_shifts::post32; + buf[pos+19] = intbuf[19] / interpolation_shifts::post32; + buf[pos+20] = intbuf[20] / interpolation_shifts::post32; + buf[pos+21] = intbuf[21] / interpolation_shifts::post32; + buf[pos+22] = intbuf[22] / interpolation_shifts::post32; + buf[pos+23] = intbuf[23] / interpolation_shifts::post32; + buf[pos+24] = intbuf[24] / interpolation_shifts::post32; + buf[pos+25] = intbuf[25] / interpolation_shifts::post32; + buf[pos+26] = intbuf[26] / interpolation_shifts::post32; + buf[pos+27] = intbuf[27] / interpolation_shifts::post32; + buf[pos+28] = intbuf[28] / interpolation_shifts::post32; + buf[pos+29] = intbuf[29] / interpolation_shifts::post32; + buf[pos+30] = intbuf[30] / interpolation_shifts::post32; + buf[pos+31] = intbuf[31] / interpolation_shifts::post32; + buf[pos+32] = intbuf[32] / interpolation_shifts::post32; + buf[pos+33] = intbuf[33] / interpolation_shifts::post32; + buf[pos+34] = intbuf[34] / interpolation_shifts::post32; + buf[pos+35] = intbuf[35] / interpolation_shifts::post32; + buf[pos+36] = intbuf[36] / interpolation_shifts::post32; + buf[pos+37] = intbuf[37] / interpolation_shifts::post32; + buf[pos+38] = intbuf[38] / interpolation_shifts::post32; + buf[pos+39] = intbuf[39] / interpolation_shifts::post32; + buf[pos+40] = intbuf[40] / interpolation_shifts::post32; + buf[pos+41] = intbuf[41] / interpolation_shifts::post32; + buf[pos+42] = intbuf[42] / interpolation_shifts::post32; + buf[pos+43] = intbuf[43] / interpolation_shifts::post32; + buf[pos+44] = intbuf[44] / interpolation_shifts::post32; + buf[pos+45] = intbuf[45] / interpolation_shifts::post32; + buf[pos+46] = intbuf[46] / interpolation_shifts::post32; + buf[pos+47] = intbuf[47] / interpolation_shifts::post32; + buf[pos+48] = intbuf[48] / interpolation_shifts::post32; + buf[pos+49] = intbuf[49] / interpolation_shifts::post32; + buf[pos+50] = intbuf[50] / interpolation_shifts::post32; + buf[pos+51] = intbuf[51] / interpolation_shifts::post32; + buf[pos+52] = intbuf[52] / interpolation_shifts::post32; + buf[pos+53] = intbuf[53] / interpolation_shifts::post32; + buf[pos+54] = intbuf[54] / interpolation_shifts::post32; + buf[pos+55] = intbuf[55] / interpolation_shifts::post32; + buf[pos+56] = intbuf[56] / interpolation_shifts::post32; + buf[pos+57] = intbuf[57] / interpolation_shifts::post32; + buf[pos+58] = intbuf[58] / interpolation_shifts::post32; + buf[pos+59] = intbuf[59] / interpolation_shifts::post32; + buf[pos+60] = intbuf[60] / interpolation_shifts::post32; + buf[pos+61] = intbuf[61] / interpolation_shifts::post32; + buf[pos+62] = intbuf[62] / interpolation_shifts::post32; + buf[pos+63] = intbuf[63] / interpolation_shifts::post32; + + ++(*it); + } +} + +template +void InterpolatorsIF::interpolate32_inf(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[128]; + qint32 *bufI0, *bufQ0, *bufI1, *bufQ1; + + if (invertIQ) + { + bufI0 = &intbuf[1]; + bufQ0 = &intbuf[0]; + bufI1 = &intbuf[65]; + bufQ1 = &intbuf[64]; + } + else + { + bufI0 = &intbuf[0]; + bufQ0 = &intbuf[1]; + bufI1 = &intbuf[64]; + bufQ1 = &intbuf[65]; + } + + for (int pos = 0; pos < len - 127; pos += 128) + { + memset(intbuf, 0, 128*sizeof(qint32)); + *bufI0 = (**it).m_real << interpolation_shifts::pre32; + *bufQ0 = (**it).m_imag << interpolation_shifts::pre32; + ++(*it); + *bufI1 = (**it).m_real << interpolation_shifts::pre32; + *bufQ1 = (**it).m_imag << interpolation_shifts::pre32; + ++(*it); + + m_interpolator2.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[32], &intbuf[33], &intbuf[64], &intbuf[65], &intbuf[96], &intbuf[97]); + + m_interpolator4.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[16], &intbuf[17], &intbuf[32], &intbuf[33], &intbuf[48], &intbuf[49]); + m_interpolator4.myInterpolateInf(&intbuf[64], &intbuf[65], &intbuf[80], &intbuf[81], &intbuf[96], &intbuf[97], &intbuf[112], &intbuf[113]); + + m_interpolator8.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[8], &intbuf[9], &intbuf[16], &intbuf[17], &intbuf[24], &intbuf[25]); + m_interpolator8.myInterpolateSup(&intbuf[32], &intbuf[33], &intbuf[40], &intbuf[41], &intbuf[48], &intbuf[49], &intbuf[56], &intbuf[57]); + m_interpolator8.myInterpolateSup(&intbuf[64], &intbuf[65], &intbuf[72], &intbuf[73], &intbuf[80], &intbuf[81], &intbuf[88], &intbuf[89]); + m_interpolator8.myInterpolateSup(&intbuf[96], &intbuf[97], &intbuf[104], &intbuf[105], &intbuf[112], &intbuf[113], &intbuf[120], &intbuf[121]); + + m_interpolator16.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5], &intbuf[8], &intbuf[9], &intbuf[12], &intbuf[13]); + m_interpolator16.myInterpolateInf(&intbuf[16], &intbuf[17], &intbuf[20], &intbuf[21], &intbuf[24], &intbuf[25], &intbuf[28], &intbuf[29]); + m_interpolator16.myInterpolateInf(&intbuf[32], &intbuf[33], &intbuf[36], &intbuf[37], &intbuf[40], &intbuf[41], &intbuf[44], &intbuf[45]); + m_interpolator16.myInterpolateInf(&intbuf[48], &intbuf[49], &intbuf[52], &intbuf[53], &intbuf[56], &intbuf[57], &intbuf[60], &intbuf[61]); + m_interpolator16.myInterpolateInf(&intbuf[64], &intbuf[65], &intbuf[68], &intbuf[69], &intbuf[72], &intbuf[73], &intbuf[76], &intbuf[77]); + m_interpolator16.myInterpolateInf(&intbuf[80], &intbuf[81], &intbuf[84], &intbuf[85], &intbuf[88], &intbuf[89], &intbuf[92], &intbuf[93]); + m_interpolator16.myInterpolateInf(&intbuf[96], &intbuf[97], &intbuf[100], &intbuf[101], &intbuf[104], &intbuf[105], &intbuf[108], &intbuf[109]); + m_interpolator16.myInterpolateInf(&intbuf[112], &intbuf[113], &intbuf[116], &intbuf[117], &intbuf[120], &intbuf[121], &intbuf[124], &intbuf[125]); + + m_interpolator32.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3], &intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + m_interpolator32.myInterpolateInf(&intbuf[8], &intbuf[9], &intbuf[10], &intbuf[11], &intbuf[12], &intbuf[13], &intbuf[14], &intbuf[15]); + m_interpolator32.myInterpolateInf(&intbuf[16], &intbuf[17], &intbuf[18], &intbuf[19], &intbuf[20], &intbuf[21], &intbuf[22], &intbuf[23]); + m_interpolator32.myInterpolateInf(&intbuf[24], &intbuf[25], &intbuf[26], &intbuf[27], &intbuf[28], &intbuf[29], &intbuf[30], &intbuf[31]); + m_interpolator32.myInterpolateInf(&intbuf[32], &intbuf[33], &intbuf[34], &intbuf[35], &intbuf[36], &intbuf[37], &intbuf[38], &intbuf[39]); + m_interpolator32.myInterpolateInf(&intbuf[40], &intbuf[41], &intbuf[42], &intbuf[43], &intbuf[44], &intbuf[45], &intbuf[46], &intbuf[47]); + m_interpolator32.myInterpolateInf(&intbuf[48], &intbuf[49], &intbuf[50], &intbuf[51], &intbuf[52], &intbuf[53], &intbuf[54], &intbuf[55]); + m_interpolator32.myInterpolateInf(&intbuf[56], &intbuf[57], &intbuf[58], &intbuf[59], &intbuf[60], &intbuf[61], &intbuf[62], &intbuf[63]); + m_interpolator32.myInterpolateInf(&intbuf[64], &intbuf[65], &intbuf[66], &intbuf[67], &intbuf[68], &intbuf[69], &intbuf[70], &intbuf[71]); + m_interpolator32.myInterpolateInf(&intbuf[72], &intbuf[73], &intbuf[74], &intbuf[75], &intbuf[76], &intbuf[77], &intbuf[78], &intbuf[79]); + m_interpolator32.myInterpolateInf(&intbuf[80], &intbuf[81], &intbuf[82], &intbuf[83], &intbuf[84], &intbuf[85], &intbuf[86], &intbuf[87]); + m_interpolator32.myInterpolateInf(&intbuf[88], &intbuf[89], &intbuf[90], &intbuf[91], &intbuf[92], &intbuf[93], &intbuf[94], &intbuf[95]); + m_interpolator32.myInterpolateInf(&intbuf[96], &intbuf[97], &intbuf[98], &intbuf[99], &intbuf[100], &intbuf[101], &intbuf[102], &intbuf[103]); + m_interpolator32.myInterpolateInf(&intbuf[104], &intbuf[105], &intbuf[106], &intbuf[107], &intbuf[108], &intbuf[109], &intbuf[110], &intbuf[111]); + m_interpolator32.myInterpolateInf(&intbuf[112], &intbuf[113], &intbuf[114], &intbuf[115], &intbuf[116], &intbuf[117], &intbuf[118], &intbuf[119]); + m_interpolator32.myInterpolateInf(&intbuf[120], &intbuf[121], &intbuf[122], &intbuf[123], &intbuf[124], &intbuf[125], &intbuf[126], &intbuf[127]); + + for (int i = 0; i < 128; i++) { + buf[pos+i] = intbuf[i] / interpolation_shifts::post32; + } + } +} + +template +void InterpolatorsIF::interpolate32_sup(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[128]; + qint32 *bufI0, *bufQ0, *bufI1, *bufQ1; + + if (invertIQ) + { + bufI0 = &intbuf[1]; + bufQ0 = &intbuf[0]; + bufI1 = &intbuf[65]; + bufQ1 = &intbuf[64]; + } + else + { + bufI0 = &intbuf[0]; + bufQ0 = &intbuf[1]; + bufI1 = &intbuf[64]; + bufQ1 = &intbuf[65]; + } + + for (int pos = 0; pos < len - 127; pos += 128) + { + memset(intbuf, 0, 128*sizeof(qint32)); + *bufI0 = (**it).m_real << interpolation_shifts::pre32; + *bufQ0 = (**it).m_imag << interpolation_shifts::pre32; + ++(*it); + *bufI1 = (**it).m_real << interpolation_shifts::pre32; + *bufQ1 = (**it).m_imag << interpolation_shifts::pre32; + ++(*it); + + m_interpolator2.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[32], &intbuf[33], &intbuf[64], &intbuf[65], &intbuf[96], &intbuf[97]); + + m_interpolator4.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[16], &intbuf[17], &intbuf[32], &intbuf[33], &intbuf[48], &intbuf[49]); + m_interpolator4.myInterpolateSup(&intbuf[64], &intbuf[65], &intbuf[80], &intbuf[81], &intbuf[96], &intbuf[97], &intbuf[112], &intbuf[113]); + + m_interpolator8.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[8], &intbuf[9], &intbuf[16], &intbuf[17], &intbuf[24], &intbuf[25]); + m_interpolator8.myInterpolateInf(&intbuf[32], &intbuf[33], &intbuf[40], &intbuf[41], &intbuf[48], &intbuf[49], &intbuf[56], &intbuf[57]); + m_interpolator8.myInterpolateInf(&intbuf[64], &intbuf[65], &intbuf[72], &intbuf[73], &intbuf[80], &intbuf[81], &intbuf[88], &intbuf[89]); + m_interpolator8.myInterpolateInf(&intbuf[96], &intbuf[97], &intbuf[104], &intbuf[105], &intbuf[112], &intbuf[113], &intbuf[120], &intbuf[121]); + + m_interpolator16.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5], &intbuf[8], &intbuf[9], &intbuf[12], &intbuf[13]); + m_interpolator16.myInterpolateSup(&intbuf[16], &intbuf[17], &intbuf[20], &intbuf[21], &intbuf[24], &intbuf[25], &intbuf[28], &intbuf[29]); + m_interpolator16.myInterpolateSup(&intbuf[32], &intbuf[33], &intbuf[36], &intbuf[37], &intbuf[40], &intbuf[41], &intbuf[44], &intbuf[45]); + m_interpolator16.myInterpolateSup(&intbuf[48], &intbuf[49], &intbuf[52], &intbuf[53], &intbuf[56], &intbuf[57], &intbuf[60], &intbuf[61]); + m_interpolator16.myInterpolateSup(&intbuf[64], &intbuf[65], &intbuf[68], &intbuf[69], &intbuf[72], &intbuf[73], &intbuf[76], &intbuf[77]); + m_interpolator16.myInterpolateSup(&intbuf[80], &intbuf[81], &intbuf[84], &intbuf[85], &intbuf[88], &intbuf[89], &intbuf[92], &intbuf[93]); + m_interpolator16.myInterpolateSup(&intbuf[96], &intbuf[97], &intbuf[100], &intbuf[101], &intbuf[104], &intbuf[105], &intbuf[108], &intbuf[109]); + m_interpolator16.myInterpolateSup(&intbuf[112], &intbuf[113], &intbuf[116], &intbuf[117], &intbuf[120], &intbuf[121], &intbuf[124], &intbuf[125]); + + m_interpolator32.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3], &intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + m_interpolator32.myInterpolateSup(&intbuf[8], &intbuf[9], &intbuf[10], &intbuf[11], &intbuf[12], &intbuf[13], &intbuf[14], &intbuf[15]); + m_interpolator32.myInterpolateSup(&intbuf[16], &intbuf[17], &intbuf[18], &intbuf[19], &intbuf[20], &intbuf[21], &intbuf[22], &intbuf[23]); + m_interpolator32.myInterpolateSup(&intbuf[24], &intbuf[25], &intbuf[26], &intbuf[27], &intbuf[28], &intbuf[29], &intbuf[30], &intbuf[31]); + m_interpolator32.myInterpolateSup(&intbuf[32], &intbuf[33], &intbuf[34], &intbuf[35], &intbuf[36], &intbuf[37], &intbuf[38], &intbuf[39]); + m_interpolator32.myInterpolateSup(&intbuf[40], &intbuf[41], &intbuf[42], &intbuf[43], &intbuf[44], &intbuf[45], &intbuf[46], &intbuf[47]); + m_interpolator32.myInterpolateSup(&intbuf[48], &intbuf[49], &intbuf[50], &intbuf[51], &intbuf[52], &intbuf[53], &intbuf[54], &intbuf[55]); + m_interpolator32.myInterpolateSup(&intbuf[56], &intbuf[57], &intbuf[58], &intbuf[59], &intbuf[60], &intbuf[61], &intbuf[62], &intbuf[63]); + m_interpolator32.myInterpolateSup(&intbuf[64], &intbuf[65], &intbuf[66], &intbuf[67], &intbuf[68], &intbuf[69], &intbuf[70], &intbuf[71]); + m_interpolator32.myInterpolateSup(&intbuf[72], &intbuf[73], &intbuf[74], &intbuf[75], &intbuf[76], &intbuf[77], &intbuf[78], &intbuf[79]); + m_interpolator32.myInterpolateSup(&intbuf[80], &intbuf[81], &intbuf[82], &intbuf[83], &intbuf[84], &intbuf[85], &intbuf[86], &intbuf[87]); + m_interpolator32.myInterpolateSup(&intbuf[88], &intbuf[89], &intbuf[90], &intbuf[91], &intbuf[92], &intbuf[93], &intbuf[94], &intbuf[95]); + m_interpolator32.myInterpolateSup(&intbuf[96], &intbuf[97], &intbuf[98], &intbuf[99], &intbuf[100], &intbuf[101], &intbuf[102], &intbuf[103]); + m_interpolator32.myInterpolateSup(&intbuf[104], &intbuf[105], &intbuf[106], &intbuf[107], &intbuf[108], &intbuf[109], &intbuf[110], &intbuf[111]); + m_interpolator32.myInterpolateSup(&intbuf[112], &intbuf[113], &intbuf[114], &intbuf[115], &intbuf[116], &intbuf[117], &intbuf[118], &intbuf[119]); + m_interpolator32.myInterpolateSup(&intbuf[120], &intbuf[121], &intbuf[122], &intbuf[123], &intbuf[124], &intbuf[125], &intbuf[126], &intbuf[127]); + + for (int i = 0; i < 128; i++) { + buf[pos+i] = intbuf[i] / interpolation_shifts::post32; + } + } +} + +template +void InterpolatorsIF::interpolate64_cen(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[128]; + qint32 *bufI, *bufQ; + + if (invertIQ) + { + bufI = &intbuf[1]; + bufQ = &intbuf[0]; + } + else + { + bufI = &intbuf[0]; + bufQ = &intbuf[1]; + } + + for (int pos = 0; pos < len - 127; pos += 128) + { + memset(intbuf, 0, 128*sizeof(qint32)); + *bufI = (**it).m_real << interpolation_shifts::pre64; + *bufQ = (**it).m_imag << interpolation_shifts::pre64; + m_interpolator2.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[64], &intbuf[65]); + + m_interpolator4.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[32], &intbuf[33]); + m_interpolator4.myInterpolate(&intbuf[64], &intbuf[65], &intbuf[96], &intbuf[97]); + + m_interpolator8.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[16], &intbuf[17]); + m_interpolator8.myInterpolate(&intbuf[32], &intbuf[33], &intbuf[48], &intbuf[49]); + m_interpolator8.myInterpolate(&intbuf[64], &intbuf[65], &intbuf[80], &intbuf[81]); + m_interpolator8.myInterpolate(&intbuf[96], &intbuf[97], &intbuf[112], &intbuf[113]); + + m_interpolator16.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[8], &intbuf[9]); + m_interpolator16.myInterpolate(&intbuf[16], &intbuf[17], &intbuf[24], &intbuf[25]); + m_interpolator16.myInterpolate(&intbuf[32], &intbuf[33], &intbuf[40], &intbuf[41]); + m_interpolator16.myInterpolate(&intbuf[48], &intbuf[49], &intbuf[56], &intbuf[57]); + m_interpolator16.myInterpolate(&intbuf[64], &intbuf[65], &intbuf[72], &intbuf[73]); + m_interpolator16.myInterpolate(&intbuf[80], &intbuf[81], &intbuf[88], &intbuf[89]); + m_interpolator16.myInterpolate(&intbuf[96], &intbuf[97], &intbuf[104], &intbuf[105]); + m_interpolator16.myInterpolate(&intbuf[112], &intbuf[113], &intbuf[120], &intbuf[121]); + + m_interpolator32.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[4], &intbuf[5]); + m_interpolator32.myInterpolate(&intbuf[8], &intbuf[9], &intbuf[12], &intbuf[13]); + m_interpolator32.myInterpolate(&intbuf[16], &intbuf[17], &intbuf[20], &intbuf[21]); + m_interpolator32.myInterpolate(&intbuf[24], &intbuf[25], &intbuf[28], &intbuf[29]); + m_interpolator32.myInterpolate(&intbuf[32], &intbuf[33], &intbuf[36], &intbuf[37]); + m_interpolator32.myInterpolate(&intbuf[40], &intbuf[41], &intbuf[44], &intbuf[45]); + m_interpolator32.myInterpolate(&intbuf[48], &intbuf[49], &intbuf[52], &intbuf[53]); + m_interpolator32.myInterpolate(&intbuf[56], &intbuf[57], &intbuf[60], &intbuf[61]); + m_interpolator32.myInterpolate(&intbuf[64], &intbuf[65], &intbuf[68], &intbuf[69]); + m_interpolator32.myInterpolate(&intbuf[72], &intbuf[73], &intbuf[76], &intbuf[77]); + m_interpolator32.myInterpolate(&intbuf[80], &intbuf[81], &intbuf[84], &intbuf[85]); + m_interpolator32.myInterpolate(&intbuf[88], &intbuf[89], &intbuf[92], &intbuf[93]); + m_interpolator32.myInterpolate(&intbuf[96], &intbuf[97], &intbuf[100], &intbuf[101]); + m_interpolator32.myInterpolate(&intbuf[104], &intbuf[105], &intbuf[108], &intbuf[109]); + m_interpolator32.myInterpolate(&intbuf[112], &intbuf[113], &intbuf[116], &intbuf[117]); + m_interpolator32.myInterpolate(&intbuf[120], &intbuf[121], &intbuf[124], &intbuf[125]); + + m_interpolator64.myInterpolate(&intbuf[0], &intbuf[1], &intbuf[2], &intbuf[3]); + m_interpolator64.myInterpolate(&intbuf[4], &intbuf[5], &intbuf[6], &intbuf[7]); + m_interpolator64.myInterpolate(&intbuf[8], &intbuf[9], &intbuf[10], &intbuf[11]); + m_interpolator64.myInterpolate(&intbuf[12], &intbuf[13], &intbuf[14], &intbuf[15]); + m_interpolator64.myInterpolate(&intbuf[16], &intbuf[17], &intbuf[18], &intbuf[19]); + m_interpolator64.myInterpolate(&intbuf[20], &intbuf[21], &intbuf[22], &intbuf[23]); + m_interpolator64.myInterpolate(&intbuf[24], &intbuf[25], &intbuf[26], &intbuf[27]); + m_interpolator64.myInterpolate(&intbuf[28], &intbuf[29], &intbuf[30], &intbuf[31]); + m_interpolator64.myInterpolate(&intbuf[32], &intbuf[33], &intbuf[34], &intbuf[35]); + m_interpolator64.myInterpolate(&intbuf[36], &intbuf[37], &intbuf[38], &intbuf[39]); + m_interpolator64.myInterpolate(&intbuf[40], &intbuf[41], &intbuf[42], &intbuf[43]); + m_interpolator64.myInterpolate(&intbuf[44], &intbuf[45], &intbuf[46], &intbuf[47]); + m_interpolator64.myInterpolate(&intbuf[48], &intbuf[49], &intbuf[50], &intbuf[51]); + m_interpolator64.myInterpolate(&intbuf[52], &intbuf[53], &intbuf[54], &intbuf[55]); + m_interpolator64.myInterpolate(&intbuf[56], &intbuf[57], &intbuf[58], &intbuf[59]); + m_interpolator64.myInterpolate(&intbuf[60], &intbuf[61], &intbuf[62], &intbuf[63]); + m_interpolator64.myInterpolate(&intbuf[64], &intbuf[65], &intbuf[66], &intbuf[67]); + m_interpolator64.myInterpolate(&intbuf[68], &intbuf[69], &intbuf[70], &intbuf[71]); + m_interpolator64.myInterpolate(&intbuf[72], &intbuf[73], &intbuf[74], &intbuf[75]); + m_interpolator64.myInterpolate(&intbuf[76], &intbuf[77], &intbuf[78], &intbuf[79]); + m_interpolator64.myInterpolate(&intbuf[80], &intbuf[81], &intbuf[82], &intbuf[83]); + m_interpolator64.myInterpolate(&intbuf[84], &intbuf[85], &intbuf[86], &intbuf[87]); + m_interpolator64.myInterpolate(&intbuf[88], &intbuf[89], &intbuf[90], &intbuf[91]); + m_interpolator64.myInterpolate(&intbuf[92], &intbuf[93], &intbuf[94], &intbuf[95]); + m_interpolator64.myInterpolate(&intbuf[96], &intbuf[97], &intbuf[98], &intbuf[99]); + m_interpolator64.myInterpolate(&intbuf[100], &intbuf[101], &intbuf[102], &intbuf[103]); + m_interpolator64.myInterpolate(&intbuf[104], &intbuf[105], &intbuf[106], &intbuf[107]); + m_interpolator64.myInterpolate(&intbuf[108], &intbuf[109], &intbuf[110], &intbuf[111]); + m_interpolator64.myInterpolate(&intbuf[112], &intbuf[113], &intbuf[114], &intbuf[115]); + m_interpolator64.myInterpolate(&intbuf[116], &intbuf[117], &intbuf[118], &intbuf[119]); + m_interpolator64.myInterpolate(&intbuf[120], &intbuf[121], &intbuf[122], &intbuf[123]); + m_interpolator64.myInterpolate(&intbuf[124], &intbuf[125], &intbuf[126], &intbuf[127]); + + buf[pos+0] = intbuf[0] / interpolation_shifts::post64; + buf[pos+1] = intbuf[1] / interpolation_shifts::post64; + buf[pos+2] = intbuf[2] / interpolation_shifts::post64; + buf[pos+3] = intbuf[3] / interpolation_shifts::post64; + buf[pos+4] = intbuf[4] / interpolation_shifts::post64; + buf[pos+5] = intbuf[5] / interpolation_shifts::post64; + buf[pos+6] = intbuf[6] / interpolation_shifts::post64; + buf[pos+7] = intbuf[7] / interpolation_shifts::post64; + buf[pos+8] = intbuf[8] / interpolation_shifts::post64; + buf[pos+9] = intbuf[9] / interpolation_shifts::post64; + buf[pos+10] = intbuf[10] / interpolation_shifts::post64; + buf[pos+11] = intbuf[11] / interpolation_shifts::post64; + buf[pos+12] = intbuf[12] / interpolation_shifts::post64; + buf[pos+13] = intbuf[13] / interpolation_shifts::post64; + buf[pos+14] = intbuf[14] / interpolation_shifts::post64; + buf[pos+15] = intbuf[15] / interpolation_shifts::post64; + buf[pos+16] = intbuf[16] / interpolation_shifts::post64; + buf[pos+17] = intbuf[17] / interpolation_shifts::post64; + buf[pos+18] = intbuf[18] / interpolation_shifts::post64; + buf[pos+19] = intbuf[19] / interpolation_shifts::post64; + buf[pos+20] = intbuf[20] / interpolation_shifts::post64; + buf[pos+21] = intbuf[21] / interpolation_shifts::post64; + buf[pos+22] = intbuf[22] / interpolation_shifts::post64; + buf[pos+23] = intbuf[23] / interpolation_shifts::post64; + buf[pos+24] = intbuf[24] / interpolation_shifts::post64; + buf[pos+25] = intbuf[25] / interpolation_shifts::post64; + buf[pos+26] = intbuf[26] / interpolation_shifts::post64; + buf[pos+27] = intbuf[27] / interpolation_shifts::post64; + buf[pos+28] = intbuf[28] / interpolation_shifts::post64; + buf[pos+29] = intbuf[29] / interpolation_shifts::post64; + buf[pos+30] = intbuf[30] / interpolation_shifts::post64; + buf[pos+31] = intbuf[31] / interpolation_shifts::post64; + buf[pos+32] = intbuf[32] / interpolation_shifts::post64; + buf[pos+33] = intbuf[33] / interpolation_shifts::post64; + buf[pos+34] = intbuf[34] / interpolation_shifts::post64; + buf[pos+35] = intbuf[35] / interpolation_shifts::post64; + buf[pos+36] = intbuf[36] / interpolation_shifts::post64; + buf[pos+37] = intbuf[37] / interpolation_shifts::post64; + buf[pos+38] = intbuf[38] / interpolation_shifts::post64; + buf[pos+39] = intbuf[39] / interpolation_shifts::post64; + buf[pos+40] = intbuf[40] / interpolation_shifts::post64; + buf[pos+41] = intbuf[41] / interpolation_shifts::post64; + buf[pos+42] = intbuf[42] / interpolation_shifts::post64; + buf[pos+43] = intbuf[43] / interpolation_shifts::post64; + buf[pos+44] = intbuf[44] / interpolation_shifts::post64; + buf[pos+45] = intbuf[45] / interpolation_shifts::post64; + buf[pos+46] = intbuf[46] / interpolation_shifts::post64; + buf[pos+47] = intbuf[47] / interpolation_shifts::post64; + buf[pos+48] = intbuf[48] / interpolation_shifts::post64; + buf[pos+49] = intbuf[49] / interpolation_shifts::post64; + buf[pos+50] = intbuf[50] / interpolation_shifts::post64; + buf[pos+51] = intbuf[51] / interpolation_shifts::post64; + buf[pos+52] = intbuf[52] / interpolation_shifts::post64; + buf[pos+53] = intbuf[53] / interpolation_shifts::post64; + buf[pos+54] = intbuf[54] / interpolation_shifts::post64; + buf[pos+55] = intbuf[55] / interpolation_shifts::post64; + buf[pos+56] = intbuf[56] / interpolation_shifts::post64; + buf[pos+57] = intbuf[57] / interpolation_shifts::post64; + buf[pos+58] = intbuf[58] / interpolation_shifts::post64; + buf[pos+59] = intbuf[59] / interpolation_shifts::post64; + buf[pos+60] = intbuf[60] / interpolation_shifts::post64; + buf[pos+61] = intbuf[61] / interpolation_shifts::post64; + buf[pos+62] = intbuf[62] / interpolation_shifts::post64; + buf[pos+63] = intbuf[63] / interpolation_shifts::post64; + buf[pos+64] = intbuf[64] / interpolation_shifts::post64; + buf[pos+65] = intbuf[65] / interpolation_shifts::post64; + buf[pos+66] = intbuf[66] / interpolation_shifts::post64; + buf[pos+67] = intbuf[67] / interpolation_shifts::post64; + buf[pos+68] = intbuf[68] / interpolation_shifts::post64; + buf[pos+69] = intbuf[69] / interpolation_shifts::post64; + buf[pos+70] = intbuf[70] / interpolation_shifts::post64; + buf[pos+71] = intbuf[71] / interpolation_shifts::post64; + buf[pos+72] = intbuf[72] / interpolation_shifts::post64; + buf[pos+73] = intbuf[73] / interpolation_shifts::post64; + buf[pos+74] = intbuf[74] / interpolation_shifts::post64; + buf[pos+75] = intbuf[75] / interpolation_shifts::post64; + buf[pos+76] = intbuf[76] / interpolation_shifts::post64; + buf[pos+77] = intbuf[77] / interpolation_shifts::post64; + buf[pos+78] = intbuf[78] / interpolation_shifts::post64; + buf[pos+79] = intbuf[79] / interpolation_shifts::post64; + buf[pos+80] = intbuf[80] / interpolation_shifts::post64; + buf[pos+81] = intbuf[81] / interpolation_shifts::post64; + buf[pos+82] = intbuf[82] / interpolation_shifts::post64; + buf[pos+83] = intbuf[83] / interpolation_shifts::post64; + buf[pos+84] = intbuf[84] / interpolation_shifts::post64; + buf[pos+85] = intbuf[85] / interpolation_shifts::post64; + buf[pos+86] = intbuf[86] / interpolation_shifts::post64; + buf[pos+87] = intbuf[87] / interpolation_shifts::post64; + buf[pos+88] = intbuf[88] / interpolation_shifts::post64; + buf[pos+89] = intbuf[89] / interpolation_shifts::post64; + buf[pos+90] = intbuf[90] / interpolation_shifts::post64; + buf[pos+91] = intbuf[91] / interpolation_shifts::post64; + buf[pos+92] = intbuf[92] / interpolation_shifts::post64; + buf[pos+93] = intbuf[93] / interpolation_shifts::post64; + buf[pos+94] = intbuf[94] / interpolation_shifts::post64; + buf[pos+95] = intbuf[95] / interpolation_shifts::post64; + buf[pos+96] = intbuf[96] / interpolation_shifts::post64; + buf[pos+97] = intbuf[97] / interpolation_shifts::post64; + buf[pos+98] = intbuf[98] / interpolation_shifts::post64; + buf[pos+99] = intbuf[99] / interpolation_shifts::post64; + buf[pos+100] = intbuf[100] / interpolation_shifts::post64; + buf[pos+101] = intbuf[101] / interpolation_shifts::post64; + buf[pos+102] = intbuf[102] / interpolation_shifts::post64; + buf[pos+103] = intbuf[103] / interpolation_shifts::post64; + buf[pos+104] = intbuf[104] / interpolation_shifts::post64; + buf[pos+105] = intbuf[105] / interpolation_shifts::post64; + buf[pos+106] = intbuf[106] / interpolation_shifts::post64; + buf[pos+107] = intbuf[107] / interpolation_shifts::post64; + buf[pos+108] = intbuf[108] / interpolation_shifts::post64; + buf[pos+109] = intbuf[109] / interpolation_shifts::post64; + + ++(*it); + } +} + +template +void InterpolatorsIF::interpolate64_inf(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[256]; + qint32 *bufI0, *bufQ0, *bufI1, *bufQ1; + + if (invertIQ) + { + bufI0 = &intbuf[1]; + bufQ0 = &intbuf[0]; + bufI1 = &intbuf[129]; + bufQ1 = &intbuf[128]; + } + else + { + bufI0 = &intbuf[0]; + bufQ0 = &intbuf[1]; + bufI1 = &intbuf[128]; + bufQ1 = &intbuf[129]; + } + + for (int pos = 0; pos < len - 255; pos += 256) + { + memset(intbuf, 0, 256*sizeof(qint32)); + *bufI0 = (**it).m_real << interpolation_shifts::pre64; + *bufQ0 = (**it).m_imag << interpolation_shifts::pre64; + ++(*it); + *bufI1 = (**it).m_real << interpolation_shifts::pre64; + *bufQ1 = (**it).m_imag << interpolation_shifts::pre64; + ++(*it); + + m_interpolator2.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[64], &intbuf[65], &intbuf[128], &intbuf[129], &intbuf[192], &intbuf[193]); + + m_interpolator4.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[32], &intbuf[33], &intbuf[64], &intbuf[65], &intbuf[96], &intbuf[97]); + m_interpolator4.myInterpolateInf(&intbuf[128], &intbuf[129], &intbuf[160], &intbuf[161], &intbuf[192], &intbuf[193], &intbuf[224], &intbuf[225]); + + m_interpolator8.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[16], &intbuf[17], &intbuf[32], &intbuf[33], &intbuf[48], &intbuf[49]); + m_interpolator8.myInterpolateSup(&intbuf[64], &intbuf[65], &intbuf[80], &intbuf[81], &intbuf[96], &intbuf[97], &intbuf[112], &intbuf[113]); + m_interpolator8.myInterpolateSup(&intbuf[128], &intbuf[129], &intbuf[144], &intbuf[145], &intbuf[160], &intbuf[161], &intbuf[176], &intbuf[177]); + m_interpolator8.myInterpolateSup(&intbuf[192], &intbuf[193], &intbuf[208], &intbuf[209], &intbuf[224], &intbuf[225], &intbuf[240], &intbuf[241]); + + m_interpolator16.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[8], &intbuf[9], &intbuf[16], &intbuf[17], &intbuf[24], &intbuf[25]); + m_interpolator16.myInterpolateInf(&intbuf[32], &intbuf[33], &intbuf[40], &intbuf[41], &intbuf[48], &intbuf[49], &intbuf[56], &intbuf[57]); + m_interpolator16.myInterpolateInf(&intbuf[64], &intbuf[65], &intbuf[72], &intbuf[73], &intbuf[80], &intbuf[81], &intbuf[88], &intbuf[89]); + m_interpolator16.myInterpolateInf(&intbuf[96], &intbuf[97], &intbuf[104], &intbuf[105], &intbuf[112], &intbuf[113], &intbuf[120], &intbuf[121]); + m_interpolator16.myInterpolateInf(&intbuf[128], &intbuf[129], &intbuf[136], &intbuf[137], &intbuf[144], &intbuf[145], &intbuf[152], &intbuf[153]); + m_interpolator16.myInterpolateInf(&intbuf[160], &intbuf[161], &intbuf[168], &intbuf[169], &intbuf[176], &intbuf[177], &intbuf[184], &intbuf[185]); + m_interpolator16.myInterpolateInf(&intbuf[192], &intbuf[193], &intbuf[200], &intbuf[201], &intbuf[208], &intbuf[209], &intbuf[216], &intbuf[217]); + m_interpolator16.myInterpolateInf(&intbuf[224], &intbuf[225], &intbuf[232], &intbuf[233], &intbuf[240], &intbuf[241], &intbuf[248], &intbuf[249]); + + for (int i = 0; i < 16; i++) { + m_interpolator32.myInterpolateSup( + &intbuf[16*i+0], + &intbuf[16*i+1], + &intbuf[16*i+4], + &intbuf[16*i+5], + &intbuf[16*i+8], + &intbuf[16*i+9], + &intbuf[16*i+12], + &intbuf[16*i+13]); + } + + for (int i = 0; i < 32; i++) { + m_interpolator64.myInterpolateInf( + &intbuf[8*i+0], + &intbuf[8*i+1], + &intbuf[8*i+2], + &intbuf[8*i+3], + &intbuf[8*i+4], + &intbuf[8*i+5], + &intbuf[8*i+6], + &intbuf[8*i+7]); + } + + for (int i = 0; i < 256; i++) { + buf[pos+i] = intbuf[i] / interpolation_shifts::post64; + } + } +} + +template +void InterpolatorsIF::interpolate64_sup(SampleVector::iterator* it, float* buf, qint32 len, bool invertIQ) +{ + qint32 intbuf[256]; + qint32 *bufI0, *bufQ0, *bufI1, *bufQ1; + + if (invertIQ) + { + bufI0 = &intbuf[1]; + bufQ0 = &intbuf[0]; + bufI1 = &intbuf[129]; + bufQ1 = &intbuf[128]; + } + else + { + bufI0 = &intbuf[0]; + bufQ0 = &intbuf[1]; + bufI1 = &intbuf[128]; + bufQ1 = &intbuf[129]; + } + + for (int pos = 0; pos < len - 255; pos += 256) + { + memset(intbuf, 0, 256*sizeof(qint32)); + *bufI0 = (**it).m_real << interpolation_shifts::pre64; + *bufQ0 = (**it).m_imag << interpolation_shifts::pre64; + ++(*it); + *bufI1 = (**it).m_real << interpolation_shifts::pre64; + *bufQ1 = (**it).m_imag << interpolation_shifts::pre64; + ++(*it); + + m_interpolator2.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[64], &intbuf[65], &intbuf[128], &intbuf[129], &intbuf[192], &intbuf[193]); + + m_interpolator4.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[32], &intbuf[33], &intbuf[64], &intbuf[65], &intbuf[96], &intbuf[97]); + m_interpolator4.myInterpolateSup(&intbuf[128], &intbuf[129], &intbuf[160], &intbuf[161], &intbuf[192], &intbuf[193], &intbuf[224], &intbuf[225]); + + m_interpolator8.myInterpolateInf(&intbuf[0], &intbuf[1], &intbuf[16], &intbuf[17], &intbuf[32], &intbuf[33], &intbuf[48], &intbuf[49]); + m_interpolator8.myInterpolateInf(&intbuf[64], &intbuf[65], &intbuf[80], &intbuf[81], &intbuf[96], &intbuf[97], &intbuf[112], &intbuf[113]); + m_interpolator8.myInterpolateInf(&intbuf[128], &intbuf[129], &intbuf[144], &intbuf[145], &intbuf[160], &intbuf[161], &intbuf[176], &intbuf[177]); + m_interpolator8.myInterpolateInf(&intbuf[192], &intbuf[193], &intbuf[208], &intbuf[209], &intbuf[224], &intbuf[225], &intbuf[240], &intbuf[241]); + + m_interpolator16.myInterpolateSup(&intbuf[0], &intbuf[1], &intbuf[8], &intbuf[9], &intbuf[16], &intbuf[17], &intbuf[24], &intbuf[25]); + m_interpolator16.myInterpolateSup(&intbuf[32], &intbuf[33], &intbuf[40], &intbuf[41], &intbuf[48], &intbuf[49], &intbuf[56], &intbuf[57]); + m_interpolator16.myInterpolateSup(&intbuf[64], &intbuf[65], &intbuf[72], &intbuf[73], &intbuf[80], &intbuf[81], &intbuf[88], &intbuf[89]); + m_interpolator16.myInterpolateSup(&intbuf[96], &intbuf[97], &intbuf[104], &intbuf[105], &intbuf[112], &intbuf[113], &intbuf[120], &intbuf[121]); + m_interpolator16.myInterpolateSup(&intbuf[128], &intbuf[129], &intbuf[136], &intbuf[137], &intbuf[144], &intbuf[145], &intbuf[152], &intbuf[153]); + m_interpolator16.myInterpolateSup(&intbuf[160], &intbuf[161], &intbuf[168], &intbuf[169], &intbuf[176], &intbuf[177], &intbuf[184], &intbuf[185]); + m_interpolator16.myInterpolateSup(&intbuf[192], &intbuf[193], &intbuf[200], &intbuf[201], &intbuf[208], &intbuf[209], &intbuf[216], &intbuf[217]); + m_interpolator16.myInterpolateSup(&intbuf[224], &intbuf[225], &intbuf[232], &intbuf[233], &intbuf[240], &intbuf[241], &intbuf[248], &intbuf[249]); + + for (int i = 0; i < 16; i++) { + m_interpolator32.myInterpolateInf( + &intbuf[16*i+0], + &intbuf[16*i+1], + &intbuf[16*i+4], + &intbuf[16*i+5], + &intbuf[16*i+8], + &intbuf[16*i+9], + &intbuf[16*i+12], + &intbuf[16*i+13]); + } + + for (int i = 0; i < 32; i++) { + m_interpolator64.myInterpolateSup( + &intbuf[8*i+0], + &intbuf[8*i+1], + &intbuf[8*i+2], + &intbuf[8*i+3], + &intbuf[8*i+4], + &intbuf[8*i+5], + &intbuf[8*i+6], + &intbuf[8*i+7]); + } + + for (int i = 0; i < 256; i++) { + buf[pos+i] = intbuf[i] / interpolation_shifts::post64; + } + } +} + +#endif // INCLUDE_SDRBASE_DSP_INTERPOLATORSIF_H_ \ No newline at end of file diff --git a/sdrbase/dsp/samplesinkfifo.cpp b/sdrbase/dsp/samplesinkfifo.cpp index 1ae77ea92..f769a202b 100644 --- a/sdrbase/dsp/samplesinkfifo.cpp +++ b/sdrbase/dsp/samplesinkfifo.cpp @@ -54,6 +54,17 @@ SampleSinkFifo::SampleSinkFifo(int size, QObject* parent) : create(size); } +SampleSinkFifo::SampleSinkFifo(const SampleSinkFifo& other) : + QObject(other.parent()), + m_data(other.m_data) +{ + m_suppressed = -1; + m_size = m_data.size(); + m_fill = 0; + m_head = 0; + m_tail = 0; +} + SampleSinkFifo::~SampleSinkFifo() { QMutexLocker mutexLocker(&m_mutex); diff --git a/sdrbase/dsp/samplesinkfifo.h b/sdrbase/dsp/samplesinkfifo.h index 5ea42fe15..4d71b6a79 100644 --- a/sdrbase/dsp/samplesinkfifo.h +++ b/sdrbase/dsp/samplesinkfifo.h @@ -43,8 +43,9 @@ private: void create(uint s); public: - SampleSinkFifo(QObject* parent = NULL); - SampleSinkFifo(int size, QObject* parent = NULL); + SampleSinkFifo(QObject* parent = nullptr); + SampleSinkFifo(int size, QObject* parent = nullptr); + SampleSinkFifo(const SampleSinkFifo& other); ~SampleSinkFifo(); bool setSize(int size); diff --git a/sdrbase/dsp/samplesourcefifo.cpp b/sdrbase/dsp/samplesourcefifo.cpp index 47ef02ed2..edfd6044e 100644 --- a/sdrbase/dsp/samplesourcefifo.cpp +++ b/sdrbase/dsp/samplesourcefifo.cpp @@ -19,7 +19,8 @@ #include #include "samplesourcefifo.h" -SampleSourceFifo::SampleSourceFifo(uint32_t size) : +SampleSourceFifo::SampleSourceFifo(uint32_t size, QObject* parent) : + QObject(parent), m_size(size), m_init(false) { @@ -27,6 +28,12 @@ SampleSourceFifo::SampleSourceFifo(uint32_t size) : init(); } +SampleSourceFifo::SampleSourceFifo(const SampleSourceFifo& other) : + QObject(other.parent()), + m_data(other.m_data) +{ + init(); +} SampleSourceFifo::~SampleSourceFifo() {} diff --git a/sdrbase/dsp/samplesourcefifo.h b/sdrbase/dsp/samplesourcefifo.h index 6878160b6..672f95282 100644 --- a/sdrbase/dsp/samplesourcefifo.h +++ b/sdrbase/dsp/samplesourcefifo.h @@ -29,7 +29,8 @@ class SDRBASE_API SampleSourceFifo : public QObject { Q_OBJECT public: - SampleSourceFifo(uint32_t size); + SampleSourceFifo(uint32_t size, QObject* parent = nullptr); + SampleSourceFifo(const SampleSourceFifo& other); ~SampleSourceFifo(); void resize(uint32_t size); diff --git a/sdrbase/mainparser.cpp b/sdrbase/mainparser.cpp index b7b383ec5..b1e935ea2 100644 --- a/sdrbase/mainparser.cpp +++ b/sdrbase/mainparser.cpp @@ -30,10 +30,12 @@ MainParser::MainParser() : m_serverPortOption(QStringList() << "p" << "api-port", "Web API server port.", "port", - "8091") + "8091"), + m_mimoOption("mimo", "Activate MIMO functionality") { m_serverAddress = "127.0.0.1"; m_serverPort = 8091; + m_mimoOption.setFlags(QCommandLineOption::HiddenFromHelp); m_parser.setApplicationDescription("Software Defined Radio application"); m_parser.addHelpOption(); @@ -41,6 +43,7 @@ MainParser::MainParser() : m_parser.addOption(m_serverAddressOption); m_parser.addOption(m_serverPortOption); + m_parser.addOption(m_mimoOption); } MainParser::~MainParser() @@ -80,4 +83,8 @@ void MainParser::parse(const QCoreApplication& app) } else { qWarning() << "MainParser::parse: server port invalid. Defaulting to " << m_serverPort; } + + // MIMO + + m_mimoSupport = m_parser.isSet(m_mimoOption); } diff --git a/sdrbase/mainparser.h b/sdrbase/mainparser.h index 130153c91..197eb19eb 100644 --- a/sdrbase/mainparser.h +++ b/sdrbase/mainparser.h @@ -34,14 +34,17 @@ public: const QString& getServerAddress() const { return m_serverAddress; } uint16_t getServerPort() const { return m_serverPort; } + bool getMIMOSupport() const { return m_mimoSupport; } private: QString m_serverAddress; uint16_t m_serverPort; + bool m_mimoSupport; QCommandLineParser m_parser; QCommandLineOption m_serverAddressOption; QCommandLineOption m_serverPortOption; + QCommandLineOption m_mimoOption; }; diff --git a/sdrbase/plugin/pluginapi.cpp b/sdrbase/plugin/pluginapi.cpp index ae9198394..963d00ae2 100644 --- a/sdrbase/plugin/pluginapi.cpp +++ b/sdrbase/plugin/pluginapi.cpp @@ -26,6 +26,11 @@ void PluginAPI::registerSampleSink(const QString& sinkName, PluginInterface* plu m_pluginManager->registerSampleSink(sinkName, plugin); } +void PluginAPI::registerSampleMIMO(const QString& mimoName, PluginInterface* plugin) +{ + m_pluginManager->registerSampleMIMO(mimoName, plugin); +} + PluginAPI::ChannelRegistrations *PluginAPI::getTxChannelRegistrations() { return m_pluginManager->getTxChannelRegistrations(); diff --git a/sdrbase/plugin/pluginapi.h b/sdrbase/plugin/pluginapi.h index 3f9206014..b4d1090fb 100644 --- a/sdrbase/plugin/pluginapi.h +++ b/sdrbase/plugin/pluginapi.h @@ -57,6 +57,9 @@ public: // Sample Sink stuff void registerSampleSink(const QString& sinkName, PluginInterface* plugin); + // Sample MIMO stuff + void registerSampleMIMO(const QString& sinkName, PluginInterface* plugin); + protected: PluginManager* m_pluginManager; diff --git a/sdrbase/plugin/plugininterface.cpp b/sdrbase/plugin/plugininterface.cpp index 458379fd6..857e5fb1e 100644 --- a/sdrbase/plugin/plugininterface.cpp +++ b/sdrbase/plugin/plugininterface.cpp @@ -1,6 +1,7 @@ #include #include "dsp/devicesamplesource.h" #include "dsp/devicesamplesink.h" +#include "dsp/devicesamplemimo.h" #include "plugin/plugininterface.h" @@ -23,3 +24,13 @@ void PluginInterface::deleteSampleSinkPluginInstanceOutput(DeviceSampleSink *sin { if (sink) { sink->destroy(); } } + +void PluginInterface::deleteSampleMIMOPluginInstanceGUI(PluginInstanceGUI *ui) +{ + if (ui) { ui->destroy(); } +} + +void PluginInterface::deleteSampleMIMOPluginInstanceMIMO(DeviceSampleMIMO *mimo) +{ + if (mimo) { mimo->destroy(); } +} diff --git a/sdrbase/plugin/plugininterface.h b/sdrbase/plugin/plugininterface.h index 352ce42f5..436b4a53b 100644 --- a/sdrbase/plugin/plugininterface.h +++ b/sdrbase/plugin/plugininterface.h @@ -23,6 +23,7 @@ class PluginInstanceGUI; class QWidget; class DeviceSampleSource; class DeviceSampleSink; +class DeviceSampleMIMO; class BasebandSampleSink; class BasebandSampleSource; class ChannelAPI; @@ -41,7 +42,7 @@ public: { StreamSingleRx, //!< Exposes a single input stream that can be one of the streams of a physical device StreamSingleTx, //!< Exposes a single output stream that can be one of the streams of a physical device - StreamAny //!< May expose any number of input and/or output streams + StreamMIMO //!< May expose any number of input and/or output streams }; QString displayedName; //!< The human readable name @@ -148,7 +149,7 @@ public: return nullptr; } - virtual DeviceSampleSource* createSampleSourcePluginInstanceInput( // creates the input "core" + virtual DeviceSampleSource* createSampleSourcePluginInstance( // creates the input "core" const QString& sourceId, DeviceAPI *deviceAPI) { @@ -174,7 +175,7 @@ public: return nullptr; } - virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput( // creates the output "core" + virtual DeviceSampleSink* createSampleSinkPluginInstance( // creates the output "core" const QString& sinkId, DeviceAPI *deviceAPI) { @@ -185,6 +186,34 @@ public: virtual void deleteSampleSinkPluginInstanceGUI(PluginInstanceGUI *ui); virtual void deleteSampleSinkPluginInstanceOutput(DeviceSampleSink *sink); + + // device MIMO plugins only + + virtual SamplingDevices enumSampleMIMO() { return SamplingDevices(); } + + virtual PluginInstanceGUI* createSampleMIMOPluginInstanceGUI( + const QString& mimoId, + QWidget **widget, + DeviceUISet *deviceUISet) + { + (void) mimoId; + (void) widget; + (void) deviceUISet; + return nullptr; + } + + virtual DeviceSampleMIMO* createSampleMIMOPluginInstance( // creates the MIMO "core" + const QString& mimoId, + DeviceAPI *deviceAPI) + { + (void) mimoId; + (void) deviceAPI; + return nullptr; + } + + virtual void deleteSampleMIMOPluginInstanceGUI(PluginInstanceGUI *ui); + virtual void deleteSampleMIMOPluginInstanceMIMO(DeviceSampleMIMO *mimo); + }; Q_DECLARE_INTERFACE(PluginInterface, "SDRangel.PluginInterface/0.1"); diff --git a/sdrbase/plugin/pluginmanager.cpp b/sdrbase/plugin/pluginmanager.cpp index 63ebe5a08..ac8779ee7 100644 --- a/sdrbase/plugin/pluginmanager.cpp +++ b/sdrbase/plugin/pluginmanager.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// Copyright (C) 2016-2019 Edouard Griffiths, F4EXB // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // @@ -17,7 +17,6 @@ #include #include -//#include #include #include @@ -45,6 +44,9 @@ const QString PluginManager::m_remoteOutputDeviceTypeID = "sdrangel.samplesink.r const QString PluginManager::m_fileSinkHardwareID = "FileSink"; const QString PluginManager::m_fileSinkDeviceTypeID = "sdrangel.samplesink.filesink"; +const QString PluginManager::m_testMIMOHardwareID = "TestMI"; +const QString PluginManager::m_testMIMODeviceTypeID = "sdrangel.samplemimo.testmi"; + PluginManager::PluginManager(QObject* parent) : QObject(parent), m_pluginAPI(this) @@ -112,6 +114,7 @@ void PluginManager::loadPluginsFinal() DeviceEnumerator::instance()->enumerateRxDevices(this); DeviceEnumerator::instance()->enumerateTxDevices(this); + DeviceEnumerator::instance()->enumerateMIMODevices(this); } void PluginManager::registerRxChannel(const QString& channelIdURI, const QString& channelId, PluginInterface* plugin) @@ -150,6 +153,15 @@ void PluginManager::registerSampleSink(const QString& sinkName, PluginInterface* m_sampleSinkRegistrations.append(PluginAPI::SamplingDeviceRegistration(sinkName, plugin)); } +void PluginManager::registerSampleMIMO(const QString& mimoName, PluginInterface* plugin) +{ + qDebug() << "PluginManager::registerSampleMIMO " + << plugin->getPluginDescriptor().displayedName.toStdString().c_str() + << " with MIMO name " << mimoName.toStdString().c_str(); + + m_sampleMIMORegistrations.append(PluginAPI::SamplingDeviceRegistration(mimoName, plugin)); +} + void PluginManager::loadPluginsDir(const QDir& dir) { QDir pluginsDir(dir); diff --git a/sdrbase/plugin/pluginmanager.h b/sdrbase/plugin/pluginmanager.h index a2accbdd6..c2f71b797 100644 --- a/sdrbase/plugin/pluginmanager.h +++ b/sdrbase/plugin/pluginmanager.h @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016-2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + #ifndef INCLUDE_PLUGINMANAGER_H #define INCLUDE_PLUGINMANAGER_H @@ -48,10 +65,12 @@ public: void registerRxChannel(const QString& channelIdURI, const QString& channelId, PluginInterface* plugin); void registerSampleSource(const QString& sourceName, PluginInterface* plugin); void registerTxChannel(const QString& channelIdURI, const QString& channelId, PluginInterface* plugin); - void registerSampleSink(const QString& sourceName, PluginInterface* plugin); + void registerSampleSink(const QString& sinkName, PluginInterface* plugin); + void registerSampleMIMO(const QString& mimoName, PluginInterface* plugin); PluginAPI::SamplingDeviceRegistrations& getSourceDeviceRegistrations() { return m_sampleSourceRegistrations; } PluginAPI::SamplingDeviceRegistrations& getSinkDeviceRegistrations() { return m_sampleSinkRegistrations; } + PluginAPI::SamplingDeviceRegistrations& getMIMODeviceRegistrations() { return m_sampleMIMORegistrations; } PluginAPI::ChannelRegistrations *getRxChannelRegistrations() { return &m_rxChannelRegistrations; } PluginAPI::ChannelRegistrations *getTxChannelRegistrations() { return &m_txChannelRegistrations; } @@ -63,6 +82,7 @@ public: static const QString& getFileSourceDeviceId() { return m_fileSourceDeviceTypeID; } static const QString& getFileSinkDeviceId() { return m_fileSinkDeviceTypeID; } + static const QString& getTestMIMODeviceId() { return m_testMIMODeviceTypeID; } private: struct SamplingDevice { //!< This is the device registration @@ -99,6 +119,8 @@ private: PluginAPI::ChannelRegistrations m_txChannelRegistrations; //!< Channel plugins register here PluginAPI::SamplingDeviceRegistrations m_sampleSinkRegistrations; //!< Output sink plugins (one per device kind) register here + PluginAPI::SamplingDeviceRegistrations m_sampleMIMORegistrations; //!< MIMO sink plugins (one per device kind) register here + // "Local" sample source device IDs static const QString m_localInputHardwareID; //!< Local input hardware ID static const QString m_localInputDeviceTypeID; //!< Local input plugin ID @@ -115,6 +137,10 @@ private: static const QString m_fileSinkHardwareID; //!< FileSource source hardware ID static const QString m_fileSinkDeviceTypeID; //!< FileSink sink plugin ID + // "Local" sample MIMO device IDs + static const QString m_testMIMOHardwareID; //!< Test MIMO hardware ID + static const QString m_testMIMODeviceTypeID; //!< Test MIMO plugin ID + void loadPluginsDir(const QDir& dir); }; diff --git a/sdrbase/resources/webapi.qrc b/sdrbase/resources/webapi.qrc index 3d45a2f40..424197f06 100644 --- a/sdrbase/resources/webapi.qrc +++ b/sdrbase/resources/webapi.qrc @@ -39,6 +39,7 @@ webapi/doc/swagger/include/SSBMod.yaml webapi/doc/swagger/include/Structs.yaml webapi/doc/swagger/include/TestSource.yaml + webapi/doc/swagger/include/TestMI.yaml webapi/doc/swagger/include/UDPSource.yaml webapi/doc/swagger/include/UDPSink.yaml webapi/doc/swagger/include/WFMDemod.yaml diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 35ecdf5f6..b840b4e26 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -761,6 +761,10 @@ margin-bottom: 20px; "type" : "integer", "description" : "Synchronous AM sidebands mode (DSB, USB, LSB)" }, + "streamIndex" : { + "type" : "integer", + "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." + }, "useReverseAPI" : { "type" : "integer", "description" : "Synchronize with reverse API (1 for yes, 0 for no)" @@ -2335,6 +2339,9 @@ margin-bottom: 20px; "testSourceSettings" : { "$ref" : "#/definitions/TestSourceSettings" }, + "testMISettings" : { + "$ref" : "#/definitions/TestMISettings" + }, "xtrxInputSettings" : { "$ref" : "#/definitions/XtrxInputSettings" }, @@ -3576,6 +3583,9 @@ margin-bottom: 20px; "ctcssOn" : { "type" : "integer" }, + "highPass" : { + "type" : "integer" + }, "audioMute" : { "type" : "integer" }, @@ -5203,6 +5213,80 @@ margin-bottom: 20px; "type" : "string" } } +}; + defs.TestMISettings = { + "properties" : { + "centerFrequency" : { + "type" : "integer", + "format" : "uint64" + }, + "frequencyShift" : { + "type" : "integer" + }, + "sampleRate" : { + "type" : "integer" + }, + "log2Decim" : { + "type" : "integer" + }, + "fcPos" : { + "type" : "integer" + }, + "sampleSizeIndex" : { + "type" : "integer" + }, + "amplitudeBits" : { + "type" : "integer" + }, + "autoCorrOptions" : { + "type" : "integer" + }, + "modulation" : { + "type" : "integer" + }, + "modulationTone" : { + "type" : "integer" + }, + "amModulation" : { + "type" : "integer" + }, + "fmDeviation" : { + "type" : "integer" + }, + "dcFactor" : { + "type" : "number", + "format" : "float" + }, + "iFactor" : { + "type" : "number", + "format" : "float" + }, + "qFactor" : { + "type" : "number", + "format" : "float" + }, + "phaseImbalance" : { + "type" : "number", + "format" : "float" + }, + "fileRecordName" : { + "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + } + }, + "description" : "TestSourceMI" }; defs.TestSourceSettings = { "properties" : { @@ -25000,7 +25084,7 @@ except ApiException as e:
- Generated 2019-05-10T14:58:45.508+02:00 + Generated 2019-05-20T16:12:41.467+02:00
diff --git a/sdrbase/resources/webapi/doc/swagger/include/AMDemod.yaml b/sdrbase/resources/webapi/doc/swagger/include/AMDemod.yaml index 22aaa5aa4..506b9a28f 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/AMDemod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/AMDemod.yaml @@ -2,7 +2,7 @@ AMDemodSettings: description: AMDemod properties: inputFrequencyOffset: - description: channel center frequency shift from baseband center in Hz + description: channel center frequency shift from baseband center in Hz type: integer format: int64 rfBandwidth: @@ -33,6 +33,9 @@ AMDemodSettings: syncAMOperation: description: Synchronous AM sidebands mode (DSB, USB, LSB) type: integer + streamIndex: + description: MIMO channel. Not relevant when connected to SI (single Rx). + type: integer useReverseAPI: description: Synchronize with reverse API (1 for yes, 0 for no) type: integer @@ -41,9 +44,9 @@ AMDemodSettings: reverseAPIPort: type: integer reverseAPIDeviceIndex: - type: integer + type: integer reverseAPIChannelIndex: - type: integer + type: integer AMDemodReport: description: AMDemod @@ -59,4 +62,3 @@ AMDemodReport: type: integer channelSampleRate: type: integer - \ No newline at end of file diff --git a/sdrbase/resources/webapi/doc/swagger/include/NFMDemod.yaml b/sdrbase/resources/webapi/doc/swagger/include/NFMDemod.yaml index 660d4201a..f1e6e43be 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/NFMDemod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/NFMDemod.yaml @@ -29,6 +29,8 @@ NFMDemodSettings: format: float ctcssOn: type: integer + highPass: + type: integer audioMute: type: integer ctcssIndex: diff --git a/sdrbase/resources/webapi/doc/swagger/include/TestMI.yaml b/sdrbase/resources/webapi/doc/swagger/include/TestMI.yaml new file mode 100644 index 000000000..16008a5e8 --- /dev/null +++ b/sdrbase/resources/webapi/doc/swagger/include/TestMI.yaml @@ -0,0 +1,51 @@ +TestMISettings: + description: TestSourceMI + properties: + centerFrequency: + type: integer + format: uint64 + frequencyShift: + type: integer + sampleRate: + type: integer + log2Decim: + type: integer + fcPos: + type: integer + sampleSizeIndex: + type: integer + amplitudeBits: + type: integer + autoCorrOptions: + type: integer + modulation: + type: integer + modulationTone: + type: integer + amModulation: + type: integer + fmDeviation: + type: integer + dcFactor: + type: number + format: float + iFactor: + type: number + format: float + qFactor: + type: number + format: float + phaseImbalance: + type: number + format: float + fileRecordName: + type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer diff --git a/sdrbase/resources/webapi/doc/swagger/include/TestSourceMI.yaml b/sdrbase/resources/webapi/doc/swagger/include/TestSourceMI.yaml new file mode 100644 index 000000000..763108033 --- /dev/null +++ b/sdrbase/resources/webapi/doc/swagger/include/TestSourceMI.yaml @@ -0,0 +1,51 @@ +TestSourceMISettings: + description: TestSourceMI + properties: + centerFrequency: + type: integer + format: uint64 + frequencyShift: + type: integer + sampleRate: + type: integer + log2Decim: + type: integer + fcPos: + type: integer + sampleSizeIndex: + type: integer + amplitudeBits: + type: integer + autoCorrOptions: + type: integer + modulation: + type: integer + modulationTone: + type: integer + amModulation: + type: integer + fmDeviation: + type: integer + dcFactor: + type: number + format: float + iFactor: + type: number + format: float + qFactor: + type: number + format: float + phaseImbalance: + type: number + format: float + fileRecordName: + type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer diff --git a/sdrbase/resources/webapi/doc/swagger/swagger.yaml b/sdrbase/resources/webapi/doc/swagger/swagger.yaml index b311cd066..8d6a6fbfc 100644 --- a/sdrbase/resources/webapi/doc/swagger/swagger.yaml +++ b/sdrbase/resources/webapi/doc/swagger/swagger.yaml @@ -1830,6 +1830,8 @@ definitions: $ref: "/doc/swagger/include/SoapySDR.yaml#/SoapySDROutputSettings" testSourceSettings: $ref: "/doc/swagger/include/TestSource.yaml#/TestSourceSettings" + testMISettings: + $ref: "/doc/swagger/include/TestMI.yaml#/TestMISettings" xtrxInputSettings: $ref: "/doc/swagger/include/Xtrx.yaml#/XtrxInputSettings" xtrxOutputSettings: diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 5a2190ccc..39667022c 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -366,7 +366,6 @@ void WebAPIRequestMapper::instanceAudioService(qtwebapp::HttpRequest& request, q void WebAPIRequestMapper::instanceAudioInputParametersService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) { - // TODO SWGSDRangel::SWGErrorResponse errorResponse; response.setHeader("Content-Type", "application/json"); response.setHeader("Access-Control-Allow-Origin", "*"); diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index a36178b21..3be7206bb 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -22,6 +22,7 @@ set(sdrgui_SOURCES gui/comboboxnoarrow.cpp gui/crightclickenabler.cpp gui/cwkeyergui.cpp + gui/devicestreamselectiondialog.cpp gui/editcommanddialog.cpp gui/externalclockbutton.cpp gui/externalclockdialog.cpp @@ -42,6 +43,7 @@ set(sdrgui_SOURCES gui/samplingdevicecontrol.cpp gui/samplingdevicedialog.cpp gui/scaleengine.cpp + gui/sdrangelsplash.cpp gui/tickedslider.cpp gui/transverterbutton.cpp gui/transverterdialog.cpp @@ -88,6 +90,7 @@ set(sdrgui_HEADERS gui/comboboxnoarrow.h gui/crightclickenabler.h gui/cwkeyergui.h + gui/devicestreamselectiondialog.h gui/editcommanddialog.h gui/externalclockbutton.h gui/externalclockdialog.h @@ -109,6 +112,7 @@ set(sdrgui_HEADERS gui/samplingdevicecontrol.h gui/samplingdevicedialog.h gui/scaleengine.h + gui/sdrangelsplash.h gui/tickedslider.h gui/transverterbutton.h gui/transverterdialog.h @@ -144,6 +148,7 @@ set(sdrgui_FORMS gui/basicdevicesettingsdialog.ui gui/commandoutputdialog.ui gui/cwkeyergui.ui + gui/devicestreamselectiondialog.ui gui/editcommanddialog.ui gui/externalclockdialog.ui gui/glscopegui.ui diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index c0266ec1c..ac1084235 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -31,23 +31,27 @@ #include "deviceuiset.h" -DeviceUISet::DeviceUISet(int tabIndex, bool rxElseTx, QTimer& timer) +DeviceUISet::DeviceUISet(int tabIndex, int deviceType, QTimer& timer) { m_spectrum = new GLSpectrum; - if (rxElseTx) { + if ((deviceType == 0) || (deviceType == 2)) { // Single Rx or MIMO m_spectrumVis = new SpectrumVis(SDR_RX_SCALEF, m_spectrum); - } else { + } else if (deviceType == 1) { // Single Tx m_spectrumVis = new SpectrumVis(SDR_TX_SCALEF, m_spectrum); } m_spectrum->connectTimer(timer); m_spectrumGUI = new GLSpectrumGUI; m_spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, m_spectrum); m_channelWindow = new ChannelWindow; - m_samplingDeviceControl = new SamplingDeviceControl(tabIndex, rxElseTx); - m_deviceSourceEngine = 0; - m_deviceAPI = 0; - m_deviceSinkEngine = 0; + m_samplingDeviceControl = new SamplingDeviceControl(tabIndex, deviceType); + m_deviceAPI = nullptr; + m_deviceSourceEngine = nullptr; + m_deviceSinkEngine = nullptr; + m_deviceMIMOEngine = nullptr; m_deviceTabIndex = tabIndex; + m_nbAvailableRxChannels = 0; // updated at enumeration for UI selector + m_nbAvailableTxChannels = 0; // updated at enumeration for UI selector + m_nbAvailableMIMOChannels = 0; // updated at enumeration for UI selector // m_spectrum needs to have its font to be set since it cannot be inherited from the main window QFont font; diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index 2b9c98950..cff2da450 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -28,9 +28,10 @@ class GLSpectrum; class GLSpectrumGUI; class ChannelWindow; class SamplingDeviceControl; -class DSPDeviceSourceEngine; class DeviceAPI; +class DSPDeviceSourceEngine; class DSPDeviceSinkEngine; +class DSPDeviceMIMOEngine; class ChannelMarker; class PluginAPI; class PluginInstanceGUI; @@ -44,12 +45,13 @@ public: GLSpectrumGUI *m_spectrumGUI; ChannelWindow *m_channelWindow; SamplingDeviceControl *m_samplingDeviceControl; - DSPDeviceSourceEngine *m_deviceSourceEngine; DeviceAPI *m_deviceAPI; + DSPDeviceSourceEngine *m_deviceSourceEngine; DSPDeviceSinkEngine *m_deviceSinkEngine; + DSPDeviceMIMOEngine *m_deviceMIMOEngine; QByteArray m_mainWindowState; - DeviceUISet(int tabIndex, bool rxElseTx, QTimer& timer); + DeviceUISet(int tabIndex, int deviceType, QTimer& timer); ~DeviceUISet(); GLSpectrum *getSpectrum() { return m_spectrum; } //!< Direct spectrum getter @@ -71,6 +73,14 @@ public: void loadTxChannelSettings(const Preset* preset, PluginAPI *pluginAPI); void saveTxChannelSettings(Preset* preset); + // These are the number of channel types available for selection + void setNumberOfAvailableRxChannels(int number) { m_nbAvailableRxChannels = number; } + void setNumberOfAvailableTxChannels(int number) { m_nbAvailableTxChannels = number; } + void setNumberOfAvailableMIMOChannels(int number) { m_nbAvailableMIMOChannels = number; } + int getNumberOfAvailableRxChannels() const { return m_nbAvailableRxChannels; } + int getNumberOfAvailableTxChannels() const { return m_nbAvailableTxChannels; } + int getNumberOfAvailableMIMOChannels() const { return m_nbAvailableMIMOChannels; } + private: struct ChannelInstanceRegistration { @@ -95,6 +105,9 @@ private: ChannelInstanceRegistrations m_rxChannelInstanceRegistrations; ChannelInstanceRegistrations m_txChannelInstanceRegistrations; int m_deviceTabIndex; + int m_nbAvailableRxChannels; //!< Number of Rx channels available for selection + int m_nbAvailableTxChannels; //!< Number of Tx channels available for selection + int m_nbAvailableMIMOChannels; //!< Number of MIMO channels available for selection void renameRxChannelInstances(); void renameTxChannelInstances(); diff --git a/sdrgui/gui/devicestreamselectiondialog.cpp b/sdrgui/gui/devicestreamselectiondialog.cpp new file mode 100644 index 000000000..20e97a907 --- /dev/null +++ b/sdrgui/gui/devicestreamselectiondialog.cpp @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "ui_devicestreamselectiondialog.h" +#include "devicestreamselectiondialog.h" + +DeviceStreamSelectionDialog::DeviceStreamSelectionDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::DeviceStreamSelectionDialog), + m_hasChanged(false) +{ + ui->setupUi(this); + setStreamIndex(0); +} + +DeviceStreamSelectionDialog::~DeviceStreamSelectionDialog() +{ + delete ui; +} + +void DeviceStreamSelectionDialog::setNumberOfStreams(int nbStreams) +{ + ui->deviceStream->clear(); + + for (int i = 0; i < nbStreams; i++) { + ui->deviceStream->addItem(tr("%1").arg(i)); + } +} + +void DeviceStreamSelectionDialog::setStreamIndex(int index) +{ + ui->deviceStream->setCurrentIndex(index); + m_streamIndex = ui->deviceStream->currentIndex(); +} + +void DeviceStreamSelectionDialog::accept() +{ + m_streamIndex = ui->deviceStream->currentIndex(); + m_hasChanged = true; + QDialog::accept(); +} \ No newline at end of file diff --git a/sdrgui/gui/devicestreamselectiondialog.h b/sdrgui/gui/devicestreamselectiondialog.h new file mode 100644 index 000000000..7dc249bb9 --- /dev/null +++ b/sdrgui/gui/devicestreamselectiondialog.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef _SDRGUI_DEVICESTREAMSELECTIONDIALOG_H_ +#define _SDRGUI_DEVICESTREAMSELECTIONDIALOG_H_ + +#include + +#include "../../exports/export.h" + +namespace Ui { + class DeviceStreamSelectionDialog; +} + +class SDRGUI_API DeviceStreamSelectionDialog : public QDialog +{ + Q_OBJECT +public: + explicit DeviceStreamSelectionDialog(QWidget *parent = nullptr); + ~DeviceStreamSelectionDialog(); + bool hasChanged() const { return m_hasChanged; } + void setNumberOfStreams(int nbStreams); + void setStreamIndex(int index); + int getSelectedStreamIndex() const { return m_streamIndex; }; + +private slots: + void accept(); + +private: + Ui::DeviceStreamSelectionDialog *ui; + bool m_hasChanged; + int m_streamIndex; +}; + +#endif // _SDRGUI_DEVICESTREAMSELECTIONDIALOG_H_ \ No newline at end of file diff --git a/sdrgui/gui/devicestreamselectiondialog.ui b/sdrgui/gui/devicestreamselectiondialog.ui new file mode 100644 index 000000000..13687f005 --- /dev/null +++ b/sdrgui/gui/devicestreamselectiondialog.ui @@ -0,0 +1,101 @@ + + + DeviceStreamSelectionDialog + + + + 0 + 0 + 360 + 72 + + + + + Liberation Sans + 9 + + + + Select device stream + + + + + + + + Stream + + + + + + + Devcie stream index + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + DeviceStreamSelectionDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DeviceStreamSelectionDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/sdrgui/gui/glspectrum.cpp b/sdrgui/gui/glspectrum.cpp index 54be4ba3e..94e686129 100644 --- a/sdrgui/gui/glspectrum.cpp +++ b/sdrgui/gui/glspectrum.cpp @@ -52,6 +52,7 @@ GLSpectrum::GLSpectrum(QWidget* parent) : m_displayMaxHold(false), m_currentSpectrum(0), m_displayCurrent(false), + m_leftMargin(0), m_waterfallBuffer(0), m_waterfallBufferPos(0), m_waterfallTextureHeight(-1), @@ -1036,7 +1037,7 @@ void GLSpectrum::applyChanges() int frequencyScaleTop = 0; int histogramTop = 0; int histogramHeight = 20; - int leftMargin; + //int m_leftMargin; int rightMargin = fm.width("000"); // displays both histogram and waterfall @@ -1092,46 +1093,46 @@ void GLSpectrum::applyChanges() m_powerScale.setRange(Unit::Decibel, m_referenceLevel - m_powerRange, m_referenceLevel); } - leftMargin = m_timeScale.getScaleWidth(); + m_leftMargin = m_timeScale.getScaleWidth(); - if(m_powerScale.getScaleWidth() > leftMargin) + if(m_powerScale.getScaleWidth() > m_leftMargin) { - leftMargin = m_powerScale.getScaleWidth(); + m_leftMargin = m_powerScale.getScaleWidth(); } - leftMargin += 2 * M; + m_leftMargin += 2 * M; - m_frequencyScale.setSize(width() - leftMargin - rightMargin); + m_frequencyScale.setSize(width() - m_leftMargin - rightMargin); m_frequencyScale.setRange(Unit::Frequency, m_centerFrequency - m_sampleRate / 2, m_centerFrequency + m_sampleRate / 2); m_frequencyScale.setMakeOpposite(m_lsbDisplay); m_glWaterfallBoxMatrix.setToIdentity(); m_glWaterfallBoxMatrix.translate( - -1.0f + ((float)(2*leftMargin) / (float) width()), + -1.0f + ((float)(2*m_leftMargin) / (float) width()), 1.0f - ((float)(2*waterfallTop) / (float) height()) ); m_glWaterfallBoxMatrix.scale( - ((float) 2 * (width() - leftMargin - rightMargin)) / (float) width(), + ((float) 2 * (width() - m_leftMargin - rightMargin)) / (float) width(), (float) (-2*waterfallHeight) / (float) height() ); m_glHistogramBoxMatrix.setToIdentity(); m_glHistogramBoxMatrix.translate( - -1.0f + ((float)(2*leftMargin) / (float) width()), + -1.0f + ((float)(2*m_leftMargin) / (float) width()), 1.0f - ((float)(2*histogramTop) / (float) height()) ); m_glHistogramBoxMatrix.scale( - ((float) 2 * (width() - leftMargin - rightMargin)) / (float) width(), + ((float) 2 * (width() - m_leftMargin - rightMargin)) / (float) width(), (float) (-2*histogramHeight) / (float) height() ); m_glHistogramSpectrumMatrix.setToIdentity(); m_glHistogramSpectrumMatrix.translate( - -1.0f + ((float)(2*leftMargin) / (float) width()), + -1.0f + ((float)(2*m_leftMargin) / (float) width()), 1.0f - ((float)(2*histogramTop) / (float) height()) ); m_glHistogramSpectrumMatrix.scale( - ((float) 2 * (width() - leftMargin - rightMargin)) / ((float) width() * (float)(m_fftSize - 1)), + ((float) 2 * (width() - m_leftMargin - rightMargin)) / ((float) width() * (float)(m_fftSize - 1)), ((float) 2*histogramHeight / height()) / m_powerRange ); @@ -1155,7 +1156,7 @@ void GLSpectrum::applyChanges() m_glLeftScaleBoxMatrix.setToIdentity(); m_glLeftScaleBoxMatrix.translate(-1.0f, 1.0f); m_glLeftScaleBoxMatrix.scale( - (float)(2*(leftMargin - 1)) / (float) width(), + (float)(2*(m_leftMargin - 1)) / (float) width(), -2.0f ); } @@ -1195,20 +1196,20 @@ void GLSpectrum::applyChanges() } } - leftMargin = m_timeScale.getScaleWidth(); - leftMargin += 2 * M; + m_leftMargin = m_timeScale.getScaleWidth(); + m_leftMargin += 2 * M; - m_frequencyScale.setSize(width() - leftMargin - rightMargin); + m_frequencyScale.setSize(width() - m_leftMargin - rightMargin); m_frequencyScale.setRange(Unit::Frequency, m_centerFrequency - m_sampleRate / 2.0, m_centerFrequency + m_sampleRate / 2.0); m_frequencyScale.setMakeOpposite(m_lsbDisplay); m_glWaterfallBoxMatrix.setToIdentity(); m_glWaterfallBoxMatrix.translate( - -1.0f + ((float)(2*leftMargin) / (float) width()), + -1.0f + ((float)(2*m_leftMargin) / (float) width()), 1.0f - ((float)(2*topMargin) / (float) height()) ); m_glWaterfallBoxMatrix.scale( - ((float) 2 * (width() - leftMargin - rightMargin)) / (float) width(), + ((float) 2 * (width() - m_leftMargin - rightMargin)) / (float) width(), (float) (-2*waterfallHeight) / (float) height() ); @@ -1232,7 +1233,7 @@ void GLSpectrum::applyChanges() m_glLeftScaleBoxMatrix.setToIdentity(); m_glLeftScaleBoxMatrix.translate(-1.0f, 1.0f); m_glLeftScaleBoxMatrix.scale( - (float)(2*(leftMargin - 1)) / (float) width(), + (float)(2*(m_leftMargin - 1)) / (float) width(), -2.0f ); } @@ -1247,30 +1248,30 @@ void GLSpectrum::applyChanges() m_powerScale.setSize(histogramHeight); m_powerScale.setRange(Unit::Decibel, m_referenceLevel - m_powerRange, m_referenceLevel); - leftMargin = m_powerScale.getScaleWidth(); - leftMargin += 2 * M; + m_leftMargin = m_powerScale.getScaleWidth(); + m_leftMargin += 2 * M; - m_frequencyScale.setSize(width() - leftMargin - rightMargin); + m_frequencyScale.setSize(width() - m_leftMargin - rightMargin); m_frequencyScale.setRange(Unit::Frequency, m_centerFrequency - m_sampleRate / 2, m_centerFrequency + m_sampleRate / 2); m_frequencyScale.setMakeOpposite(m_lsbDisplay); m_glHistogramSpectrumMatrix.setToIdentity(); m_glHistogramSpectrumMatrix.translate( - -1.0f + ((float)(2*leftMargin) / (float) width()), + -1.0f + ((float)(2*m_leftMargin) / (float) width()), 1.0f - ((float)(2*histogramTop) / (float) height()) ); m_glHistogramSpectrumMatrix.scale( - ((float) 2 * (width() - leftMargin - rightMargin)) / ((float) width() * (float)(m_fftSize - 1)), + ((float) 2 * (width() - m_leftMargin - rightMargin)) / ((float) width() * (float)(m_fftSize - 1)), ((float) 2*(height() - topMargin - frequencyScaleHeight) / height()) / m_powerRange ); m_glHistogramBoxMatrix.setToIdentity(); m_glHistogramBoxMatrix.translate( - -1.0f + ((float)(2*leftMargin) / (float) width()), + -1.0f + ((float)(2*m_leftMargin) / (float) width()), 1.0f - ((float)(2*histogramTop) / (float) height()) ); m_glHistogramBoxMatrix.scale( - ((float) 2 * (width() - leftMargin - rightMargin)) / (float) width(), + ((float) 2 * (width() - m_leftMargin - rightMargin)) / (float) width(), (float) (-2*(height() - topMargin - frequencyScaleHeight)) / (float) height() ); @@ -1294,13 +1295,13 @@ void GLSpectrum::applyChanges() m_glLeftScaleBoxMatrix.setToIdentity(); m_glLeftScaleBoxMatrix.translate(-1.0f, 1.0f); m_glLeftScaleBoxMatrix.scale( - (float)(2*(leftMargin - 1)) / (float) width(), + (float)(2*(m_leftMargin - 1)) / (float) width(), -2.0f ); } else { - leftMargin = 2; + m_leftMargin = 2; waterfallHeight = 0; } @@ -1338,7 +1339,7 @@ void GLSpectrum::applyChanges() QMatrix4x4 glMatrixDsb; glMatrixDsb.setToIdentity(); glMatrixDsb.translate( - -1.0f + 2.0f * ((leftMargin + m_frequencyScale.getPosFromValue(xc - (dsbw/2))) / (float) width()), + -1.0f + 2.0f * ((m_leftMargin + m_frequencyScale.getPosFromValue(xc - (dsbw/2))) / (float) width()), 1.0f ); glMatrixDsb.scale( @@ -1352,7 +1353,7 @@ void GLSpectrum::applyChanges() (float) waterfallTop / (float) height() ); dv->m_glMatrixDsbWaterfall.scale( - (float) (width() - leftMargin - rightMargin) / (float) width(), + (float) (width() - m_leftMargin - rightMargin) / (float) width(), (float) waterfallHeight / (float) height() ); @@ -1362,7 +1363,7 @@ void GLSpectrum::applyChanges() (float) histogramTop / (float) height() ); dv->m_glMatrixDsbHistogram.scale( - (float) (width() - leftMargin - rightMargin) / (float) width(), + (float) (width() - m_leftMargin - rightMargin) / (float) width(), (float) histogramHeight / (float) height() ); @@ -1372,7 +1373,7 @@ void GLSpectrum::applyChanges() (float) frequencyScaleTop / (float) height() ); dv->m_glMatrixDsbFreqScale.scale( - (float) (width() - leftMargin - rightMargin) / (float) width(), + (float) (width() - m_leftMargin - rightMargin) / (float) width(), (float) frequencyScaleHeight / (float) height() ); @@ -1381,7 +1382,7 @@ void GLSpectrum::applyChanges() QMatrix4x4 glMatrix; glMatrix.setToIdentity(); glMatrix.translate( - -1.0f + 2.0f * ((leftMargin + m_frequencyScale.getPosFromValue(xc + nw)) / (float) width()), + -1.0f + 2.0f * ((m_leftMargin + m_frequencyScale.getPosFromValue(xc + nw)) / (float) width()), 1.0f ); glMatrix.scale( @@ -1395,7 +1396,7 @@ void GLSpectrum::applyChanges() (float) waterfallTop / (float) height() ); dv->m_glMatrixWaterfall.scale( - (float) (width() - leftMargin - rightMargin) / (float) width(), + (float) (width() - m_leftMargin - rightMargin) / (float) width(), (float) waterfallHeight / (float) height() ); @@ -1405,7 +1406,7 @@ void GLSpectrum::applyChanges() (float) histogramTop / (float) height() ); dv->m_glMatrixHistogram.scale( - (float) (width() - leftMargin - rightMargin) / (float) width(), + (float) (width() - m_leftMargin - rightMargin) / (float) width(), (float) histogramHeight / (float) height() ); @@ -1415,14 +1416,14 @@ void GLSpectrum::applyChanges() (float) frequencyScaleTop / (float) height() ); dv->m_glMatrixFreqScale.scale( - (float) (width() - leftMargin - rightMargin) / (float) width(), + (float) (width() - m_leftMargin - rightMargin) / (float) width(), (float) frequencyScaleHeight / (float) height() ); /* dv->m_glRect.setRect( - m_frequencyScale.getPosFromValue(m_centerFrequency + dv->m_channelMarker->getCenterFrequency() - dv->m_channelMarker->getBandwidth() / 2) / (float)(width() - leftMargin - rightMargin), + m_frequencyScale.getPosFromValue(m_centerFrequency + dv->m_channelMarker->getCenterFrequency() - dv->m_channelMarker->getBandwidth() / 2) / (float)(width() - m_leftMargin - rightMargin), 0, (dv->m_channelMarker->getBandwidth() / (float)m_sampleRate), 1); @@ -1430,7 +1431,7 @@ void GLSpectrum::applyChanges() if(m_displayHistogram || m_displayMaxHold || m_displayCurrent || m_displayWaterfall) { - dv->m_rect.setRect(m_frequencyScale.getPosFromValue(xc) + leftMargin - 1, + dv->m_rect.setRect(m_frequencyScale.getPosFromValue(xc) + m_leftMargin - 1, topMargin, 5, height() - topMargin - bottomMargin); @@ -1438,7 +1439,7 @@ void GLSpectrum::applyChanges() /* if(m_displayHistogram || m_displayMaxHold || m_displayWaterfall) { - dv->m_rect.setRect(m_frequencyScale.getPosFromValue(m_centerFrequency + dv->m_channelMarker->getCenterFrequency()) + leftMargin - 1, + dv->m_rect.setRect(m_frequencyScale.getPosFromValue(m_centerFrequency + dv->m_channelMarker->getCenterFrequency()) + m_leftMargin - 1, topMargin, 5, height() - topMargin - bottomMargin); @@ -1448,7 +1449,7 @@ void GLSpectrum::applyChanges() // prepare left scales (time and power) { - m_leftMarginPixmap = QPixmap(leftMargin - 1, height()); + m_leftMarginPixmap = QPixmap(m_leftMargin - 1, height()); m_leftMarginPixmap.fill(Qt::black); { QPainter painter(&m_leftMarginPixmap); @@ -1462,7 +1463,7 @@ void GLSpectrum::applyChanges() tick = &(*tickList)[i]; if(tick->major) { if(tick->textSize > 0) - painter.drawText(QPointF(leftMargin - M - tick->textSize, waterfallTop + fm.ascent() + tick->textPos), tick->text); + painter.drawText(QPointF(m_leftMargin - M - tick->textSize, waterfallTop + fm.ascent() + tick->textPos), tick->text); } } } @@ -1472,7 +1473,7 @@ void GLSpectrum::applyChanges() tick = &(*tickList)[i]; if(tick->major) { if(tick->textSize > 0) - painter.drawText(QPointF(leftMargin - M - tick->textSize, histogramTop + histogramHeight - tick->textPos - 1), tick->text); + painter.drawText(QPointF(m_leftMargin - M - tick->textSize, histogramTop + histogramHeight - tick->textPos - 1), tick->text); } } } @@ -1489,7 +1490,7 @@ void GLSpectrum::applyChanges() painter.setPen(Qt::NoPen); painter.setBrush(Qt::black); painter.setBrush(Qt::transparent); - painter.drawRect(leftMargin, 0, width() - leftMargin, frequencyScaleHeight); + painter.drawRect(m_leftMargin, 0, width() - m_leftMargin, frequencyScaleHeight); painter.setPen(QColor(0xf0, 0xf0, 0xff)); painter.setFont(font()); const ScaleEngine::TickList* tickList = &m_frequencyScale.getTickList(); @@ -1498,7 +1499,7 @@ void GLSpectrum::applyChanges() tick = &(*tickList)[i]; if(tick->major) { if(tick->textSize > 0) - painter.drawText(QPointF(leftMargin + tick->textPos, fm.height() + fm.ascent() / 2 - 1), tick->text); + painter.drawText(QPointF(m_leftMargin + tick->textPos, fm.height() + fm.ascent() / 2 - 1), tick->text); } } @@ -1537,7 +1538,7 @@ void GLSpectrum::applyChanges() ftext = ftext + " "; shift = - fm.width(ftext); } - painter.drawText(QPointF(leftMargin + m_frequencyScale.getPosFromValue(xc) + shift, 2*fm.height() + fm.ascent() / 2 - 1), ftext); + painter.drawText(QPointF(m_leftMargin + m_frequencyScale.getPosFromValue(xc) + shift, 2*fm.height() + fm.ascent() / 2 - 1), ftext); } } @@ -1741,6 +1742,52 @@ void GLSpectrum::mouseReleaseEvent(QMouseEvent*) } } +void GLSpectrum::wheelEvent(QWheelEvent *event) +{ + int mul; + + if (event->modifiers() & Qt::ShiftModifier) { + mul = 100; + } else if (event->modifiers() & Qt::ControlModifier) { + mul = 10; + } else { + mul = 1; + } + + for (int i = 0; i < m_channelMarkerStates.size(); ++i) + { + if (m_channelMarkerStates[i]->m_rect.contains(event->pos())) + { + int freq = m_channelMarkerStates[i]->m_channelMarker->getCenterFrequency(); + + if (event->delta() > 0) { + freq += 10 * mul; + } else if (event->delta() < 0) { + freq -= 10 * mul; + } + + // calculate scale relative cursor position for new frequency + float x_pos = m_frequencyScale.getPosFromValue(m_centerFrequency + freq); + + if ((x_pos >= 0.0) && (x_pos < m_frequencyScale.getSize())) // cursor must be in scale + { + m_channelMarkerStates[i]->m_channelMarker->setCenterFrequencyByCursor(freq); + m_channelMarkerStates[i]->m_channelMarker->setCenterFrequency(freq); + + // cursor follow-up + int xd = x_pos + m_leftMargin; + QCursor c = cursor(); + QPoint cp_a = c.pos(); + QPoint cp_w = mapFromGlobal(cp_a); + cp_w.setX(xd); + cp_a = mapToGlobal(cp_w); + c.setPos(cp_a); + setCursor(c); + } + } + } +} + void GLSpectrum::enterEvent(QEvent* event) { m_mouseInside = true; diff --git a/sdrgui/gui/glspectrum.h b/sdrgui/gui/glspectrum.h index 095e72cd1..053a173e4 100644 --- a/sdrgui/gui/glspectrum.h +++ b/sdrgui/gui/glspectrum.h @@ -151,6 +151,7 @@ private: Real m_waterfallShare; + int m_leftMargin; QPixmap m_leftMarginPixmap; QPixmap m_frequencyPixmap; ScaleEngine m_timeScale; @@ -211,6 +212,7 @@ private: void mouseMoveEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); + void wheelEvent(QWheelEvent*); void enterEvent(QEvent* event); void leaveEvent(QEvent* event); diff --git a/sdrgui/gui/rollupwidget.cpp b/sdrgui/gui/rollupwidget.cpp index db65407fe..e3e49fc29 100644 --- a/sdrgui/gui/rollupwidget.cpp +++ b/sdrgui/gui/rollupwidget.cpp @@ -119,11 +119,11 @@ int RollupWidget::arrangeRollups() for(int i = 0; i < children().count(); ++i) { QWidget* r = qobject_cast(children()[i]); - if(r != NULL) + if (r != nullptr) { pos += fm.height() + 2; - if(!r->isHidden() && (r->windowTitle() != "Basic channel settings")) + if (!r->isHidden() && (r->windowTitle() != "Basic channel settings") && (r->windowTitle() != "Select device stream")) { r->move(2, pos + 3); int h = 0; @@ -241,7 +241,9 @@ void RollupWidget::paintEvent(QPaintEvent*) int RollupWidget::paintRollup(QWidget* rollup, int pos, QPainter* p, bool last, const QColor& frame) { - if (rollup->windowTitle() == "Basic channel settings") return 0; + if ((rollup->windowTitle() == "Basic channel settings") || (rollup->windowTitle() == "Select device stream")) { + return 0; + } QFontMetrics fm(font()); int height = 1; @@ -322,7 +324,6 @@ void RollupWidget::mousePressEvent(QMouseEvent* event) return; } - // close button right if(QRectF(width() - 3.5 - fm.ascent(), 3.5, fm.ascent(), fm.ascent()).contains(event->pos())) { close(); @@ -384,3 +385,9 @@ bool RollupWidget::eventFilter(QObject* object, QEvent* event) } return QWidget::eventFilter(object, event); } + +void RollupWidget::setStreamIndicator(const QString& indicator) +{ + m_streamIndicator = indicator; + update(); +} \ No newline at end of file diff --git a/sdrgui/gui/rollupwidget.h b/sdrgui/gui/rollupwidget.h index 01d1def56..6bfd96c07 100644 --- a/sdrgui/gui/rollupwidget.h +++ b/sdrgui/gui/rollupwidget.h @@ -48,6 +48,7 @@ protected: bool eventFilter(QObject* object, QEvent* event); void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } + void setStreamIndicator(const QString& indicator); }; #endif // INCLUDE_ROLLUPWIDGET_H diff --git a/sdrgui/gui/samplingdevicecontrol.cpp b/sdrgui/gui/samplingdevicecontrol.cpp index 35a774997..43d67c29a 100644 --- a/sdrgui/gui/samplingdevicecontrol.cpp +++ b/sdrgui/gui/samplingdevicecontrol.cpp @@ -22,12 +22,12 @@ #include "ui_samplingdevicecontrol.h" -SamplingDeviceControl::SamplingDeviceControl(int tabIndex, bool rxElseTx, QWidget* parent) : +SamplingDeviceControl::SamplingDeviceControl(int tabIndex, int deviceType, QWidget* parent) : QWidget(parent), ui(new Ui::SamplingDeviceControl), m_pluginManager(0), m_deviceTabIndex(tabIndex), - m_rxElseTx(rxElseTx), + m_deviceType(deviceType), m_selectedDeviceIndex(-1) { ui->setupUi(this); @@ -41,7 +41,7 @@ SamplingDeviceControl::~SamplingDeviceControl() void SamplingDeviceControl::on_deviceChange_clicked() { - SamplingDeviceDialog dialog(m_rxElseTx, m_deviceTabIndex, this); + SamplingDeviceDialog dialog(m_deviceType, m_deviceTabIndex, this); dialog.exec(); if (dialog.getSelectedDeviceIndex() >= 0) @@ -61,30 +61,36 @@ void SamplingDeviceControl::on_deviceReload_clicked() void SamplingDeviceControl::setSelectedDeviceIndex(int index) { - if (m_rxElseTx) + if (m_deviceType == 0) // Single Rx { const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(index); DeviceEnumerator::instance()->changeRxSelection(m_deviceTabIndex, index); ui->deviceSelectedText->setText(samplingDevice->displayedName); } - else + else if (m_deviceType == 1) // Single Tx { const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(index); DeviceEnumerator::instance()->changeTxSelection(m_deviceTabIndex, index); ui->deviceSelectedText->setText(samplingDevice->displayedName); } + else if (m_deviceType == 2) // MIMO + { + const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getMIMOSamplingDevice(index); + DeviceEnumerator::instance()->changeMIMOSelection(m_deviceTabIndex, index); + ui->deviceSelectedText->setText(samplingDevice->displayedName); + } m_selectedDeviceIndex = index; } void SamplingDeviceControl::removeSelectedDeviceIndex() { - if (m_rxElseTx) + if (m_deviceType == 0) // Single Rx { DeviceEnumerator::instance()->removeRxSelection(m_deviceTabIndex); ui->deviceSelectedText->setText("None"); } - else + else if (m_deviceType == 1) // Single Tx { DeviceEnumerator::instance()->removeTxSelection(m_deviceTabIndex); ui->deviceSelectedText->setText("None"); diff --git a/sdrgui/gui/samplingdevicecontrol.h b/sdrgui/gui/samplingdevicecontrol.h index 46f767f85..d329f6627 100644 --- a/sdrgui/gui/samplingdevicecontrol.h +++ b/sdrgui/gui/samplingdevicecontrol.h @@ -36,7 +36,7 @@ class SDRGUI_API SamplingDeviceControl : public QWidget { Q_OBJECT public: - explicit SamplingDeviceControl(int tabIndex, bool rxElseTx, QWidget* parent = 0); + explicit SamplingDeviceControl(int tabIndex, int deviceType, QWidget* parent = 0); ~SamplingDeviceControl(); int getSelectedDeviceIndex() const { return m_selectedDeviceIndex; } @@ -55,7 +55,7 @@ private: Ui::SamplingDeviceControl* ui; PluginManager *m_pluginManager; int m_deviceTabIndex; - bool m_rxElseTx; + int m_deviceType; int m_selectedDeviceIndex; signals: diff --git a/sdrgui/gui/samplingdevicedialog.cpp b/sdrgui/gui/samplingdevicedialog.cpp index e2a2df4b7..59dddba6b 100644 --- a/sdrgui/gui/samplingdevicedialog.cpp +++ b/sdrgui/gui/samplingdevicedialog.cpp @@ -24,10 +24,10 @@ #include "device/deviceenumerator.h" -SamplingDeviceDialog::SamplingDeviceDialog(bool rxElseTx, int deviceTabIndex, QWidget* parent) : +SamplingDeviceDialog::SamplingDeviceDialog(int deviceType, int deviceTabIndex, QWidget* parent) : QDialog(parent), ui(new Ui::SamplingDeviceDialog), - m_rxElseTx(rxElseTx), + m_deviceType(deviceType), m_deviceTabIndex(deviceTabIndex), m_selectedDeviceIndex(-1) { @@ -35,10 +35,12 @@ SamplingDeviceDialog::SamplingDeviceDialog(bool rxElseTx, int deviceTabIndex, QW QList deviceDisplayNames; - if (m_rxElseTx) { + if (m_deviceType == 0) { // Single Rx DeviceEnumerator::instance()->listRxDeviceNames(deviceDisplayNames, m_deviceIndexes); - } else { + } else if (m_deviceType == 1) { // Single Tx DeviceEnumerator::instance()->listTxDeviceNames(deviceDisplayNames, m_deviceIndexes); + } else if (m_deviceType == 2) { // MIMO + DeviceEnumerator::instance()->listMIMODeviceNames(deviceDisplayNames, m_deviceIndexes); } QStringList devicesNamesList(deviceDisplayNames); @@ -54,10 +56,12 @@ void SamplingDeviceDialog::accept() { m_selectedDeviceIndex = m_deviceIndexes[ui->deviceSelect->currentIndex()]; - if (m_rxElseTx) { + if (m_deviceType == 0) { // Single Rx DeviceEnumerator::instance()->changeRxSelection(m_deviceTabIndex, m_selectedDeviceIndex); - } else { + } else if (m_deviceType == 1) { // Single Tx DeviceEnumerator::instance()->changeTxSelection(m_deviceTabIndex, m_selectedDeviceIndex); + } else if (m_deviceType == 2) { // MIMO + DeviceEnumerator::instance()->changeMIMOSelection(m_deviceTabIndex, m_selectedDeviceIndex); } QDialog::accept(); diff --git a/sdrgui/gui/samplingdevicedialog.h b/sdrgui/gui/samplingdevicedialog.h index 1235061b6..9b05f9876 100644 --- a/sdrgui/gui/samplingdevicedialog.h +++ b/sdrgui/gui/samplingdevicedialog.h @@ -35,13 +35,13 @@ class SDRGUI_API SamplingDeviceDialog : public QDialog { Q_OBJECT public: - explicit SamplingDeviceDialog(bool rxElseTx, int deviceTabIndex, QWidget* parent = 0); + explicit SamplingDeviceDialog(int deviceType, int deviceTabIndex, QWidget* parent = 0); ~SamplingDeviceDialog(); int getSelectedDeviceIndex() const { return m_selectedDeviceIndex; } private: Ui::SamplingDeviceDialog* ui; - bool m_rxElseTx; + int m_deviceType; int m_deviceTabIndex; int m_selectedDeviceIndex; std::vector m_deviceIndexes; diff --git a/sdrgui/gui/sdrangelsplash.cpp b/sdrgui/gui/sdrangelsplash.cpp new file mode 100644 index 000000000..f0fc4f5c4 --- /dev/null +++ b/sdrgui/gui/sdrangelsplash.cpp @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB. // +// // +// Swagger server adapter interface // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "sdrangelsplash.h" + +SDRangelSplash::SDRangelSplash(const QPixmap& pixmap) +{ + QSplashScreen::setPixmap(pixmap); +}; + +SDRangelSplash::~SDRangelSplash() +{ +}; + +void SDRangelSplash::drawContents(QPainter *painter) +{ + QPixmap textPix = QSplashScreen::pixmap(); + painter->setPen(this->color); + painter->drawText(this->rect, this->alignement, this->message); +}; + +void SDRangelSplash::showStatusMessage(const QString &message, const QColor &color) +{ + this->message = message; + this->color = color; + this->showMessage(this->message, this->alignement, this->color); +}; + +void SDRangelSplash::setMessageRect(QRect rect, int alignement) +{ + this->rect = rect; + this->alignement = alignement; +}; \ No newline at end of file diff --git a/sdrgui/gui/sdrangelsplash.h b/sdrgui/gui/sdrangelsplash.h new file mode 100644 index 000000000..7e6c0eae6 --- /dev/null +++ b/sdrgui/gui/sdrangelsplash.h @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB. // +// // +// Swagger server adapter interface // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRGUI_SDRANGELSPLASH_H +#define SDRGUI_SDRANGELSPLASH_H + +#include +#include + +class SDRangelSplash : public QSplashScreen +{ +public: + SDRangelSplash(const QPixmap& pixmap); + ~SDRangelSplash(); + virtual void drawContents(QPainter *painter); + void showStatusMessage(const QString &message, const QColor &color = Qt::black); + void setMessageRect(QRect rect, int alignment = Qt::AlignLeft); +private: + QString message; + int alignement; + QColor color; + QRect rect; +}; + +#endif // SDRGUI_SDRANGELSPLASHSCREEN_H \ No newline at end of file diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 4e5060a7c..1175d96b9 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -48,14 +48,17 @@ #include "gui/audiodialog.h" #include "gui/loggingdialog.h" #include "gui/samplingdevicecontrol.h" +#include "gui/sdrangelsplash.h" #include "gui/mypositiondialog.h" #include "dsp/dspengine.h" #include "dsp/spectrumvis.h" #include "dsp/dspcommands.h" #include "dsp/devicesamplesource.h" #include "dsp/devicesamplesink.h" +#include "dsp/devicesamplemimo.h" #include "dsp/dspdevicesourceengine.h" #include "dsp/dspdevicesinkengine.h" +#include "dsp/dspdevicemimoengine.h" #include "plugin/pluginapi.h" #include "gui/glspectrum.h" #include "gui/glspectrumgui.h" @@ -73,6 +76,7 @@ #include #include +#include MESSAGE_CLASS_DEFINITION(MainWindow::MsgLoadPreset, Message) MESSAGE_CLASS_DEFINITION(MainWindow::MsgSavePreset, Message) @@ -102,7 +106,6 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse qDebug() << "MainWindow::MainWindow: start"; m_instance = this; - m_settings.setAudioDeviceManager(m_dspEngine->getAudioDeviceManager()); QFontDatabase::addApplicationFont(":/LiberationSans-Regular.ttf"); QFontDatabase::addApplicationFont(":/LiberationMono-Regular.ttf"); @@ -111,6 +114,14 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse font.setPointSize(9); qApp->setFont(font); + QPixmap logoPixmap(":/sdrangel_logo.png"); + SDRangelSplash *splash = new SDRangelSplash(logoPixmap); + splash->setMessageRect(QRect(10, 80, 350, 12)); + splash->show(); + splash->showStatusMessage("starting...", Qt::white); + + m_settings.setAudioDeviceManager(m_dspEngine->getAudioDeviceManager()); + ui->setupUi(this); createStatusBar(); @@ -166,33 +177,40 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_masterTimer.start(50); + splash->showStatusMessage("load settings...", Qt::white); qDebug() << "MainWindow::MainWindow: load settings..."; loadSettings(); + splash->showStatusMessage("load plugins...", Qt::white); qDebug() << "MainWindow::MainWindow: load plugins..."; m_pluginManager = new PluginManager(this); m_pluginManager->loadPlugins(QString("plugins")); - qDebug() << "MainWindow::MainWindow: select SampleSource from settings or default (file source) ..."; + splash->showStatusMessage("load file source...", Qt::white); + qDebug() << "MainWindow::MainWindow: select SampleSource from settings or default (file source)..."; int deviceIndex = DeviceEnumerator::instance()->getRxSamplingDeviceIndex(m_settings.getSourceDeviceId(), m_settings.getSourceIndex()); addSourceDevice(deviceIndex); // add the first device set with file source device as default if device in settings is not enumerated m_deviceUIs.back()->m_deviceAPI->setBuddyLeader(true); // the first device is always the leader + splash->showStatusMessage("load current preset settings...", Qt::white); qDebug() << "MainWindow::MainWindow: load current preset settings..."; loadPresetSettings(m_settings.getWorkingPreset(), 0); + splash->showStatusMessage("apply settings...", Qt::white); qDebug() << "MainWindow::MainWindow: apply settings..."; applySettings(); + splash->showStatusMessage("update preset controls...", Qt::white); qDebug() << "MainWindow::MainWindow: update preset controls..."; updatePresetControls(); + splash->showStatusMessage("finishing...", Qt::white); connect(ui->tabInputsView, SIGNAL(currentChanged(int)), this, SLOT(tabInputViewIndexChanged())); QString applicationDirPath = qApp->applicationDirPath(); @@ -209,6 +227,10 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_commandKeyReceiver->setRelease(true); this->installEventFilter(m_commandKeyReceiver); + m_dspEngine->setMIMOSupport(parser.getMIMOSupport()); + + delete splash; + qDebug() << "MainWindow::MainWindow: end"; } @@ -240,13 +262,13 @@ void MainWindow::addSourceDevice(int deviceIndex) sprintf(uidCStr, "UID:%d", dspDeviceSourceEngineUID); int deviceTabIndex = m_deviceUIs.size(); - m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, true, m_masterTimer)); + m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, 0, m_masterTimer)); m_deviceUIs.back()->m_deviceSourceEngine = dspDeviceSourceEngine; char tabNameCStr[16]; sprintf(tabNameCStr, "R%d", deviceTabIndex); - DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamSingleRx, deviceTabIndex, dspDeviceSourceEngine, nullptr); + DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamSingleRx, deviceTabIndex, dspDeviceSourceEngine, nullptr, nullptr); m_deviceUIs.back()->m_deviceAPI = deviceAPI; m_deviceUIs.back()->m_samplingDeviceControl->setPluginManager(m_pluginManager); @@ -254,6 +276,7 @@ void MainWindow::addSourceDevice(int deviceIndex) m_pluginManager->listRxChannels(channelNames); QStringList channelNamesList(channelNames); m_deviceUIs.back()->m_samplingDeviceControl->getChannelSelector()->addItems(channelNamesList); + m_deviceUIs.back()->setNumberOfAvailableRxChannels(channelNamesList.size()); connect(m_deviceUIs.back()->m_samplingDeviceControl->getAddChannelButton(), SIGNAL(clicked(bool)), this, SLOT(channelAddClicked(bool))); @@ -274,8 +297,8 @@ void MainWindow::addSourceDevice(int deviceIndex) const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(deviceIndex); m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - m_deviceUIs.back()->m_deviceAPI->setNbItems(samplingDevice->deviceNbItems); - m_deviceUIs.back()->m_deviceAPI->setItemIndex(samplingDevice->deviceItemIndex); + m_deviceUIs.back()->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + m_deviceUIs.back()->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); m_deviceUIs.back()->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); @@ -289,7 +312,7 @@ void MainWindow::addSourceDevice(int deviceIndex) m_deviceUIs.back()->m_deviceAPI->getSamplingDevicePluginInstanceGUI()); - DeviceSampleSource *source = m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->createSampleSourcePluginInstanceInput( + DeviceSampleSource *source = m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->createSampleSourcePluginInstance( m_deviceUIs.back()->m_deviceAPI->getSamplingDeviceId(), m_deviceUIs.back()->m_deviceAPI); m_deviceUIs.back()->m_deviceAPI->setSampleSource(source); QWidget *gui; @@ -313,14 +336,14 @@ void MainWindow::addSinkDevice() sprintf(uidCStr, "UID:%d", dspDeviceSinkEngineUID); int deviceTabIndex = m_deviceUIs.size(); - m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, false, m_masterTimer)); + m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, 1, m_masterTimer)); m_deviceUIs.back()->m_deviceSourceEngine = 0; m_deviceUIs.back()->m_deviceSinkEngine = dspDeviceSinkEngine; char tabNameCStr[16]; sprintf(tabNameCStr, "T%d", deviceTabIndex); - DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamSingleTx, deviceTabIndex, nullptr, dspDeviceSinkEngine); + DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamSingleTx, deviceTabIndex, nullptr, dspDeviceSinkEngine, nullptr); m_deviceUIs.back()->m_deviceAPI = deviceAPI; m_deviceUIs.back()->m_samplingDeviceControl->setPluginManager(m_pluginManager); @@ -328,6 +351,7 @@ void MainWindow::addSinkDevice() m_pluginManager->listTxChannels(channelNames); QStringList channelNamesList(channelNames); m_deviceUIs.back()->m_samplingDeviceControl->getChannelSelector()->addItems(channelNamesList); + m_deviceUIs.back()->setNumberOfAvailableTxChannels(channelNamesList.size()); connect(m_deviceUIs.back()->m_samplingDeviceControl->getAddChannelButton(), SIGNAL(clicked(bool)), this, SLOT(channelAddClicked(bool))); @@ -345,8 +369,8 @@ void MainWindow::addSinkDevice() int fileSinkDeviceIndex = DeviceEnumerator::instance()->getFileSinkDeviceIndex(); const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(fileSinkDeviceIndex); m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - m_deviceUIs.back()->m_deviceAPI->setNbItems(samplingDevice->deviceNbItems); - m_deviceUIs.back()->m_deviceAPI->setItemIndex(samplingDevice->deviceItemIndex); + m_deviceUIs.back()->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + m_deviceUIs.back()->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); m_deviceUIs.back()->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); @@ -359,7 +383,7 @@ void MainWindow::addSinkDevice() m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( m_deviceUIs.back()->m_deviceAPI->getSamplingDevicePluginInstanceGUI()); - DeviceSampleSink *sink = m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->createSampleSinkPluginInstanceOutput( + DeviceSampleSink *sink = m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->createSampleSinkPluginInstance( m_deviceUIs.back()->m_deviceAPI->getSamplingDeviceId(), m_deviceUIs.back()->m_deviceAPI); m_deviceUIs.back()->m_deviceAPI->setSampleSink(sink); QWidget *gui; @@ -373,6 +397,87 @@ void MainWindow::addSinkDevice() setDeviceGUI(deviceTabIndex, gui, m_deviceUIs.back()->m_deviceAPI->getSamplingDeviceDisplayName(), false); } +void MainWindow::addMIMODevice() +{ + DSPDeviceMIMOEngine *dspDeviceMIMOEngine = m_dspEngine->addDeviceMIMOEngine(); + dspDeviceMIMOEngine->start(); + + uint dspDeviceMIMOEngineUID = dspDeviceMIMOEngine->getUID(); + char uidCStr[16]; + sprintf(uidCStr, "UID:%d", dspDeviceMIMOEngineUID); + + int deviceTabIndex = m_deviceUIs.size(); + m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, 2, m_masterTimer)); + m_deviceUIs.back()->m_deviceSourceEngine = nullptr; + m_deviceUIs.back()->m_deviceSinkEngine = nullptr; + m_deviceUIs.back()->m_deviceMIMOEngine = dspDeviceMIMOEngine; + + char tabNameCStr[16]; + sprintf(tabNameCStr, "M%d", deviceTabIndex); + + DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamMIMO, deviceTabIndex, nullptr, nullptr, dspDeviceMIMOEngine); + + m_deviceUIs.back()->m_deviceAPI = deviceAPI; + m_deviceUIs.back()->m_samplingDeviceControl->setPluginManager(m_pluginManager); + QComboBox *channelSelector = m_deviceUIs.back()->m_samplingDeviceControl->getChannelSelector(); + // Add Rx channels + QList rxChannelNames; + m_pluginManager->listRxChannels(rxChannelNames); + QStringList rxChannelNamesList(rxChannelNames); + channelSelector->addItems(rxChannelNamesList); + m_deviceUIs.back()->setNumberOfAvailableRxChannels(rxChannelNamesList.size()); + // Add Tx channels + QList txChannelNames; + m_pluginManager->listTxChannels(txChannelNames); + QStringList txChannelNamesList(txChannelNames); + channelSelector->addItems(txChannelNamesList); + m_deviceUIs.back()->setNumberOfAvailableTxChannels(txChannelNamesList.size()); + // TODO: add MIMO channels + + connect(m_deviceUIs.back()->m_samplingDeviceControl->getAddChannelButton(), SIGNAL(clicked(bool)), this, SLOT(channelAddClicked(bool))); + + dspDeviceMIMOEngine->addSpectrumSink(m_deviceUIs.back()->m_spectrumVis); + ui->tabSpectra->addTab(m_deviceUIs.back()->m_spectrum, tabNameCStr); + ui->tabSpectraGUI->addTab(m_deviceUIs.back()->m_spectrumGUI, tabNameCStr); + ui->tabChannels->addTab(m_deviceUIs.back()->m_channelWindow, tabNameCStr); + + connect(m_deviceUIs.back()->m_samplingDeviceControl, SIGNAL(changed()), this, SLOT(sampleMIMOChanged())); + + ui->tabInputsSelect->addTab(m_deviceUIs.back()->m_samplingDeviceControl, tabNameCStr); + ui->tabInputsSelect->setTabToolTip(deviceTabIndex, QString(uidCStr)); + + // create a test MIMO by default + int testMIMODeviceIndex = DeviceEnumerator::instance()->getTestMIMODeviceIndex(); + const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getMIMOSamplingDevice(testMIMODeviceIndex); + m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); + m_deviceUIs.back()->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + m_deviceUIs.back()->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); + m_deviceUIs.back()->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); + m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); + m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); + m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); + m_deviceUIs.back()->m_deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getMIMOPluginInterface(testMIMODeviceIndex)); + + m_deviceUIs.back()->m_samplingDeviceControl->setSelectedDeviceIndex(testMIMODeviceIndex); + + // delete previous plugin GUI if it exists + m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( + m_deviceUIs.back()->m_deviceAPI->getSamplingDevicePluginInstanceGUI()); + + DeviceSampleMIMO *mimo = m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->createSampleMIMOPluginInstance( + m_deviceUIs.back()->m_deviceAPI->getSamplingDeviceId(), m_deviceUIs.back()->m_deviceAPI); + m_deviceUIs.back()->m_deviceAPI->setSampleMIMO(mimo); + QWidget *gui; + PluginInstanceGUI *pluginUI = m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->createSampleMIMOPluginInstanceGUI( + m_deviceUIs.back()->m_deviceAPI->getSamplingDeviceId(), + &gui, + m_deviceUIs.back()); + m_deviceUIs.back()->m_deviceAPI->getSampleMIMO()->setMessageQueueToGUI(pluginUI->getInputMessageQueue()); + m_deviceUIs.back()->m_deviceAPI->setSamplingDevicePluginInstanceGUI(pluginUI); + m_deviceUIs.back()->m_deviceAPI->getSampleMIMO()->init(); + setDeviceGUI(deviceTabIndex, gui, m_deviceUIs.back()->m_deviceAPI->getSamplingDeviceDisplayName(), 2); +} + void MainWindow::removeLastDevice() { if (m_deviceUIs.back()->m_deviceSourceEngine) // source tab @@ -459,6 +564,47 @@ void MainWindow::removeLastDevice() delete sinkAPI; } + else if (m_deviceUIs.back()->m_deviceMIMOEngine) // MIMO tab + { + DSPDeviceMIMOEngine *lastDeviceEngine = m_deviceUIs.back()->m_deviceMIMOEngine; + lastDeviceEngine->stopProcess(); + lastDeviceEngine->removeSpectrumSink(m_deviceUIs.back()->m_spectrumVis); + + ui->tabSpectraGUI->removeTab(ui->tabSpectraGUI->count() - 1); + ui->tabSpectra->removeTab(ui->tabSpectra->count() - 1); + + // deletes old UI and output object + m_deviceUIs.back()->freeRxChannels(); + m_deviceUIs.back()->freeTxChannels(); + m_deviceUIs.back()->m_deviceAPI->getSampleMIMO()->setMessageQueueToGUI(nullptr); // have sink stop sending messages to the GUI + m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->deleteSampleMIMOPluginInstanceGUI( + m_deviceUIs.back()->m_deviceAPI->getSamplingDevicePluginInstanceGUI()); + m_deviceUIs.back()->m_deviceAPI->resetSamplingDeviceId(); + m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->deleteSampleMIMOPluginInstanceMIMO( + m_deviceUIs.back()->m_deviceAPI->getSampleMIMO()); + m_deviceUIs.back()->m_samplingDeviceControl->removeSelectedDeviceIndex(); // This releases the device in the device list + + ui->tabChannels->removeTab(ui->tabChannels->count() - 1); + ui->tabInputsSelect->removeTab(ui->tabInputsSelect->count() - 1); + + m_deviceWidgetTabs.removeLast(); + ui->tabInputsView->clear(); + + for (int i = 0; i < m_deviceWidgetTabs.size(); i++) + { + qDebug("MainWindow::removeLastDevice: adding back tab for %s", m_deviceWidgetTabs[i].displayName.toStdString().c_str()); + ui->tabInputsView->addTab(m_deviceWidgetTabs[i].gui, m_deviceWidgetTabs[i].tabName); + ui->tabInputsView->setTabToolTip(i, m_deviceWidgetTabs[i].displayName); + } + + DeviceAPI *mimoAPI = m_deviceUIs.back()->m_deviceAPI; + delete m_deviceUIs.back(); + + lastDeviceEngine->stop(); + m_dspEngine->removeLastDeviceMIMOEngine(); + + delete mimoAPI; + } m_deviceUIs.pop_back(); } @@ -496,27 +642,23 @@ void MainWindow::addViewAction(QAction* action) ui->menu_Window->addAction(action); } -void MainWindow::setDeviceGUI(int deviceTabIndex, QWidget* gui, const QString& deviceDisplayName, bool sourceDevice) +void MainWindow::setDeviceGUI(int deviceTabIndex, QWidget* gui, const QString& deviceDisplayName, int deviceType) { char tabNameCStr[16]; - if (sourceDevice) - { + if (deviceType == 0) { sprintf(tabNameCStr, "R%d", deviceTabIndex); - } - else - { + } else if (deviceType == 1) { sprintf(tabNameCStr, "T%d", deviceTabIndex); + } else if (deviceType == 2) { + sprintf(tabNameCStr, "M%d", deviceTabIndex); } - qDebug("MainWindow::setDeviceGUI: insert %s tab at %d", sourceDevice ? "Rx" : "Tx", deviceTabIndex); + qDebug("MainWindow::setDeviceGUI: insert device type %d tab at %d", deviceType, deviceTabIndex); - if (deviceTabIndex < m_deviceWidgetTabs.size()) - { + if (deviceTabIndex < m_deviceWidgetTabs.size()) { m_deviceWidgetTabs[deviceTabIndex] = {gui, deviceDisplayName, QString(tabNameCStr)}; - } - else - { + } else { m_deviceWidgetTabs.append({gui, deviceDisplayName, QString(tabNameCStr)}); } @@ -795,7 +937,9 @@ bool MainWindow::handleMessage(const Message& cmd) addSinkDevice(); } else if (direction == 0) { // Single stream Rx addSourceDevice(-1); // create with file source device by default - } // other device types not (yet) supported + } else if (direction == 2) { // MIMO + addMIMODevice(); + } return true; } @@ -814,10 +958,12 @@ bool MainWindow::handleMessage(const Message& cmd) DeviceUISet *deviceUI = m_deviceUIs[notif.getDeviceSetIndex()]; deviceUI->m_samplingDeviceControl->setSelectedDeviceIndex(notif.getDeviceIndex()); - if (notif.isTx()) { + if (notif.getDeviceType() == 1) { sampleSinkChanged(); - } else { + } else if (notif.getDeviceType() == 0) { sampleSourceChanged(); + } else if (notif.getDeviceType() == 2) { + sampleMIMOChanged(); } return true; @@ -1504,8 +1650,8 @@ void MainWindow::sampleSourceChanged() const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice( deviceUI->m_samplingDeviceControl->getSelectedDeviceIndex()); deviceUI->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - deviceUI->m_deviceAPI->setNbItems(samplingDevice->deviceNbItems); - deviceUI->m_deviceAPI->setItemIndex(samplingDevice->deviceItemIndex); + deviceUI->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + deviceUI->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); deviceUI->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); deviceUI->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); deviceUI->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); @@ -1547,7 +1693,7 @@ void MainWindow::sampleSourceChanged() } // constructs new GUI and input object - DeviceSampleSource *source = deviceUI->m_deviceAPI->getPluginInterface()->createSampleSourcePluginInstanceInput( + DeviceSampleSource *source = deviceUI->m_deviceAPI->getPluginInterface()->createSampleSourcePluginInstance( deviceUI->m_deviceAPI->getSamplingDeviceId(), deviceUI->m_deviceAPI); deviceUI->m_deviceAPI->setSampleSource(source); QWidget *gui; @@ -1593,8 +1739,8 @@ void MainWindow::sampleSinkChanged() const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(deviceUI->m_samplingDeviceControl->getSelectedDeviceIndex()); deviceUI->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - deviceUI->m_deviceAPI->setNbItems(samplingDevice->deviceNbItems); - deviceUI->m_deviceAPI->setItemIndex(samplingDevice->deviceItemIndex); + deviceUI->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + deviceUI->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); deviceUI->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); deviceUI->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); deviceUI->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); @@ -1636,7 +1782,7 @@ void MainWindow::sampleSinkChanged() } // constructs new GUI and output object - DeviceSampleSink *sink = deviceUI->m_deviceAPI->getPluginInterface()->createSampleSinkPluginInstanceOutput( + DeviceSampleSink *sink = deviceUI->m_deviceAPI->getPluginInterface()->createSampleSinkPluginInstance( deviceUI->m_deviceAPI->getSamplingDeviceId(), deviceUI->m_deviceAPI); deviceUI->m_deviceAPI->setSampleSink(sink); QWidget *gui; @@ -1646,13 +1792,63 @@ void MainWindow::sampleSinkChanged() deviceUI); deviceUI->m_deviceAPI->getSampleSink()->setMessageQueueToGUI(pluginUI->getInputMessageQueue()); deviceUI->m_deviceAPI->setSamplingDevicePluginInstanceGUI(pluginUI); - setDeviceGUI(currentSinkTabIndex, gui, deviceUI->m_deviceAPI->getSamplingDeviceDisplayName(), false); + setDeviceGUI(currentSinkTabIndex, gui, deviceUI->m_deviceAPI->getSamplingDeviceDisplayName(), 1); m_deviceUIs.back()->m_deviceAPI->getSampleSink()->init(); deviceUI->m_deviceAPI->loadSamplingDeviceSettings(m_settings.getWorkingPreset()); // load new API settings } } +void MainWindow::sampleMIMOChanged() +{ + // Do it in the currently selected source tab + int currentMIMOTabIndex = ui->tabInputsSelect->currentIndex(); + + if (currentMIMOTabIndex >= 0) + { + qDebug("MainWindow::sampleMIMOChanged: tab at %d", currentMIMOTabIndex); + DeviceUISet *deviceUI = m_deviceUIs[currentMIMOTabIndex]; + deviceUI->m_deviceAPI->saveSamplingDeviceSettings(m_settings.getWorkingPreset()); // save old API settings + deviceUI->m_deviceAPI->stopDeviceEngine(); + + // deletes old UI and output object + deviceUI->m_deviceAPI->getSampleMIMO()->setMessageQueueToGUI(nullptr); // have sink stop sending messages to the GUI + deviceUI->m_deviceAPI->getPluginInterface()->deleteSampleMIMOPluginInstanceGUI( + deviceUI->m_deviceAPI->getSamplingDevicePluginInstanceGUI()); + deviceUI->m_deviceAPI->resetSamplingDeviceId(); + deviceUI->m_deviceAPI->getPluginInterface()->deleteSampleMIMOPluginInstanceMIMO( + deviceUI->m_deviceAPI->getSampleMIMO()); + + const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getMIMOSamplingDevice( + deviceUI->m_samplingDeviceControl->getSelectedDeviceIndex()); + deviceUI->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); + deviceUI->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + deviceUI->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); + deviceUI->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); + deviceUI->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); + deviceUI->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); + deviceUI->m_deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); + deviceUI->m_deviceAPI->setSamplingDevicePluginInterface( + DeviceEnumerator::instance()->getMIMOPluginInterface(deviceUI->m_samplingDeviceControl->getSelectedDeviceIndex())); + + // constructs new GUI and output object + DeviceSampleMIMO *mimo = deviceUI->m_deviceAPI->getPluginInterface()->createSampleMIMOPluginInstance( + deviceUI->m_deviceAPI->getSamplingDeviceId(), deviceUI->m_deviceAPI); + deviceUI->m_deviceAPI->setSampleMIMO(mimo); + QWidget *gui; + PluginInstanceGUI *pluginUI = deviceUI->m_deviceAPI->getPluginInterface()->createSampleMIMOPluginInstanceGUI( + deviceUI->m_deviceAPI->getSamplingDeviceId(), + &gui, + deviceUI); + deviceUI->m_deviceAPI->getSampleMIMO()->setMessageQueueToGUI(pluginUI->getInputMessageQueue()); + deviceUI->m_deviceAPI->setSamplingDevicePluginInstanceGUI(pluginUI); + setDeviceGUI(currentMIMOTabIndex, gui, deviceUI->m_deviceAPI->getSamplingDeviceDisplayName(), 2); + m_deviceUIs.back()->m_deviceAPI->getSampleMIMO()->init(); + + deviceUI->m_deviceAPI->loadSamplingDeviceSettings(m_settings.getWorkingPreset()); // load new API settings + } +} + void MainWindow::channelAddClicked(bool checked) { (void) checked; @@ -1673,6 +1869,22 @@ void MainWindow::channelAddClicked(bool checked) m_pluginManager->createTxChannelInstance( deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex(), deviceUI, deviceUI->m_deviceAPI); } + else if (deviceUI->m_deviceMIMOEngine) // MIMO device => all possible channels. Depends on index range + { + int nbRxChannels = deviceUI->getNumberOfAvailableRxChannels(); + int nbTxChannels = deviceUI->getNumberOfAvailableTxChannels(); + int selectedIndex = deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex(); + qDebug("MainWindow::channelAddClicked: MIMO: tab: %d nbRx: %d nbTx: %d selected: %d", + currentSourceTabIndex, nbRxChannels, nbTxChannels, selectedIndex); + + if (selectedIndex < nbRxChannels) { + m_pluginManager->createRxChannelInstance( + selectedIndex, deviceUI, deviceUI->m_deviceAPI); + } else if (selectedIndex < nbRxChannels + nbTxChannels) { + m_pluginManager->createTxChannelInstance( + selectedIndex - nbRxChannels, deviceUI, deviceUI->m_deviceAPI); + } + } } } @@ -1692,6 +1904,15 @@ void MainWindow::on_action_addSinkDevice_triggered() addSinkDevice(); } +void MainWindow::on_action_addMIMODevice_triggered() +{ + if (m_dspEngine->getMIMOSupport()) { + addMIMODevice(); + } else { + QMessageBox::information(this, tr("Message"), tr("MIMO operation not supported yet")); + } +} + void MainWindow::on_action_removeLastDevice_triggered() { if (m_deviceUIs.size() > 1) diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index a06337e12..baf965c7d 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -32,7 +32,6 @@ class QLabel; class QTreeWidgetItem; class QDir; -class SamplingDeviceControl; class DSPEngine; class DSPDeviceSourceEngine; @@ -78,7 +77,7 @@ public: void addViewAction(QAction* action); void addChannelRollup(int deviceTabIndex, QWidget* widget); - void setDeviceGUI(int deviceTabIndex, QWidget* gui, const QString& deviceDisplayName, bool sourceDevice = true); + void setDeviceGUI(int deviceTabIndex, QWidget* gui, const QString& deviceDisplayName, int deviceType = 0); const QTimer& getMasterTimer() const { return m_masterTimer; } const MainSettings& getMainSettings() const { return m_settings; } @@ -196,7 +195,7 @@ private: public: int getDeviceSetIndex() const { return m_deviceSetIndex; } int getDeviceIndex() const { return m_deviceIndex; } - bool isTx() const { return m_tx; } + int getDeviceType() const { return m_deviceType; } static MsgSetDevice* create(int deviceSetIndex, int deviceIndex, bool tx) { @@ -206,13 +205,13 @@ private: private: int m_deviceSetIndex; int m_deviceIndex; - bool m_tx; + int m_deviceType; - MsgSetDevice(int deviceSetIndex, int deviceIndex, bool tx) : + MsgSetDevice(int deviceSetIndex, int deviceIndex, int deviceType) : Message(), m_deviceSetIndex(deviceSetIndex), m_deviceIndex(deviceIndex), - m_tx(tx) + m_deviceType(deviceType) { } }; @@ -348,6 +347,7 @@ private: void addSourceDevice(int deviceIndex); void addSinkDevice(); + void addMIMODevice(); void removeLastDevice(); void deleteChannel(int deviceSetIndex, int channelIndex); @@ -383,11 +383,13 @@ private slots: void on_action_My_Position_triggered(); void sampleSourceChanged(); void sampleSinkChanged(); + void sampleMIMOChanged(); void channelAddClicked(bool checked); void on_action_Loaded_Plugins_triggered(); void on_action_About_triggered(); void on_action_addSourceDevice_triggered(); void on_action_addSinkDevice_triggered(); + void on_action_addMIMODevice_triggered(); void on_action_removeLastDevice_triggered(); void on_action_Exit_triggered(); void tabInputViewIndexChanged(); diff --git a/sdrgui/mainwindow.ui b/sdrgui/mainwindow.ui index 9c08c869c..fa9f7ddab 100644 --- a/sdrgui/mainwindow.ui +++ b/sdrgui/mainwindow.ui @@ -64,7 +64,7 @@ 0 0 1012 - 19 + 20
@@ -79,6 +79,7 @@ + @@ -914,6 +915,17 @@ + + + Add MIMO device set + + + + Liberation Sans + 9 + + + presetDock channelDock commandsDock diff --git a/sdrgui/webapi/webapiadaptergui.cpp b/sdrgui/webapi/webapiadaptergui.cpp index 1e1256c16..dfc58e4c4 100644 --- a/sdrgui/webapi/webapiadaptergui.cpp +++ b/sdrgui/webapi/webapiadaptergui.cpp @@ -1052,7 +1052,7 @@ int WebAPIAdapterGUI::devicesetDevicePut( continue; } - MainWindow::MsgSetDevice *msg = MainWindow::MsgSetDevice::create(deviceSetIndex, i, query.getDirection() == 1); + MainWindow::MsgSetDevice *msg = MainWindow::MsgSetDevice::create(deviceSetIndex, i, query.getDirection()); m_mainWindow.m_inputMessageQueue.push(msg); response.init(); @@ -1749,8 +1749,8 @@ void WebAPIAdapterGUI::getDeviceSet(SWGSDRangel::SWGDeviceSet *deviceSet, const *samplingDevice->getHwType() = deviceUISet->m_deviceAPI->getHardwareId(); *samplingDevice->getSerial() = deviceUISet->m_deviceAPI->getSamplingDeviceSerial(); samplingDevice->setSequence(deviceUISet->m_deviceAPI->getSamplingDeviceSequence()); - samplingDevice->setDeviceNbStreams(deviceUISet->m_deviceAPI->getNbItems()); - samplingDevice->setDeviceStreamIndex(deviceUISet->m_deviceAPI->getItemIndex()); + samplingDevice->setDeviceNbStreams(deviceUISet->m_deviceAPI->getDeviceNbItems()); + samplingDevice->setDeviceStreamIndex(deviceUISet->m_deviceAPI->getDeviceItemIndex()); deviceUISet->m_deviceAPI->getDeviceEngineStateStr(*samplingDevice->getState()); DeviceSampleSink *sampleSink = deviceUISet->m_deviceSinkEngine->getSink(); @@ -1781,8 +1781,8 @@ void WebAPIAdapterGUI::getDeviceSet(SWGSDRangel::SWGDeviceSet *deviceSet, const *samplingDevice->getHwType() = deviceUISet->m_deviceAPI->getHardwareId(); *samplingDevice->getSerial() = deviceUISet->m_deviceAPI->getSamplingDeviceSerial(); samplingDevice->setSequence(deviceUISet->m_deviceAPI->getSamplingDeviceSequence()); - samplingDevice->setDeviceNbStreams(deviceUISet->m_deviceAPI->getNbItems()); - samplingDevice->setDeviceStreamIndex(deviceUISet->m_deviceAPI->getItemIndex()); + samplingDevice->setDeviceNbStreams(deviceUISet->m_deviceAPI->getDeviceNbItems()); + samplingDevice->setDeviceStreamIndex(deviceUISet->m_deviceAPI->getDeviceItemIndex()); deviceUISet->m_deviceAPI->getDeviceEngineStateStr(*samplingDevice->getState()); DeviceSampleSource *sampleSource = deviceUISet->m_deviceSourceEngine->getSource(); diff --git a/sdrsrv/device/deviceset.cpp b/sdrsrv/device/deviceset.cpp index efb45a21b..d4c58a52b 100644 --- a/sdrsrv/device/deviceset.cpp +++ b/sdrsrv/device/deviceset.cpp @@ -50,9 +50,10 @@ DeviceSet::ChannelInstanceRegistration::ChannelInstanceRegistration(const QStrin DeviceSet::DeviceSet(int tabIndex) { - m_deviceSourceEngine = 0; - m_deviceAPI = 0; - m_deviceSinkEngine = 0; + m_deviceAPI = nullptr; + m_deviceSourceEngine = nullptr; + m_deviceSinkEngine = nullptr; + m_deviceMIMOEngine = nullptr; m_deviceTabIndex = tabIndex; } diff --git a/sdrsrv/device/deviceset.h b/sdrsrv/device/deviceset.h index fcee0d7db..c253ce5db 100644 --- a/sdrsrv/device/deviceset.h +++ b/sdrsrv/device/deviceset.h @@ -20,9 +20,10 @@ #include -class DSPDeviceSourceEngine; class DeviceAPI; +class DSPDeviceSourceEngine; class DSPDeviceSinkEngine; +class DSPDeviceMIMOEngine; class PluginAPI; class ChannelAPI; class Preset; @@ -30,9 +31,10 @@ class Preset; class DeviceSet { public: - DSPDeviceSourceEngine *m_deviceSourceEngine; DeviceAPI *m_deviceAPI; + DSPDeviceSourceEngine *m_deviceSourceEngine; DSPDeviceSinkEngine *m_deviceSinkEngine; + DSPDeviceMIMOEngine *m_deviceMIMOEngine; DeviceSet(int tabIndex); ~DeviceSet(); diff --git a/sdrsrv/maincore.cpp b/sdrsrv/maincore.cpp index 8aa3c5964..c63e659f9 100644 --- a/sdrsrv/maincore.cpp +++ b/sdrsrv/maincore.cpp @@ -76,6 +76,8 @@ MainCore::MainCore(qtwebapp::LoggerWithFile *logger, const MainParser& parser, Q m_apiServer = new WebAPIServer(parser.getServerAddress(), parser.getServerPort(), m_requestMapper); m_apiServer->start(); + m_dspEngine->setMIMOSupport(parser.getMIMOSupport()); + qDebug() << "MainCore::MainCore: end"; } @@ -156,11 +158,11 @@ bool MainCore::handleMessage(const Message& cmd) { MsgSetDevice& notif = (MsgSetDevice&) cmd; - if (notif.isTx()) { + if (notif.getDeviceType() == 1) { changeSampleSink(notif.getDeviceSetIndex(), notif.getDeviceIndex()); - } else { + } else if (notif.getDeviceType() == 0) { changeSampleSource(notif.getDeviceSetIndex(), notif.getDeviceIndex()); - } + } // TODO: for MIMO return true; } @@ -264,12 +266,12 @@ void MainCore::addSinkDevice() int deviceTabIndex = m_deviceSets.size(); m_deviceSets.push_back(new DeviceSet(deviceTabIndex)); m_deviceSets.back()->m_deviceSourceEngine = 0; - m_deviceSets.back()->m_deviceSinkEngine = dspDeviceSinkEngine; + m_deviceSets.back()->m_deviceMIMOEngine = 0; char tabNameCStr[16]; sprintf(tabNameCStr, "T%d", deviceTabIndex); - DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamSingleTx, deviceTabIndex, nullptr, dspDeviceSinkEngine); + DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamSingleTx, deviceTabIndex, nullptr, dspDeviceSinkEngine, nullptr); m_deviceSets.back()->m_deviceAPI = deviceAPI; QList channelNames; @@ -278,8 +280,8 @@ void MainCore::addSinkDevice() int fileSinkDeviceIndex = DeviceEnumerator::instance()->getFileSinkDeviceIndex(); const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(fileSinkDeviceIndex); m_deviceSets.back()->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - m_deviceSets.back()->m_deviceAPI->setNbItems(samplingDevice->deviceNbItems); - m_deviceSets.back()->m_deviceAPI->setItemIndex(samplingDevice->deviceItemIndex); + m_deviceSets.back()->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + m_deviceSets.back()->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); m_deviceSets.back()->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); m_deviceSets.back()->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); m_deviceSets.back()->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); @@ -289,7 +291,7 @@ void MainCore::addSinkDevice() // delete previous plugin instance //m_deviceSets.back()->m_deviceSinkAPI->getPluginInterface()->deleteSampleSinkPluginInstanceOutput() - DeviceSampleSink *sink = m_deviceSets.back()->m_deviceAPI->getPluginInterface()->createSampleSinkPluginInstanceOutput( + DeviceSampleSink *sink = m_deviceSets.back()->m_deviceAPI->getPluginInterface()->createSampleSinkPluginInstance( m_deviceSets.back()->m_deviceAPI->getSamplingDeviceId(), m_deviceSets.back()->m_deviceAPI); m_deviceSets.back()->m_deviceAPI->setSampleSink(sink); } @@ -306,11 +308,13 @@ void MainCore::addSourceDevice() int deviceTabIndex = m_deviceSets.size(); m_deviceSets.push_back(new DeviceSet(deviceTabIndex)); m_deviceSets.back()->m_deviceSourceEngine = dspDeviceSourceEngine; + m_deviceSets.back()->m_deviceSinkEngine = 0; + m_deviceSets.back()->m_deviceMIMOEngine = 0; char tabNameCStr[16]; sprintf(tabNameCStr, "R%d", deviceTabIndex); - DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamSingleRx, deviceTabIndex, dspDeviceSourceEngine, nullptr); + DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamSingleRx, deviceTabIndex, dspDeviceSourceEngine, nullptr, nullptr); m_deviceSets.back()->m_deviceAPI = deviceAPI; @@ -318,15 +322,15 @@ void MainCore::addSourceDevice() int fileSourceDeviceIndex = DeviceEnumerator::instance()->getFileSourceDeviceIndex(); const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(fileSourceDeviceIndex); m_deviceSets.back()->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - m_deviceSets.back()->m_deviceAPI->setNbItems(samplingDevice->deviceNbItems); - m_deviceSets.back()->m_deviceAPI->setItemIndex(samplingDevice->deviceItemIndex); + m_deviceSets.back()->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + m_deviceSets.back()->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); m_deviceSets.back()->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); m_deviceSets.back()->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); m_deviceSets.back()->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); m_deviceSets.back()->m_deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); m_deviceSets.back()->m_deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getRxPluginInterface(fileSourceDeviceIndex)); - DeviceSampleSource *source = m_deviceSets.back()->m_deviceAPI->getPluginInterface()->createSampleSourcePluginInstanceInput( + DeviceSampleSource *source = m_deviceSets.back()->m_deviceAPI->getPluginInterface()->createSampleSourcePluginInstance( m_deviceSets.back()->m_deviceAPI->getSamplingDeviceId(), m_deviceSets.back()->m_deviceAPI); m_deviceSets.back()->m_deviceAPI->setSampleSource(source); } @@ -394,8 +398,8 @@ void MainCore::changeSampleSource(int deviceSetIndex, int selectedDeviceIndex) const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(selectedDeviceIndex); deviceSet->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - deviceSet->m_deviceAPI->setNbItems(samplingDevice->deviceNbItems); - deviceSet->m_deviceAPI->setItemIndex(samplingDevice->deviceItemIndex); + deviceSet->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + deviceSet->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); deviceSet->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); deviceSet->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); deviceSet->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); @@ -437,7 +441,7 @@ void MainCore::changeSampleSource(int deviceSetIndex, int selectedDeviceIndex) } // constructs new GUI and input object - DeviceSampleSource *source = deviceSet->m_deviceAPI->getPluginInterface()->createSampleSourcePluginInstanceInput( + DeviceSampleSource *source = deviceSet->m_deviceAPI->getPluginInterface()->createSampleSourcePluginInstance( deviceSet->m_deviceAPI->getSamplingDeviceId(), deviceSet->m_deviceAPI); deviceSet->m_deviceAPI->setSampleSource(source); @@ -462,8 +466,8 @@ void MainCore::changeSampleSink(int deviceSetIndex, int selectedDeviceIndex) const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(selectedDeviceIndex); deviceSet->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - deviceSet->m_deviceAPI->setNbItems(samplingDevice->deviceNbItems); - deviceSet->m_deviceAPI->setItemIndex(samplingDevice->deviceItemIndex); + deviceSet->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + deviceSet->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); deviceSet->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); deviceSet->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); deviceSet->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); @@ -505,7 +509,7 @@ void MainCore::changeSampleSink(int deviceSetIndex, int selectedDeviceIndex) } // constructs new GUI and output object - DeviceSampleSink *sink = deviceSet->m_deviceAPI->getPluginInterface()->createSampleSinkPluginInstanceOutput( + DeviceSampleSink *sink = deviceSet->m_deviceAPI->getPluginInterface()->createSampleSinkPluginInstance( deviceSet->m_deviceAPI->getSamplingDeviceId(), deviceSet->m_deviceAPI); deviceSet->m_deviceAPI->setSampleSink(sink); diff --git a/sdrsrv/maincore.h b/sdrsrv/maincore.h index 876ee1f6c..ca4fba350 100644 --- a/sdrsrv/maincore.h +++ b/sdrsrv/maincore.h @@ -197,23 +197,23 @@ private: public: int getDeviceSetIndex() const { return m_deviceSetIndex; } int getDeviceIndex() const { return m_deviceIndex; } - bool isTx() const { return m_tx; } + int getDeviceType() const { return m_deviceType; } - static MsgSetDevice* create(int deviceSetIndex, int deviceIndex, bool tx) + static MsgSetDevice* create(int deviceSetIndex, int deviceIndex, int deviceType) { - return new MsgSetDevice(deviceSetIndex, deviceIndex, tx); + return new MsgSetDevice(deviceSetIndex, deviceIndex, deviceType); } private: int m_deviceSetIndex; int m_deviceIndex; - bool m_tx; + int m_deviceType; - MsgSetDevice(int deviceSetIndex, int deviceIndex, bool tx) : + MsgSetDevice(int deviceSetIndex, int deviceIndex, int deviceType) : Message(), m_deviceSetIndex(deviceSetIndex), m_deviceIndex(deviceIndex), - m_tx(tx) + m_deviceType(deviceType) { } }; diff --git a/sdrsrv/webapi/webapiadaptersrv.cpp b/sdrsrv/webapi/webapiadaptersrv.cpp index 9b237df55..9443cac35 100644 --- a/sdrsrv/webapi/webapiadaptersrv.cpp +++ b/sdrsrv/webapi/webapiadaptersrv.cpp @@ -1137,7 +1137,7 @@ int WebAPIAdapterSrv::devicesetDevicePut( continue; } - MainCore::MsgSetDevice *msg = MainCore::MsgSetDevice::create(deviceSetIndex, i, query.getDirection() == 1); + MainCore::MsgSetDevice *msg = MainCore::MsgSetDevice::create(deviceSetIndex, i, query.getDirection()); m_mainCore.m_inputMessageQueue.push(msg); response.init(); @@ -1828,8 +1828,8 @@ void WebAPIAdapterSrv::getDeviceSet(SWGSDRangel::SWGDeviceSet *swgDeviceSet, con *samplingDevice->getHwType() = deviceSet->m_deviceAPI->getHardwareId(); *samplingDevice->getSerial() = deviceSet->m_deviceAPI->getSamplingDeviceSerial(); samplingDevice->setSequence(deviceSet->m_deviceAPI->getSamplingDeviceSequence()); - samplingDevice->setDeviceNbStreams(deviceSet->m_deviceAPI->getNbItems()); - samplingDevice->setDeviceStreamIndex(deviceSet->m_deviceAPI->getItemIndex()); + samplingDevice->setDeviceNbStreams(deviceSet->m_deviceAPI->getDeviceNbItems()); + samplingDevice->setDeviceStreamIndex(deviceSet->m_deviceAPI->getDeviceItemIndex()); deviceSet->m_deviceAPI->getDeviceEngineStateStr(*samplingDevice->getState()); DeviceSampleSink *sampleSink = deviceSet->m_deviceSinkEngine->getSink(); @@ -1860,8 +1860,8 @@ void WebAPIAdapterSrv::getDeviceSet(SWGSDRangel::SWGDeviceSet *swgDeviceSet, con *samplingDevice->getHwType() = deviceSet->m_deviceAPI->getHardwareId(); *samplingDevice->getSerial() = deviceSet->m_deviceAPI->getSamplingDeviceSerial(); samplingDevice->setSequence(deviceSet->m_deviceAPI->getSamplingDeviceSequence()); - samplingDevice->setDeviceNbStreams(deviceSet->m_deviceAPI->getNbItems()); - samplingDevice->setDeviceStreamIndex(deviceSet->m_deviceAPI->getItemIndex()); + samplingDevice->setDeviceNbStreams(deviceSet->m_deviceAPI->getDeviceNbItems()); + samplingDevice->setDeviceStreamIndex(deviceSet->m_deviceAPI->getDeviceItemIndex()); deviceSet->m_deviceAPI->getDeviceEngineStateStr(*samplingDevice->getState()); DeviceSampleSource *sampleSource = deviceSet->m_deviceSourceEngine->getSource(); diff --git a/swagger/sdrangel/api/swagger/include/AMDemod.yaml b/swagger/sdrangel/api/swagger/include/AMDemod.yaml index 22aaa5aa4..506b9a28f 100644 --- a/swagger/sdrangel/api/swagger/include/AMDemod.yaml +++ b/swagger/sdrangel/api/swagger/include/AMDemod.yaml @@ -2,7 +2,7 @@ AMDemodSettings: description: AMDemod properties: inputFrequencyOffset: - description: channel center frequency shift from baseband center in Hz + description: channel center frequency shift from baseband center in Hz type: integer format: int64 rfBandwidth: @@ -33,6 +33,9 @@ AMDemodSettings: syncAMOperation: description: Synchronous AM sidebands mode (DSB, USB, LSB) type: integer + streamIndex: + description: MIMO channel. Not relevant when connected to SI (single Rx). + type: integer useReverseAPI: description: Synchronize with reverse API (1 for yes, 0 for no) type: integer @@ -41,9 +44,9 @@ AMDemodSettings: reverseAPIPort: type: integer reverseAPIDeviceIndex: - type: integer + type: integer reverseAPIChannelIndex: - type: integer + type: integer AMDemodReport: description: AMDemod @@ -59,4 +62,3 @@ AMDemodReport: type: integer channelSampleRate: type: integer - \ No newline at end of file diff --git a/swagger/sdrangel/api/swagger/include/NFMDemod.yaml b/swagger/sdrangel/api/swagger/include/NFMDemod.yaml index 660d4201a..f1e6e43be 100644 --- a/swagger/sdrangel/api/swagger/include/NFMDemod.yaml +++ b/swagger/sdrangel/api/swagger/include/NFMDemod.yaml @@ -29,6 +29,8 @@ NFMDemodSettings: format: float ctcssOn: type: integer + highPass: + type: integer audioMute: type: integer ctcssIndex: diff --git a/swagger/sdrangel/api/swagger/include/TestMI.yaml b/swagger/sdrangel/api/swagger/include/TestMI.yaml new file mode 100644 index 000000000..16008a5e8 --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/TestMI.yaml @@ -0,0 +1,51 @@ +TestMISettings: + description: TestSourceMI + properties: + centerFrequency: + type: integer + format: uint64 + frequencyShift: + type: integer + sampleRate: + type: integer + log2Decim: + type: integer + fcPos: + type: integer + sampleSizeIndex: + type: integer + amplitudeBits: + type: integer + autoCorrOptions: + type: integer + modulation: + type: integer + modulationTone: + type: integer + amModulation: + type: integer + fmDeviation: + type: integer + dcFactor: + type: number + format: float + iFactor: + type: number + format: float + qFactor: + type: number + format: float + phaseImbalance: + type: number + format: float + fileRecordName: + type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer diff --git a/swagger/sdrangel/api/swagger/swagger.yaml b/swagger/sdrangel/api/swagger/swagger.yaml index 0a1a4ce71..6c7a69e96 100644 --- a/swagger/sdrangel/api/swagger/swagger.yaml +++ b/swagger/sdrangel/api/swagger/swagger.yaml @@ -1830,6 +1830,8 @@ definitions: $ref: "http://localhost:8081/api/swagger/include/SoapySDR.yaml#/SoapySDROutputSettings" testSourceSettings: $ref: "http://localhost:8081/api/swagger/include/TestSource.yaml#/TestSourceSettings" + testMISettings: + $ref: "http://localhost:8081/api/swagger/include/TestMI.yaml#/TestMISettings" xtrxInputSettings: $ref: "http://localhost:8081/api/swagger/include/Xtrx.yaml#/XtrxInputSettings" xtrxOutputSettings: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 35ecdf5f6..b840b4e26 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -761,6 +761,10 @@ margin-bottom: 20px; "type" : "integer", "description" : "Synchronous AM sidebands mode (DSB, USB, LSB)" }, + "streamIndex" : { + "type" : "integer", + "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." + }, "useReverseAPI" : { "type" : "integer", "description" : "Synchronize with reverse API (1 for yes, 0 for no)" @@ -2335,6 +2339,9 @@ margin-bottom: 20px; "testSourceSettings" : { "$ref" : "#/definitions/TestSourceSettings" }, + "testMISettings" : { + "$ref" : "#/definitions/TestMISettings" + }, "xtrxInputSettings" : { "$ref" : "#/definitions/XtrxInputSettings" }, @@ -3576,6 +3583,9 @@ margin-bottom: 20px; "ctcssOn" : { "type" : "integer" }, + "highPass" : { + "type" : "integer" + }, "audioMute" : { "type" : "integer" }, @@ -5203,6 +5213,80 @@ margin-bottom: 20px; "type" : "string" } } +}; + defs.TestMISettings = { + "properties" : { + "centerFrequency" : { + "type" : "integer", + "format" : "uint64" + }, + "frequencyShift" : { + "type" : "integer" + }, + "sampleRate" : { + "type" : "integer" + }, + "log2Decim" : { + "type" : "integer" + }, + "fcPos" : { + "type" : "integer" + }, + "sampleSizeIndex" : { + "type" : "integer" + }, + "amplitudeBits" : { + "type" : "integer" + }, + "autoCorrOptions" : { + "type" : "integer" + }, + "modulation" : { + "type" : "integer" + }, + "modulationTone" : { + "type" : "integer" + }, + "amModulation" : { + "type" : "integer" + }, + "fmDeviation" : { + "type" : "integer" + }, + "dcFactor" : { + "type" : "number", + "format" : "float" + }, + "iFactor" : { + "type" : "number", + "format" : "float" + }, + "qFactor" : { + "type" : "number", + "format" : "float" + }, + "phaseImbalance" : { + "type" : "number", + "format" : "float" + }, + "fileRecordName" : { + "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + } + }, + "description" : "TestSourceMI" }; defs.TestSourceSettings = { "properties" : { @@ -25000,7 +25084,7 @@ except ApiException as e:
- Generated 2019-05-10T14:58:45.508+02:00 + Generated 2019-05-20T16:12:41.467+02:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.cpp index 2a64d5271..7f5a12ffa 100644 --- a/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.cpp @@ -50,6 +50,8 @@ SWGAMDemodSettings::SWGAMDemodSettings() { m_pll_isSet = false; sync_am_operation = 0; m_sync_am_operation_isSet = false; + stream_index = 0; + m_stream_index_isSet = false; use_reverse_api = 0; m_use_reverse_api_isSet = false; reverse_api_address = nullptr; @@ -90,6 +92,8 @@ SWGAMDemodSettings::init() { m_pll_isSet = false; sync_am_operation = 0; m_sync_am_operation_isSet = false; + stream_index = 0; + m_stream_index_isSet = false; use_reverse_api = 0; m_use_reverse_api_isSet = false; reverse_api_address = new QString(""); @@ -120,6 +124,7 @@ SWGAMDemodSettings::cleanup() { + if(reverse_api_address != nullptr) { delete reverse_api_address; } @@ -161,6 +166,8 @@ SWGAMDemodSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&sync_am_operation, pJson["syncAMOperation"], "qint32", ""); + ::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", ""); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); @@ -220,6 +227,9 @@ SWGAMDemodSettings::asJsonObject() { if(m_sync_am_operation_isSet){ obj->insert("syncAMOperation", QJsonValue(sync_am_operation)); } + if(m_stream_index_isSet){ + obj->insert("streamIndex", QJsonValue(stream_index)); + } if(m_use_reverse_api_isSet){ obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); } @@ -349,6 +359,16 @@ SWGAMDemodSettings::setSyncAmOperation(qint32 sync_am_operation) { this->m_sync_am_operation_isSet = true; } +qint32 +SWGAMDemodSettings::getStreamIndex() { + return stream_index; +} +void +SWGAMDemodSettings::setStreamIndex(qint32 stream_index) { + this->stream_index = stream_index; + this->m_stream_index_isSet = true; +} + qint32 SWGAMDemodSettings::getUseReverseApi() { return use_reverse_api; @@ -415,6 +435,7 @@ SWGAMDemodSettings::isSet(){ if(audio_device_name != nullptr && *audio_device_name != QString("")){ isObjectUpdated = true; break;} if(m_pll_isSet){ isObjectUpdated = true; break;} if(m_sync_am_operation_isSet){ isObjectUpdated = true; break;} + if(m_stream_index_isSet){ isObjectUpdated = true; break;} if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} diff --git a/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.h index b238eb1d2..8d194bbf3 100644 --- a/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.h @@ -75,6 +75,9 @@ public: qint32 getSyncAmOperation(); void setSyncAmOperation(qint32 sync_am_operation); + qint32 getStreamIndex(); + void setStreamIndex(qint32 stream_index); + qint32 getUseReverseApi(); void setUseReverseApi(qint32 use_reverse_api); @@ -127,6 +130,9 @@ private: qint32 sync_am_operation; bool m_sync_am_operation_isSet; + qint32 stream_index; + bool m_stream_index_isSet; + qint32 use_reverse_api; bool m_use_reverse_api_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.cpp index be54847f6..7ed2bcc3a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.cpp @@ -84,6 +84,8 @@ SWGDeviceSettings::SWGDeviceSettings() { m_soapy_sdr_output_settings_isSet = false; test_source_settings = nullptr; m_test_source_settings_isSet = false; + test_mi_settings = nullptr; + m_test_mi_settings_isSet = false; xtrx_input_settings = nullptr; m_xtrx_input_settings_isSet = false; xtrx_output_settings = nullptr; @@ -152,6 +154,8 @@ SWGDeviceSettings::init() { m_soapy_sdr_output_settings_isSet = false; test_source_settings = new SWGTestSourceSettings(); m_test_source_settings_isSet = false; + test_mi_settings = new SWGTestMISettings(); + m_test_mi_settings_isSet = false; xtrx_input_settings = new SWGXtrxInputSettings(); m_xtrx_input_settings_isSet = false; xtrx_output_settings = new SWGXtrxOutputSettings(); @@ -240,6 +244,9 @@ SWGDeviceSettings::cleanup() { if(test_source_settings != nullptr) { delete test_source_settings; } + if(test_mi_settings != nullptr) { + delete test_mi_settings; + } if(xtrx_input_settings != nullptr) { delete xtrx_input_settings; } @@ -315,6 +322,8 @@ SWGDeviceSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&test_source_settings, pJson["testSourceSettings"], "SWGTestSourceSettings", "SWGTestSourceSettings"); + ::SWGSDRangel::setValue(&test_mi_settings, pJson["testMISettings"], "SWGTestMISettings", "SWGTestMISettings"); + ::SWGSDRangel::setValue(&xtrx_input_settings, pJson["xtrxInputSettings"], "SWGXtrxInputSettings", "SWGXtrxInputSettings"); ::SWGSDRangel::setValue(&xtrx_output_settings, pJson["xtrxOutputSettings"], "SWGXtrxOutputSettings", "SWGXtrxOutputSettings"); @@ -419,6 +428,9 @@ SWGDeviceSettings::asJsonObject() { if((test_source_settings != nullptr) && (test_source_settings->isSet())){ toJsonValue(QString("testSourceSettings"), test_source_settings, obj, QString("SWGTestSourceSettings")); } + if((test_mi_settings != nullptr) && (test_mi_settings->isSet())){ + toJsonValue(QString("testMISettings"), test_mi_settings, obj, QString("SWGTestMISettings")); + } if((xtrx_input_settings != nullptr) && (xtrx_input_settings->isSet())){ toJsonValue(QString("xtrxInputSettings"), xtrx_input_settings, obj, QString("SWGXtrxInputSettings")); } @@ -709,6 +721,16 @@ SWGDeviceSettings::setTestSourceSettings(SWGTestSourceSettings* test_source_sett this->m_test_source_settings_isSet = true; } +SWGTestMISettings* +SWGDeviceSettings::getTestMiSettings() { + return test_mi_settings; +} +void +SWGDeviceSettings::setTestMiSettings(SWGTestMISettings* test_mi_settings) { + this->test_mi_settings = test_mi_settings; + this->m_test_mi_settings_isSet = true; +} + SWGXtrxInputSettings* SWGDeviceSettings::getXtrxInputSettings() { return xtrx_input_settings; @@ -762,6 +784,7 @@ SWGDeviceSettings::isSet(){ if(soapy_sdr_input_settings != nullptr && soapy_sdr_input_settings->isSet()){ isObjectUpdated = true; break;} if(soapy_sdr_output_settings != nullptr && soapy_sdr_output_settings->isSet()){ isObjectUpdated = true; break;} if(test_source_settings != nullptr && test_source_settings->isSet()){ isObjectUpdated = true; break;} + if(test_mi_settings != nullptr && test_mi_settings->isSet()){ isObjectUpdated = true; break;} if(xtrx_input_settings != nullptr && xtrx_input_settings->isSet()){ isObjectUpdated = true; break;} if(xtrx_output_settings != nullptr && xtrx_output_settings->isSet()){ isObjectUpdated = true; break;} }while(false); diff --git a/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.h b/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.h index f4277cff3..fdc91d022 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.h @@ -46,6 +46,7 @@ #include "SWGSDRPlaySettings.h" #include "SWGSoapySDRInputSettings.h" #include "SWGSoapySDROutputSettings.h" +#include "SWGTestMISettings.h" #include "SWGTestSourceSettings.h" #include "SWGXtrxInputSettings.h" #include "SWGXtrxOutputSettings.h" @@ -153,6 +154,9 @@ public: SWGTestSourceSettings* getTestSourceSettings(); void setTestSourceSettings(SWGTestSourceSettings* test_source_settings); + SWGTestMISettings* getTestMiSettings(); + void setTestMiSettings(SWGTestMISettings* test_mi_settings); + SWGXtrxInputSettings* getXtrxInputSettings(); void setXtrxInputSettings(SWGXtrxInputSettings* xtrx_input_settings); @@ -247,6 +251,9 @@ private: SWGTestSourceSettings* test_source_settings; bool m_test_source_settings_isSet; + SWGTestMISettings* test_mi_settings; + bool m_test_mi_settings_isSet; + SWGXtrxInputSettings* xtrx_input_settings; bool m_xtrx_input_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 0614c131e..45f07d9fc 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -132,6 +132,7 @@ #include "SWGSoapySDROutputSettings.h" #include "SWGSoapySDRReport.h" #include "SWGSuccessResponse.h" +#include "SWGTestMISettings.h" #include "SWGTestSourceSettings.h" #include "SWGUDPSinkReport.h" #include "SWGUDPSinkSettings.h" @@ -503,6 +504,9 @@ namespace SWGSDRangel { if(QString("SWGSuccessResponse").compare(type) == 0) { return new SWGSuccessResponse(); } + if(QString("SWGTestMISettings").compare(type) == 0) { + return new SWGTestMISettings(); + } if(QString("SWGTestSourceSettings").compare(type) == 0) { return new SWGTestSourceSettings(); } diff --git a/swagger/sdrangel/code/qt5/client/SWGNFMDemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGNFMDemodSettings.cpp index e1beb433a..9bb0f0e19 100644 --- a/swagger/sdrangel/code/qt5/client/SWGNFMDemodSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGNFMDemodSettings.cpp @@ -46,6 +46,8 @@ SWGNFMDemodSettings::SWGNFMDemodSettings() { m_volume_isSet = false; ctcss_on = 0; m_ctcss_on_isSet = false; + high_pass = 0; + m_high_pass_isSet = false; audio_mute = 0; m_audio_mute_isSet = false; ctcss_index = 0; @@ -92,6 +94,8 @@ SWGNFMDemodSettings::init() { m_volume_isSet = false; ctcss_on = 0; m_ctcss_on_isSet = false; + high_pass = 0; + m_high_pass_isSet = false; audio_mute = 0; m_audio_mute_isSet = false; ctcss_index = 0; @@ -128,6 +132,7 @@ SWGNFMDemodSettings::cleanup() { + if(title != nullptr) { delete title; } @@ -172,6 +177,8 @@ SWGNFMDemodSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&ctcss_on, pJson["ctcssOn"], "qint32", ""); + ::SWGSDRangel::setValue(&high_pass, pJson["highPass"], "qint32", ""); + ::SWGSDRangel::setValue(&audio_mute, pJson["audioMute"], "qint32", ""); ::SWGSDRangel::setValue(&ctcss_index, pJson["ctcssIndex"], "qint32", ""); @@ -235,6 +242,9 @@ SWGNFMDemodSettings::asJsonObject() { if(m_ctcss_on_isSet){ obj->insert("ctcssOn", QJsonValue(ctcss_on)); } + if(m_high_pass_isSet){ + obj->insert("highPass", QJsonValue(high_pass)); + } if(m_audio_mute_isSet){ obj->insert("audioMute", QJsonValue(audio_mute)); } @@ -359,6 +369,16 @@ SWGNFMDemodSettings::setCtcssOn(qint32 ctcss_on) { this->m_ctcss_on_isSet = true; } +qint32 +SWGNFMDemodSettings::getHighPass() { + return high_pass; +} +void +SWGNFMDemodSettings::setHighPass(qint32 high_pass) { + this->high_pass = high_pass; + this->m_high_pass_isSet = true; +} + qint32 SWGNFMDemodSettings::getAudioMute() { return audio_mute; @@ -473,6 +493,7 @@ SWGNFMDemodSettings::isSet(){ if(m_squelch_isSet){ isObjectUpdated = true; break;} if(m_volume_isSet){ isObjectUpdated = true; break;} if(m_ctcss_on_isSet){ isObjectUpdated = true; break;} + if(m_high_pass_isSet){ isObjectUpdated = true; break;} if(m_audio_mute_isSet){ isObjectUpdated = true; break;} if(m_ctcss_index_isSet){ isObjectUpdated = true; break;} if(m_rgb_color_isSet){ isObjectUpdated = true; break;} diff --git a/swagger/sdrangel/code/qt5/client/SWGNFMDemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGNFMDemodSettings.h index c2a6767c4..9e2dfa268 100644 --- a/swagger/sdrangel/code/qt5/client/SWGNFMDemodSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGNFMDemodSettings.h @@ -69,6 +69,9 @@ public: qint32 getCtcssOn(); void setCtcssOn(qint32 ctcss_on); + qint32 getHighPass(); + void setHighPass(qint32 high_pass); + qint32 getAudioMute(); void setAudioMute(qint32 audio_mute); @@ -130,6 +133,9 @@ private: qint32 ctcss_on; bool m_ctcss_on_isSet; + qint32 high_pass; + bool m_high_pass_isSet; + qint32 audio_mute; bool m_audio_mute_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGTestMISettings.cpp b/swagger/sdrangel/code/qt5/client/SWGTestMISettings.cpp new file mode 100644 index 000000000..b77b161ff --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGTestMISettings.cpp @@ -0,0 +1,530 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 4.8.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGTestMISettings.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGTestMISettings::SWGTestMISettings(QString* json) { + init(); + this->fromJson(*json); +} + +SWGTestMISettings::SWGTestMISettings() { + center_frequency = 0; + m_center_frequency_isSet = false; + frequency_shift = 0; + m_frequency_shift_isSet = false; + sample_rate = 0; + m_sample_rate_isSet = false; + log2_decim = 0; + m_log2_decim_isSet = false; + fc_pos = 0; + m_fc_pos_isSet = false; + sample_size_index = 0; + m_sample_size_index_isSet = false; + amplitude_bits = 0; + m_amplitude_bits_isSet = false; + auto_corr_options = 0; + m_auto_corr_options_isSet = false; + modulation = 0; + m_modulation_isSet = false; + modulation_tone = 0; + m_modulation_tone_isSet = false; + am_modulation = 0; + m_am_modulation_isSet = false; + fm_deviation = 0; + m_fm_deviation_isSet = false; + dc_factor = 0.0f; + m_dc_factor_isSet = false; + i_factor = 0.0f; + m_i_factor_isSet = false; + q_factor = 0.0f; + m_q_factor_isSet = false; + phase_imbalance = 0.0f; + m_phase_imbalance_isSet = false; + file_record_name = nullptr; + m_file_record_name_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; +} + +SWGTestMISettings::~SWGTestMISettings() { + this->cleanup(); +} + +void +SWGTestMISettings::init() { + center_frequency = 0; + m_center_frequency_isSet = false; + frequency_shift = 0; + m_frequency_shift_isSet = false; + sample_rate = 0; + m_sample_rate_isSet = false; + log2_decim = 0; + m_log2_decim_isSet = false; + fc_pos = 0; + m_fc_pos_isSet = false; + sample_size_index = 0; + m_sample_size_index_isSet = false; + amplitude_bits = 0; + m_amplitude_bits_isSet = false; + auto_corr_options = 0; + m_auto_corr_options_isSet = false; + modulation = 0; + m_modulation_isSet = false; + modulation_tone = 0; + m_modulation_tone_isSet = false; + am_modulation = 0; + m_am_modulation_isSet = false; + fm_deviation = 0; + m_fm_deviation_isSet = false; + dc_factor = 0.0f; + m_dc_factor_isSet = false; + i_factor = 0.0f; + m_i_factor_isSet = false; + q_factor = 0.0f; + m_q_factor_isSet = false; + phase_imbalance = 0.0f; + m_phase_imbalance_isSet = false; + file_record_name = new QString(""); + m_file_record_name_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; +} + +void +SWGTestMISettings::cleanup() { + + + + + + + + + + + + + + + + + if(file_record_name != nullptr) { + delete file_record_name; + } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + +} + +SWGTestMISettings* +SWGTestMISettings::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGTestMISettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(¢er_frequency, pJson["centerFrequency"], "qint32", ""); + + ::SWGSDRangel::setValue(&frequency_shift, pJson["frequencyShift"], "qint32", ""); + + ::SWGSDRangel::setValue(&sample_rate, pJson["sampleRate"], "qint32", ""); + + ::SWGSDRangel::setValue(&log2_decim, pJson["log2Decim"], "qint32", ""); + + ::SWGSDRangel::setValue(&fc_pos, pJson["fcPos"], "qint32", ""); + + ::SWGSDRangel::setValue(&sample_size_index, pJson["sampleSizeIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&litude_bits, pJson["amplitudeBits"], "qint32", ""); + + ::SWGSDRangel::setValue(&auto_corr_options, pJson["autoCorrOptions"], "qint32", ""); + + ::SWGSDRangel::setValue(&modulation, pJson["modulation"], "qint32", ""); + + ::SWGSDRangel::setValue(&modulation_tone, pJson["modulationTone"], "qint32", ""); + + ::SWGSDRangel::setValue(&am_modulation, pJson["amModulation"], "qint32", ""); + + ::SWGSDRangel::setValue(&fm_deviation, pJson["fmDeviation"], "qint32", ""); + + ::SWGSDRangel::setValue(&dc_factor, pJson["dcFactor"], "float", ""); + + ::SWGSDRangel::setValue(&i_factor, pJson["iFactor"], "float", ""); + + ::SWGSDRangel::setValue(&q_factor, pJson["qFactor"], "float", ""); + + ::SWGSDRangel::setValue(&phase_imbalance, pJson["phaseImbalance"], "float", ""); + + ::SWGSDRangel::setValue(&file_record_name, pJson["fileRecordName"], "QString", "QString"); + + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + +} + +QString +SWGTestMISettings::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGTestMISettings::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_center_frequency_isSet){ + obj->insert("centerFrequency", QJsonValue(center_frequency)); + } + if(m_frequency_shift_isSet){ + obj->insert("frequencyShift", QJsonValue(frequency_shift)); + } + if(m_sample_rate_isSet){ + obj->insert("sampleRate", QJsonValue(sample_rate)); + } + if(m_log2_decim_isSet){ + obj->insert("log2Decim", QJsonValue(log2_decim)); + } + if(m_fc_pos_isSet){ + obj->insert("fcPos", QJsonValue(fc_pos)); + } + if(m_sample_size_index_isSet){ + obj->insert("sampleSizeIndex", QJsonValue(sample_size_index)); + } + if(m_amplitude_bits_isSet){ + obj->insert("amplitudeBits", QJsonValue(amplitude_bits)); + } + if(m_auto_corr_options_isSet){ + obj->insert("autoCorrOptions", QJsonValue(auto_corr_options)); + } + if(m_modulation_isSet){ + obj->insert("modulation", QJsonValue(modulation)); + } + if(m_modulation_tone_isSet){ + obj->insert("modulationTone", QJsonValue(modulation_tone)); + } + if(m_am_modulation_isSet){ + obj->insert("amModulation", QJsonValue(am_modulation)); + } + if(m_fm_deviation_isSet){ + obj->insert("fmDeviation", QJsonValue(fm_deviation)); + } + if(m_dc_factor_isSet){ + obj->insert("dcFactor", QJsonValue(dc_factor)); + } + if(m_i_factor_isSet){ + obj->insert("iFactor", QJsonValue(i_factor)); + } + if(m_q_factor_isSet){ + obj->insert("qFactor", QJsonValue(q_factor)); + } + if(m_phase_imbalance_isSet){ + obj->insert("phaseImbalance", QJsonValue(phase_imbalance)); + } + if(file_record_name != nullptr && *file_record_name != QString("")){ + toJsonValue(QString("fileRecordName"), file_record_name, obj, QString("QString")); + } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + + return obj; +} + +qint32 +SWGTestMISettings::getCenterFrequency() { + return center_frequency; +} +void +SWGTestMISettings::setCenterFrequency(qint32 center_frequency) { + this->center_frequency = center_frequency; + this->m_center_frequency_isSet = true; +} + +qint32 +SWGTestMISettings::getFrequencyShift() { + return frequency_shift; +} +void +SWGTestMISettings::setFrequencyShift(qint32 frequency_shift) { + this->frequency_shift = frequency_shift; + this->m_frequency_shift_isSet = true; +} + +qint32 +SWGTestMISettings::getSampleRate() { + return sample_rate; +} +void +SWGTestMISettings::setSampleRate(qint32 sample_rate) { + this->sample_rate = sample_rate; + this->m_sample_rate_isSet = true; +} + +qint32 +SWGTestMISettings::getLog2Decim() { + return log2_decim; +} +void +SWGTestMISettings::setLog2Decim(qint32 log2_decim) { + this->log2_decim = log2_decim; + this->m_log2_decim_isSet = true; +} + +qint32 +SWGTestMISettings::getFcPos() { + return fc_pos; +} +void +SWGTestMISettings::setFcPos(qint32 fc_pos) { + this->fc_pos = fc_pos; + this->m_fc_pos_isSet = true; +} + +qint32 +SWGTestMISettings::getSampleSizeIndex() { + return sample_size_index; +} +void +SWGTestMISettings::setSampleSizeIndex(qint32 sample_size_index) { + this->sample_size_index = sample_size_index; + this->m_sample_size_index_isSet = true; +} + +qint32 +SWGTestMISettings::getAmplitudeBits() { + return amplitude_bits; +} +void +SWGTestMISettings::setAmplitudeBits(qint32 amplitude_bits) { + this->amplitude_bits = amplitude_bits; + this->m_amplitude_bits_isSet = true; +} + +qint32 +SWGTestMISettings::getAutoCorrOptions() { + return auto_corr_options; +} +void +SWGTestMISettings::setAutoCorrOptions(qint32 auto_corr_options) { + this->auto_corr_options = auto_corr_options; + this->m_auto_corr_options_isSet = true; +} + +qint32 +SWGTestMISettings::getModulation() { + return modulation; +} +void +SWGTestMISettings::setModulation(qint32 modulation) { + this->modulation = modulation; + this->m_modulation_isSet = true; +} + +qint32 +SWGTestMISettings::getModulationTone() { + return modulation_tone; +} +void +SWGTestMISettings::setModulationTone(qint32 modulation_tone) { + this->modulation_tone = modulation_tone; + this->m_modulation_tone_isSet = true; +} + +qint32 +SWGTestMISettings::getAmModulation() { + return am_modulation; +} +void +SWGTestMISettings::setAmModulation(qint32 am_modulation) { + this->am_modulation = am_modulation; + this->m_am_modulation_isSet = true; +} + +qint32 +SWGTestMISettings::getFmDeviation() { + return fm_deviation; +} +void +SWGTestMISettings::setFmDeviation(qint32 fm_deviation) { + this->fm_deviation = fm_deviation; + this->m_fm_deviation_isSet = true; +} + +float +SWGTestMISettings::getDcFactor() { + return dc_factor; +} +void +SWGTestMISettings::setDcFactor(float dc_factor) { + this->dc_factor = dc_factor; + this->m_dc_factor_isSet = true; +} + +float +SWGTestMISettings::getIFactor() { + return i_factor; +} +void +SWGTestMISettings::setIFactor(float i_factor) { + this->i_factor = i_factor; + this->m_i_factor_isSet = true; +} + +float +SWGTestMISettings::getQFactor() { + return q_factor; +} +void +SWGTestMISettings::setQFactor(float q_factor) { + this->q_factor = q_factor; + this->m_q_factor_isSet = true; +} + +float +SWGTestMISettings::getPhaseImbalance() { + return phase_imbalance; +} +void +SWGTestMISettings::setPhaseImbalance(float phase_imbalance) { + this->phase_imbalance = phase_imbalance; + this->m_phase_imbalance_isSet = true; +} + +QString* +SWGTestMISettings::getFileRecordName() { + return file_record_name; +} +void +SWGTestMISettings::setFileRecordName(QString* file_record_name) { + this->file_record_name = file_record_name; + this->m_file_record_name_isSet = true; +} + +qint32 +SWGTestMISettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGTestMISettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGTestMISettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGTestMISettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGTestMISettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGTestMISettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGTestMISettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGTestMISettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + + +bool +SWGTestMISettings::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_center_frequency_isSet){ isObjectUpdated = true; break;} + if(m_frequency_shift_isSet){ isObjectUpdated = true; break;} + if(m_sample_rate_isSet){ isObjectUpdated = true; break;} + if(m_log2_decim_isSet){ isObjectUpdated = true; break;} + if(m_fc_pos_isSet){ isObjectUpdated = true; break;} + if(m_sample_size_index_isSet){ isObjectUpdated = true; break;} + if(m_amplitude_bits_isSet){ isObjectUpdated = true; break;} + if(m_auto_corr_options_isSet){ isObjectUpdated = true; break;} + if(m_modulation_isSet){ isObjectUpdated = true; break;} + if(m_modulation_tone_isSet){ isObjectUpdated = true; break;} + if(m_am_modulation_isSet){ isObjectUpdated = true; break;} + if(m_fm_deviation_isSet){ isObjectUpdated = true; break;} + if(m_dc_factor_isSet){ isObjectUpdated = true; break;} + if(m_i_factor_isSet){ isObjectUpdated = true; break;} + if(m_q_factor_isSet){ isObjectUpdated = true; break;} + if(m_phase_imbalance_isSet){ isObjectUpdated = true; break;} + if(file_record_name != nullptr && *file_record_name != QString("")){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGTestMISettings.h b/swagger/sdrangel/code/qt5/client/SWGTestMISettings.h new file mode 100644 index 000000000..43d67afa7 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGTestMISettings.h @@ -0,0 +1,179 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 4.8.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGTestMISettings.h + * + * TestSourceMI + */ + +#ifndef SWGTestMISettings_H_ +#define SWGTestMISettings_H_ + +#include + + +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGTestMISettings: public SWGObject { +public: + SWGTestMISettings(); + SWGTestMISettings(QString* json); + virtual ~SWGTestMISettings(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGTestMISettings* fromJson(QString &jsonString) override; + + qint32 getCenterFrequency(); + void setCenterFrequency(qint32 center_frequency); + + qint32 getFrequencyShift(); + void setFrequencyShift(qint32 frequency_shift); + + qint32 getSampleRate(); + void setSampleRate(qint32 sample_rate); + + qint32 getLog2Decim(); + void setLog2Decim(qint32 log2_decim); + + qint32 getFcPos(); + void setFcPos(qint32 fc_pos); + + qint32 getSampleSizeIndex(); + void setSampleSizeIndex(qint32 sample_size_index); + + qint32 getAmplitudeBits(); + void setAmplitudeBits(qint32 amplitude_bits); + + qint32 getAutoCorrOptions(); + void setAutoCorrOptions(qint32 auto_corr_options); + + qint32 getModulation(); + void setModulation(qint32 modulation); + + qint32 getModulationTone(); + void setModulationTone(qint32 modulation_tone); + + qint32 getAmModulation(); + void setAmModulation(qint32 am_modulation); + + qint32 getFmDeviation(); + void setFmDeviation(qint32 fm_deviation); + + float getDcFactor(); + void setDcFactor(float dc_factor); + + float getIFactor(); + void setIFactor(float i_factor); + + float getQFactor(); + void setQFactor(float q_factor); + + float getPhaseImbalance(); + void setPhaseImbalance(float phase_imbalance); + + QString* getFileRecordName(); + void setFileRecordName(QString* file_record_name); + + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + + virtual bool isSet() override; + +private: + qint32 center_frequency; + bool m_center_frequency_isSet; + + qint32 frequency_shift; + bool m_frequency_shift_isSet; + + qint32 sample_rate; + bool m_sample_rate_isSet; + + qint32 log2_decim; + bool m_log2_decim_isSet; + + qint32 fc_pos; + bool m_fc_pos_isSet; + + qint32 sample_size_index; + bool m_sample_size_index_isSet; + + qint32 amplitude_bits; + bool m_amplitude_bits_isSet; + + qint32 auto_corr_options; + bool m_auto_corr_options_isSet; + + qint32 modulation; + bool m_modulation_isSet; + + qint32 modulation_tone; + bool m_modulation_tone_isSet; + + qint32 am_modulation; + bool m_am_modulation_isSet; + + qint32 fm_deviation; + bool m_fm_deviation_isSet; + + float dc_factor; + bool m_dc_factor_isSet; + + float i_factor; + bool m_i_factor_isSet; + + float q_factor; + bool m_q_factor_isSet; + + float phase_imbalance; + bool m_phase_imbalance_isSet; + + QString* file_record_name; + bool m_file_record_name_isSet; + + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + +}; + +} + +#endif /* SWGTestMISettings_H_ */