mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-23 01:55:48 -05:00
Merged latest developments on dev branch (4.8.1 and 4.8.2)
This commit is contained in:
commit
76f20fe5d0
24
CHANGELOG
24
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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> Sun, 20 May 2019 20:44:06 +0100
|
||||
|
||||
sdrangel (4.8.0-1) unstable; urgency=medium
|
||||
|
||||
* Local output plugin
|
||||
|
@ -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
|
||||
|
20
Readme.md
20
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.
|
||||
|
||||
<h1>Note on udev rules</h1>
|
||||
|
||||
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.
|
||||
|
||||
<h1>Software build on Linux</h1>
|
||||
|
||||
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
|
||||
|
||||
<h2>Ubuntu</h2>
|
||||
|
||||
- `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`
|
||||
|
||||
<h2>Debian</h2>
|
||||
|
||||
- `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`
|
||||
|
||||
<h2>openSUSE</h2>
|
||||
|
||||
@ -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 <user>`
|
||||
- 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.
|
||||
|
||||
<h2>Fedora</h2>
|
||||
|
||||
@ -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.
|
||||
|
||||
<h2>Arch Linux / Manjaro</h2>
|
||||
|
||||
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).
|
||||
|
||||
<h1>Compile for Windows</h1>
|
||||
|
||||
@ -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.
|
||||
|
||||
<h3>Ubuntu 18.04</h2>
|
||||
|
||||
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.
|
||||
|
@ -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)
|
1
debian/changelog
vendored
1
debian/changelog
vendored
@ -1 +0,0 @@
|
||||
../CHANGELOG
|
804
debian/changelog
vendored
Normal file
804
debian/changelog
vendored
Normal file
@ -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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> Sun, 20 May 2019 20:44:06 +0100
|
||||
|
||||
sdrangel (4.8.0-1) unstable; urgency=medium
|
||||
|
||||
* Local output plugin
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> Wed, 02 Jan 2019 21:14:18 +0100
|
||||
|
||||
sdrangel (4.4.0-1) unstable; urgency=medium
|
||||
|
||||
* XTRX support
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> Sun, 08 Jul 2018 15:14:18 +0200
|
||||
|
||||
sdrangel (4.0.2-1) unstable; urgency=medium
|
||||
|
||||
* Spectrum: added averaging
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> Sun, 28 Oct 2017 18:14:18 +0100
|
||||
|
||||
sdrangel (3.7.7-1) unstable; urgency=medium
|
||||
|
||||
* PlutoSDR: removed Tx support
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> Fri, 13 Jan 2017 03:14:18 +0100
|
||||
|
||||
sdrangel (3.1.0-1) unstable; urgency=medium
|
||||
|
||||
* HackRF Tx support
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> Sun, 01 Jan 2017 23:14:18 +0100
|
||||
|
||||
sdrangel (2.5.2-1) unstable; urgency=medium
|
||||
|
||||
* Changed modulators source device feeding
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> Sun, 25 Dec 2016 23:14:18 +0100
|
||||
|
||||
sdrangel (2.5.1-1) unstable; urgency=medium
|
||||
|
||||
* WFM Modulator
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> Sun, 18 Dec 2016 23:14:18 +0100
|
||||
|
||||
sdrangel (2.5.0-1) unstable; urgency=medium
|
||||
|
||||
* SSB Modulator
|
||||
* CW Keyer
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> Thu, 15 Dec 2016 23:14:18 +0100
|
||||
|
||||
sdrangel (2.4.0-1) unstable; urgency=medium
|
||||
|
||||
* NFM Modulator
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> Sun, 05 Dec 2016 23:14:18 +0100
|
||||
|
||||
sdrangel (2.3.1-1) unstable; urgency=medium
|
||||
|
||||
* AM Modulator: support file input
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> 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 <f4exb06@gmail.com> Sun, 13 Nov 2016 23:14:18 +0100
|
||||
|
||||
sdrangel (2.2.2-1) unstable; urgency=medium
|
||||
|
||||
* Baseband Halfband FIR filter optimizations
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> 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 <f4exb06@gmail.com> 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 <f4exb06@gmail.com> Tue, 24 Oct 2016 23:15:15 +0200
|
@ -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
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
Binary file not shown.
@ -34,5 +34,6 @@ endif()
|
||||
|
||||
add_subdirectory(channelrx)
|
||||
add_subdirectory(channeltx)
|
||||
add_subdirectory(samplemimo)
|
||||
add_subdirectory(samplesource)
|
||||
add_subdirectory(samplesink)
|
||||
|
@ -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<QString>& 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)
|
||||
|
@ -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<Real, double, 16> m_movingAverage;
|
||||
SimpleAGC<4800> m_volumeAGC;
|
||||
Bandpass<Real> m_bandpass;
|
||||
Lowpass<Real> m_lowpass;
|
||||
Lowpass<std::complex<float> > m_pllFilt;
|
||||
PhaseLockComplex m_pll;
|
||||
fftfilt* DSBFilter;
|
||||
|
@ -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);
|
||||
|
@ -65,6 +65,7 @@ private:
|
||||
void blockApplySettings(bool block);
|
||||
void applySettings(bool force = false);
|
||||
void displaySettings();
|
||||
void displayStreamIndex();
|
||||
|
||||
void leaveEvent(QEvent*);
|
||||
void enterEvent(QEvent*);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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<QString>& 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);
|
||||
}
|
||||
|
@ -220,8 +220,9 @@ private:
|
||||
Interpolator m_interpolator;
|
||||
Real m_interpolatorDistance;
|
||||
Real m_interpolatorDistanceRemain;
|
||||
Lowpass<Real> m_lowpass;
|
||||
Lowpass<Real> m_ctcssLowpass;
|
||||
Bandpass<Real> m_bandpass;
|
||||
Lowpass<Real> 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<QString>& channelSettingsKeys, const NFMDemodSettings& settings, bool force);
|
||||
|
||||
void processOneSample(Complex &ci);
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -574,6 +574,20 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="highPassFilter">
|
||||
<property name="toolTip">
|
||||
<string>High pass audio filter</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/filter_highpass.png</normaloff>:/filter_highpass.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="audioMute">
|
||||
<property name="toolTip">
|
||||
|
@ -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<int>(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);
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
||||
<h3>13: Audio mute and audio output select</h3>
|
||||
<h3>13: Audio high pass filter</h3>
|
||||
|
||||
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.
|
||||
|
||||
<h3>14: Audio mute and audio output select</h3>
|
||||
|
||||
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.
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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<QString>& channelSettingsKeys, const SSBDemodSettings& settings, bool force);
|
||||
|
||||
void processOneSample(Complex &ci);
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
};
|
||||
|
@ -371,14 +371,25 @@ void SSBDemodGUI::applySettings(bool force)
|
||||
}
|
||||
}
|
||||
|
||||
int SSBDemodGUI::spanLog2Limit(int spanLog2)
|
||||
{
|
||||
while (((m_ssbDemod->getAudioSampleRate() / (1<<spanLog2)) > 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<<spanLog2);
|
||||
int bw = ui->BW->value();
|
||||
int lw = ui->lowCut->value();
|
||||
int bwMax = m_ssbDemod->getAudioSampleRate() / (100*(1<<spanLog2));
|
||||
int bwMax = std::min(m_ssbDemod->getAudioSampleRate() / (100*(1<<spanLog2)), m_ssbDemod->getInputSampleRate()/100);
|
||||
int tickInterval = m_spectrumRate / 1200;
|
||||
tickInterval = tickInterval == 0 ? 1 : tickInterval;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
3
plugins/samplemimo/CMakeLists.txt
Normal file
3
plugins/samplemimo/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
project(samplemimo)
|
||||
|
||||
add_subdirectory(testmi)
|
54
plugins/samplemimo/testmi/CMakeLists.txt
Normal file
54
plugins/samplemimo/testmi/CMakeLists.txt
Normal file
@ -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})
|
134
plugins/samplemimo/testmi/readme.md
Normal file
134
plugins/samplemimo/testmi/readme.md
Normal file
@ -0,0 +1,134 @@
|
||||
<h1>Test source input plugin</h1>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
This input sample source plugin is an internal continuous wave generator that can be used to carry out test of software internals.
|
||||
|
||||
<h2>Build</h2>
|
||||
|
||||
The plugin is present in the core of the software and thus is always present in the list of sources.
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||
![Test source input plugin GUI](../../../doc/img/TestSourceInput_plugin.png)
|
||||
|
||||
<h3>1: Common stream parameters</h3>
|
||||
|
||||
![Remote source input stream GUI](../../../doc/img/RemoteInput_plugin_01.png)
|
||||
|
||||
<h4>1.1: Frequency</h4>
|
||||
|
||||
This is the center frequency of reception in kHz.
|
||||
|
||||
<h4>1.2: Start/Stop</h4>
|
||||
|
||||
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.
|
||||
|
||||
<h4>1.3: Record</h4>
|
||||
|
||||
Record baseband I/Q stream toggle button
|
||||
|
||||
<h4>1.4: Stream sample rate</h4>
|
||||
|
||||
Baseband I/Q sample rate in kS/s. This is the device to host sample rate (3) divided by the decimation factor (4).
|
||||
|
||||
<h3>2: Various options</h3>
|
||||
|
||||
![Test source input plugin GUI 2](../../../doc/img/TestSourceInput_plugin_2.png)
|
||||
|
||||
<h4>2.1: Auto corrections</h4>
|
||||
|
||||
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.
|
||||
|
||||
<h4>2.2: Decimation factor</h4>
|
||||
|
||||
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.
|
||||
|
||||
<h4>2.3: Baseband center frequency position relative the center frequency</h4>
|
||||
|
||||
- **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.
|
||||
|
||||
<h3>2.4: Sample size</h3>
|
||||
|
||||
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+
|
||||
|
||||
<h3>3: Sample rate</h3>
|
||||
|
||||
This controls the generator sample rate in samples per second.
|
||||
|
||||
<h3>4: Modulation</h4>
|
||||
|
||||
- **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
|
||||
|
||||
<h3>5: Modulating tone frequency</h3>
|
||||
|
||||
This controls the modulating tone frequency in kHz in 10 Hz steps.
|
||||
|
||||
<h3>6: Carrier shift from center frequency</h3>
|
||||
|
||||
Use this control to set the offset of the carrier from the center frequency of reception.
|
||||
|
||||
<h3>7: AM modulation factor</h3>
|
||||
|
||||
This controls the AM modulation factor from 0 to 99%
|
||||
|
||||
<h3>8: FM deviation</h3>
|
||||
|
||||
This controls the frequency modulation deviation in kHz in 100 Hz steps. It cannot exceed the sample rate.
|
||||
|
||||
<h3>9: Amplitude coarse control</h3>
|
||||
|
||||
This slider controls the number of amplitude bits by steps of 100 bits. The total number of amplitude bits appear on the right.
|
||||
|
||||
<h3>10: Amplitude fine control</h3>
|
||||
|
||||
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.
|
||||
|
||||
<h3>11: DC bias</h3>
|
||||
|
||||
Use this slider to give a DC component in percentage of maximum amplitude.
|
||||
|
||||
<h3>12: I bias</h3>
|
||||
|
||||
Use this slider to give an in-phase (I) bias in percentage of maximum amplitude.
|
||||
|
||||
<h3>13: Q bias</h3>
|
||||
|
||||
Use this slider to give an quadrature-phase (Q) bias in percentage of maximum amplitude.
|
||||
|
||||
<h3>14: Phase imbalance</h3>
|
||||
|
||||
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).
|
754
plugins/samplemimo/testmi/testmi.cpp
Normal file
754
plugins/samplemimo/testmi/testmi.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QBuffer>
|
||||
|
||||
#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<<m_settings.m_log2Decim);
|
||||
}
|
||||
|
||||
quint64 TestMI::getSourceCenterFrequency(int index) const
|
||||
{
|
||||
(void) index;
|
||||
return m_settings.m_centerFrequency;
|
||||
}
|
||||
|
||||
void TestMI::setSourceCenterFrequency(qint64 centerFrequency, int index)
|
||||
{
|
||||
(void) index;
|
||||
TestMISettings settings = m_settings;
|
||||
settings.m_centerFrequency = centerFrequency;
|
||||
|
||||
MsgConfigureTestSource* message = MsgConfigureTestSource::create(settings, false);
|
||||
m_inputMessageQueue.push(message);
|
||||
|
||||
if (m_guiMessageQueue)
|
||||
{
|
||||
MsgConfigureTestSource* messageToGUI = MsgConfigureTestSource::create(settings, false);
|
||||
m_guiMessageQueue->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<QString> 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<<settings.m_log2Decim);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_centerFrequency != settings.m_centerFrequency)
|
||||
|| (m_settings.m_fcPos != settings.m_fcPos)
|
||||
|| (m_settings.m_frequencyShift != settings.m_frequencyShift)
|
||||
|| (m_settings.m_sampleRate != settings.m_sampleRate)
|
||||
|| (m_settings.m_log2Decim != settings.m_log2Decim) || force)
|
||||
{
|
||||
reverseAPIKeys.append("centerFrequency");
|
||||
reverseAPIKeys.append("fcPos");
|
||||
reverseAPIKeys.append("frequencyShift");
|
||||
|
||||
qint64 deviceCenterFrequency = DeviceSampleSource::calculateDeviceCenterFrequency(
|
||||
settings.m_centerFrequency,
|
||||
0, // no transverter mode
|
||||
settings.m_log2Decim,
|
||||
(DeviceSampleSource::fcPos_t) settings.m_fcPos,
|
||||
settings.m_sampleRate,
|
||||
DeviceSampleSource::FrequencyShiftScheme::FSHIFT_STD,
|
||||
false);
|
||||
|
||||
int frequencyShift = settings.m_frequencyShift;
|
||||
quint32 devSampleRate = settings.m_sampleRate;
|
||||
|
||||
if (settings.m_log2Decim != 0)
|
||||
{
|
||||
frequencyShift += DeviceSampleSource::calculateFrequencyShift(
|
||||
settings.m_log2Decim,
|
||||
(DeviceSampleSource::fcPos_t) settings.m_fcPos,
|
||||
settings.m_sampleRate,
|
||||
DeviceSampleSource::FSHIFT_STD);
|
||||
}
|
||||
|
||||
if (m_testSourceThread != 0)
|
||||
{
|
||||
m_testSourceThread->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<<m_settings.m_log2Decim) << "Hz"
|
||||
<< " f shift: " << settings.m_frequencyShift;
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_amplitudeBits != settings.m_amplitudeBits) || force)
|
||||
{
|
||||
reverseAPIKeys.append("amplitudeBits");
|
||||
|
||||
if (m_testSourceThread != 0) {
|
||||
m_testSourceThread->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<<settings.m_log2Decim);
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, settings.m_centerFrequency);
|
||||
m_fileSink->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<QString>& 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());
|
||||
}
|
165
plugins/samplemimo/testmi/testmi.h
Normal file
165
plugins/samplemimo/testmi/testmi.h
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _TESTMI_TESTMI_H_
|
||||
#define _TESTMI_TESTMI_H_
|
||||
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QTimer>
|
||||
#include <QNetworkRequest>
|
||||
|
||||
#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<QString>& deviceSettingsKeys, const TestMISettings& settings, bool force);
|
||||
void webapiReverseSendStartStop(bool start);
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
};
|
||||
|
||||
#endif // _TESTMI_TESTMI_H_
|
540
plugins/samplemimo/testmi/testmigui.cpp
Normal file
540
plugins/samplemimo/testmi/testmigui.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <QTime>
|
||||
#include <QDateTime>
|
||||
#include <QString>
|
||||
#include <QMessageBox>
|
||||
|
||||
#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();
|
||||
}
|
106
plugins/samplemimo/testmi/testmigui.h
Normal file
106
plugins/samplemimo/testmi/testmigui.h
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _TESTMI_TESTMIGUI_H_
|
||||
#define _TESTMI_TESTMIGUI_H_
|
||||
|
||||
#include <plugin/plugininstancegui.h>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#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_
|
1012
plugins/samplemimo/testmi/testmigui.ui
Normal file
1012
plugins/samplemimo/testmi/testmigui.ui
Normal file
File diff suppressed because it is too large
Load Diff
111
plugins/samplemimo/testmi/testmiplugin.cpp
Normal file
111
plugins/samplemimo/testmi/testmiplugin.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QtPlugin>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
53
plugins/samplemimo/testmi/testmiplugin.h
Normal file
53
plugins/samplemimo/testmi/testmiplugin.h
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _TESTMI_TESTMIPLUGIN_H
|
||||
#define _TESTMI_TESTMIPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#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
|
150
plugins/samplemimo/testmi/testmisettings.cpp
Normal file
150
plugins/samplemimo/testmi/testmisettings.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QtGlobal>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
79
plugins/samplemimo/testmi/testmisettings.h
Normal file
79
plugins/samplemimo/testmi/testmisettings.h
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _TESTMI_TESTMISETTINGS_H_
|
||||
#define _TESTMI_TESTMISETTINGS_H_
|
||||
|
||||
#include <QString>
|
||||
|
||||
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_ */
|
450
plugins/samplemimo/testmi/testmithread.cpp
Normal file
450
plugins/samplemimo/testmi/testmithread.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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<<patPulseIndex)) != 0 ? 0.3 : 0.0; // make binary pattern ~-10dB vs sync pattern
|
||||
m_buf[i++] = (int16_t) (patFigure * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
|
||||
m_buf[i++] = (int16_t) (patFigure * (float) m_phaseImbalance * m_amplitudeBitsQ);
|
||||
}
|
||||
|
||||
if (m_pulseSampleCount < (4+m_pulsePatternPlaces)*m_pulseWidth - 1)
|
||||
{
|
||||
m_pulseSampleCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_pulsePatternCount < m_pulsePatternCycle - 1) {
|
||||
m_pulsePatternCount++;
|
||||
} else {
|
||||
m_pulsePatternCount = 0;
|
||||
}
|
||||
|
||||
m_pulseSampleCount = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TestMISettings::ModulationPattern1: // sawtooth pattern
|
||||
{
|
||||
Real re, im;
|
||||
re = (float) (m_pulseWidth - m_pulseSampleCount) / (float) m_pulseWidth;
|
||||
im = m_phaseImbalance*re;
|
||||
m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
|
||||
m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ);
|
||||
|
||||
if (m_pulseSampleCount < m_pulseWidth - 1) {
|
||||
m_pulseSampleCount++;
|
||||
} else {
|
||||
m_pulseSampleCount = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TestMISettings::ModulationPattern2: // 50% duty cycle square pattern
|
||||
{
|
||||
if (m_pulseSampleCount < m_pulseWidth) // 1
|
||||
{
|
||||
m_buf[i++] = (int16_t) (m_amplitudeBitsI + m_amplitudeBitsDC);
|
||||
m_buf[i++] = (int16_t) (m_phaseImbalance * (float) m_amplitudeBitsQ);
|
||||
} else { // 0
|
||||
m_buf[i++] = m_amplitudeBitsDC;
|
||||
m_buf[i++] = 0;
|
||||
}
|
||||
|
||||
if (m_pulseSampleCount < 2*m_pulseWidth - 1) {
|
||||
m_pulseSampleCount++;
|
||||
} else {
|
||||
m_pulseSampleCount = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TestMISettings::ModulationNone:
|
||||
default:
|
||||
{
|
||||
Complex c = m_nco.nextIQ(m_phaseImbalance);
|
||||
m_buf[i++] = (int16_t) (c.real() * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
|
||||
m_buf[i++] = (int16_t) (c.imag() * (float) m_amplitudeBitsQ);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
callback(m_buf, n);
|
||||
}
|
||||
|
||||
void TestMIThread::pullAF(Real& afSample)
|
||||
{
|
||||
afSample = m_toneNco.next();
|
||||
}
|
||||
|
||||
// call appropriate conversion (decimation) routine depending on the number of sample bits
|
||||
void TestMIThread::callback(const qint16* buf, qint32 len)
|
||||
{
|
||||
SampleVector::iterator it = m_convertBuffer.begin();
|
||||
|
||||
switch (m_bitSizeIndex)
|
||||
{
|
||||
case 0: // 8 bit samples
|
||||
convert_8(&it, buf, len);
|
||||
break;
|
||||
case 1: // 12 bit samples
|
||||
convert_12(&it, buf, len);
|
||||
break;
|
||||
case 2: // 16 bit samples
|
||||
default:
|
||||
convert_16(&it, buf, len);
|
||||
break;
|
||||
}
|
||||
|
||||
m_sampleFifo->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;
|
||||
}
|
385
plugins/samplemimo/testmi/testmithread.h
Normal file
385
plugins/samplemimo/testmi/testmithread.h
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _TESTMI_TESTMITHREAD_H_
|
||||
#define _TESTMI_TESTMITHREAD_H_
|
||||
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
#include <QTimer>
|
||||
#include <QElapsedTimer>
|
||||
#include <QDebug>
|
||||
|
||||
#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<qint32, qint16, SDR_RX_SAMP_SZ, 8> m_decimators_8;
|
||||
Decimators<qint32, qint16, SDR_RX_SAMP_SZ, 12> m_decimators_12;
|
||||
Decimators<qint32, qint16, SDR_RX_SAMP_SZ, 16> 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_
|
48
plugins/samplemimo/testmi/testsourcemi.pro
Normal file
48
plugins/samplemimo/testmi/testsourcemi.pro
Normal file
@ -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
|
@ -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*)));
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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<QString> 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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -49,6 +49,7 @@ FileSinkOutput::FileSinkOutput(DeviceAPI *deviceAPI) :
|
||||
m_startingTimeStamp(0),
|
||||
m_masterTimer(deviceAPI->getMasterTimer())
|
||||
{
|
||||
m_deviceAPI->setNbSinkStreams(1);
|
||||
}
|
||||
|
||||
FileSinkOutput::~FileSinkOutput()
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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*)));
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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`
|
||||
|
@ -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*)));
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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()));
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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<void *>& 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_channels[channel].m_log2Interp));
|
||||
beginRead -= len;
|
||||
|
||||
if (m_channels[channel].m_log2Interp == 0)
|
||||
{
|
||||
m_channels[channel].m_interpolatorsIF.interpolate1(&beginRead, buf, len*2);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (m_channels[channel].m_log2Interp)
|
||||
{
|
||||
case 1:
|
||||
m_channels[channel].m_interpolatorsIF.interpolate2_cen(&beginRead, buf, len*2);
|
||||
break;
|
||||
case 2:
|
||||
m_channels[channel].m_interpolatorsIF.interpolate4_cen(&beginRead, buf, len*2);
|
||||
break;
|
||||
case 3:
|
||||
m_channels[channel].m_interpolatorsIF.interpolate8_cen(&beginRead, buf, len*2);
|
||||
break;
|
||||
case 4:
|
||||
m_channels[channel].m_interpolatorsIF.interpolate16_cen(&beginRead, buf, len*2);
|
||||
break;
|
||||
case 5:
|
||||
m_channels[channel].m_interpolatorsIF.interpolate32_cen(&beginRead, buf, len*2);
|
||||
break;
|
||||
case 6:
|
||||
m_channels[channel].m_interpolatorsIF.interpolate64_cen(&beginRead, buf, len*2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::fill(buf, buf+2*len, 0.0f);
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include "soapysdr/devicesoapysdrshared.h"
|
||||
#include "dsp/interpolators.h"
|
||||
#include "dsp/interpolatorsif.h"
|
||||
|
||||
class SampleSourceFifo;
|
||||
|
||||
@ -56,6 +57,7 @@ private:
|
||||
Interpolators<qint8, SDR_TX_SAMP_SZ, 8> m_interpolators8;
|
||||
Interpolators<qint16, SDR_TX_SAMP_SZ, 12> m_interpolators12;
|
||||
Interpolators<qint16, SDR_TX_SAMP_SZ, 16> m_interpolators16;
|
||||
InterpolatorsIF<SDR_TX_SAMP_SZ, SDR_TX_SAMP_SZ> 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<void *>& buffs, qint32 samplesPerChannel);
|
||||
};
|
||||
|
||||
|
@ -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<QString> reverseAPIKeys;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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*)));
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -50,6 +50,7 @@ FCDProInput::FCDProInput(DeviceAPI *deviceAPI) :
|
||||
m_fcdFIFO.setSize(20*fcd_traits<Pro>::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*)));
|
||||
|
@ -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<Pro>::interfaceIID)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -50,6 +50,7 @@ FCDProPlusInput::FCDProPlusInput(DeviceAPI *deviceAPI) :
|
||||
m_fcdFIFO.setSize(20*fcd_traits<ProPlus>::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*)));
|
||||
|
@ -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<ProPlus>::interfaceIID)
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user