Merged latest developments on dev branch (4.8.1 and 4.8.2)

This commit is contained in:
f4exb 2019-05-31 12:45:29 +02:00
commit 76f20fe5d0
212 changed files with 10805 additions and 645 deletions

View File

@ -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

View File

@ -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

View File

@ -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`
**&#9888;** 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.

View File

@ -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
View File

@ -1 +0,0 @@
../CHANGELOG

804
debian/changelog vendored Normal file
View 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

View File

@ -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.

View File

@ -34,5 +34,6 @@ endif()
add_subdirectory(channelrx)
add_subdirectory(channeltx)
add_subdirectory(samplemimo)
add_subdirectory(samplesource)
add_subdirectory(samplesink)

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -65,6 +65,7 @@ private:
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
void displayStreamIndex();
void leaveEvent(QEvent*);
void enterEvent(QEvent*);

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
};

View File

@ -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);

View File

@ -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);

View File

@ -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">

View File

@ -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);

View File

@ -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;

View File

@ -10,7 +10,7 @@ This plugin can be used to listen to a narrowband FM modulated signal. "Narrowba
<h3>1: Frequency shift from center frequency of reception value</h3>
Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. Left click on a digit sets the cursor position at this digit.
Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. Left click on a digit sets the cursor position at this digit.
<h3>2: Channel power</h3>
@ -26,13 +26,13 @@ Average total power in dB relative to a +/- 1.0 amplitude signal received in the
This is the bandwidth in kHz of the channel signal before demodulation. It can be set in steps as 5, 6.25, 8.33, 10, 12.5, 15, 20, 25 and 40 kHz. The expected one side frequency deviation is 0.4 times the bandwidth.
&#9758; The demodulation is done at the channel sample rate which is guaranteed not to be lower than the requested audio sample rate but can possibly be equal to it. This means that for correct operation in any case you must ensure that the sample rate of the audio device is not lower than the Nyquist rate required to process this channel bandwidth.
&#9758; The demodulation is done at the channel sample rate which is guaranteed not to be lower than the requested audio sample rate but can possibly be equal to it. This means that for correct operation in any case you must ensure that the sample rate of the audio device is not lower than the Nyquist rate required to process this channel bandwidth.
&#9758; The channel sample rate is always the baseband signal rate divided by an integer power of two so depending on the baseband sample rate obtained from the sampling device you could also guarantee a minimal channel bandwidth. For example with a 125 kS/s baseband sample rate and a 8 kS/s audio sample rate the channel sample rate cannot be lower than 125/8 = 15.625 kS/s (125/16 = 7.8125 kS/s is too small) which is still OK for 5 or 6.25 kHz channel bandwidths.
<h3>5: AF bandwidth</h3>
This is the bandwidth of the audio signal in kHz (i.e. after demodulation). It can be set in continuous kHz steps from 1 to 20 kHz.
This is the bandwidth of the audio signal in kHz (i.e. after demodulation). It can be set in continuous kHz steps from 1 to 20 kHz.
<h3>6: Volume</h3>
@ -50,7 +50,7 @@ Case when the delta/Level squelch control (7) is off (power). This is the squelc
<h4>Audio frequency delta mode</h4>
Case when the delta/Level squelch control (7) is on (delta). In this mode the squelch compares the power of the demodulated audio signal in a low frequency band and a high frequency band. In the absence of signal the discriminator response is nearly flat and the power in the two bands is more or less balanced. In the presence of a signal the lower band will receive more power than the higher band. The squelch does the ratio of both powers and the squelch is opened if this ratio is lower than the threshold given in percent.
Case when the delta/Level squelch control (7) is on (delta). In this mode the squelch compares the power of the demodulated audio signal in a low frequency band and a high frequency band. In the absence of signal the discriminator response is nearly flat and the power in the two bands is more or less balanced. In the presence of a signal the lower band will receive more power than the higher band. The squelch does the ratio of both powers and the squelch is opened if this ratio is lower than the threshold given in percent.
A ratio of 1 (100%) will always open the squelch and a ratio of 0 will always close it. The value can be varied to detect more distorted and thus weak signals towards the higher values. The button rotation runs from higher to lower as you turn it clockwise thus giving the same feel as in power mode. The best ratio for a standard NFM transmission is ~40%.
@ -66,7 +66,7 @@ This is the squelch gate in milliseconds. The squelch input must be open for thi
<h3>10: CTCSS on/off</h3>
Use the checkbox to toggle CTCSS activation. When activated it will look for a tone squelch in the demodulated signal and display its frequency (see 10).
Use the checkbox to toggle CTCSS activation. When activated it will look for a tone squelch in the demodulated signal and display its frequency (see 10).
<h3>11: CTCSS tone</h3>
@ -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.

View File

@ -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);

View File

@ -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);
};

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -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,

View File

@ -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;

View File

@ -0,0 +1,3 @@
project(samplemimo)
add_subdirectory(testmi)

View 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})

View 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).

View 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());
}

View 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_

View 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();
}

View 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_

File diff suppressed because it is too large Load Diff

View 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;
}
}

View 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

View 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;
}
}

View 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_ */

View 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;
}

View 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_

View 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

View File

@ -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*)));

View File

@ -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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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)
{

View File

@ -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;

View File

@ -49,6 +49,7 @@ FileSinkOutput::FileSinkOutput(DeviceAPI *deviceAPI) :
m_startingTimeStamp(0),
m_masterTimer(deviceAPI->getMasterTimer())
{
m_deviceAPI->setNbSinkStreams(1);
}
FileSinkOutput::~FileSinkOutput()

View File

@ -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)
{

View File

@ -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;

View File

@ -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*)));

View File

@ -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)
{

View File

@ -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;

View File

@ -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

View File

@ -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)
{

View File

@ -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;

View File

@ -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`

View File

@ -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*)));
}

View File

@ -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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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)
{

View File

@ -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;

View File

@ -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()));

View File

@ -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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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)
{

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
};

View File

@ -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;

View File

@ -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)
{

View File

@ -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;

View File

@ -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();

View File

@ -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)
{

View File

@ -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;

View File

@ -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*)));

View File

@ -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)
{

View File

@ -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;

View File

@ -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);

View File

@ -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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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)
{

View File

@ -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;

View File

@ -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*)));

View File

@ -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)
{

View File

@ -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;

View File

@ -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*)));

View File

@ -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)
{

View File

@ -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;

View File

@ -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))

View File

@ -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;

View File

@ -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