mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-02-03 09:44:01 -05:00
Merge branch 'dev'
This commit is contained in:
commit
3bfb2452ba
@ -1,6 +1,19 @@
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
cmake_policy(SET CMP0043 OLD)
|
||||
|
||||
# use, i.e. don't skip the full RPATH for the build tree
|
||||
set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
||||
|
||||
# when building, don't use the install RPATH already
|
||||
# (but later on when installing)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
||||
|
||||
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
|
||||
|
||||
# add the automatically determined parts of the RPATH
|
||||
# which point to directories outside the build tree to the install RPATH
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
|
||||
option(V4L-RTL "Use Linux Kernel RTL-SDR Source." OFF)
|
||||
option(V4L-MSI "Use Linux Kernel MSI2500 Source." OFF)
|
||||
option(BUILD_TYPE "Build type (RELEASE, RELEASEWITHDBGINFO, DEBUG" RELEASE)
|
||||
@ -126,6 +139,7 @@ set(sdrbase_SOURCES
|
||||
sdrbase/settings/preset.cpp
|
||||
sdrbase/settings/mainsettings.cpp
|
||||
|
||||
sdrbase/util/CRC64.cpp
|
||||
sdrbase/util/db.cpp
|
||||
sdrbase/util/message.cpp
|
||||
sdrbase/util/messagequeue.cpp
|
||||
@ -208,7 +222,8 @@ set(sdrbase_HEADERS
|
||||
include/settings/preset.h
|
||||
include/settings/mainsettings.h
|
||||
|
||||
include/util/db.h
|
||||
include/util/CRC64.h
|
||||
include/util/db.h
|
||||
include/util/export.h
|
||||
include/util/message.h
|
||||
include/util/messagequeue.h
|
||||
@ -355,3 +370,20 @@ if(LIBUSB_FOUND AND UNIX)
|
||||
add_subdirectory(fcdhid)
|
||||
add_subdirectory(fcdlib)
|
||||
endif(LIBUSB_FOUND AND UNIX)
|
||||
|
||||
##############################################################################
|
||||
|
||||
#install targets
|
||||
install(TARGETS sdrangel DESTINATION bin)
|
||||
install(TARGETS sdrbase DESTINATION lib)
|
||||
|
||||
##############################################################################
|
||||
|
||||
# uninstall target
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
add_custom_target(uninstall
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
||||
|
34
Readme.md
34
Readme.md
@ -47,7 +47,7 @@ If you use your own location for gr.osmocom install directory you need to specif
|
||||
|
||||
`-DGNURADIO_OSMOSDR_LIBRARIES=/opt/install/gr-osmosdr/lib/libgnuradio-osmosdr.so -DGNURADIO_OSMOSDR_INCLUDE_DIRS=/opt/install/gr-osmosdr/include`
|
||||
|
||||
<h3>v4l-\*</h3>
|
||||
<h3>v4l-*</h3>
|
||||
|
||||
Use `cmake ../ -DV4L-RTL=ON` to build the Linux kernel driver for RTL-SDR (Experimental). Needs a recent kernel and libv4l2. Will need extra work to support SDRPlay. Needs `cp KERNEL_SOURCE/include/linux/compiler.h /usr/include/linux/` and `cp KERNEL_SOURCE/include/uapi/linux/videodev2.h /usr/include/uapi/linux/` and package `libv4l-dev`.
|
||||
|
||||
@ -99,6 +99,22 @@ If you use your own location for librtlsdr install directory you need to specify
|
||||
|
||||
`-DLIBRTLSDR_LIBRARIES=/opt/install/librtlsdr/lib/librtlsdr.so -DLIBRTLSDR_INCLUDE_DIR=/opt/install/librtlsdr/include`
|
||||
|
||||
<h1>Plugins for special sample sources</h1>
|
||||
|
||||
<h2>File input</h2>
|
||||
|
||||
The file input plugin allows the playback of a recorded IQ file. Such a file is obtained using the recording feature. Press F7 to start recording and F8 to stop. The file has a fixed name `test.sdriq` created in the current directory.
|
||||
|
||||
Note that this plugin does not require any of the hardware support libraries nor the libusb library. It is alwasys available in the list of devices as `FileSource[0]` even if no physical device is connected.
|
||||
|
||||
<h2>SDRdaemon input</h2>
|
||||
|
||||
This is the client side of the SDRdaemon server. See the [SDRdaemon](https://github.com/f4exb/sdrdaemon) project in this Github repository. You must specify the address and UDP port to which the server connects and samples will flow into the SDRangel application (default is `127.0.0.1`port `9090`). It uses the meta data to retrieve the sample flow characteristics such as sample rate and receiveng center frequency.
|
||||
|
||||
There is an automated skew rate compensation in place. During rate readjustemnt streaming can be suspended or signal glitches can occur for about one second.
|
||||
|
||||
Note that this plugin does not require any of the hardware support libraries nor the libusb library. It is alwasys available in the list of devices as `SDRdaemon[0]` even if no physical device is connected.
|
||||
|
||||
<h1>Software build</h1>
|
||||
|
||||
<h2>Ubuntu</h2>
|
||||
@ -118,7 +134,7 @@ Install cmake version 3:
|
||||
|
||||
<h3>With newer versions just do:</h3>
|
||||
|
||||
- `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`
|
||||
- `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 liblz4-dev`
|
||||
- `mkdir build && cd build && cmake ../ && make`
|
||||
|
||||
`librtlsdr-dev` is in the `universe` repo. (utopic 14.10 amd64.)
|
||||
@ -145,7 +161,7 @@ For Debian Jessie or Stretch:
|
||||
|
||||
This has been tested with the bleeding edge "Thumbleweed" distribution:
|
||||
|
||||
`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`
|
||||
`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 liblz4 liblz4-devel`
|
||||
|
||||
Then you should be all set to build the software with `cmake` and `make` as discussed earlier.
|
||||
|
||||
@ -158,7 +174,7 @@ This has been tested with Fedora 23 and 22:
|
||||
|
||||
- `sudo dnf groupinstall "C Development Tools and Libraries"`
|
||||
- `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`
|
||||
- `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 liblz4 liblz4-devel`
|
||||
|
||||
Then you should be all set to build the software with `cmake` and `make` as discussed earlier.
|
||||
|
||||
@ -168,13 +184,19 @@ Then you should be all set to build the software with `cmake` and `make` as disc
|
||||
|
||||
Tested with the 15.09 version with LXDE desktop (community supported). The exact desktop environment should not matter anyway. Since Manjaro is Arch Linux based 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`
|
||||
`sudo pacman -S cmake pkg-config fftw qt5-multimedia qt5-tools qt5-base libusb boost boost-libs pulseaudio lz4`
|
||||
|
||||
Then you should be all set to build the software with `cmake` and `make` as discussed earlier.
|
||||
|
||||
- Note1 for udev rules: the same as for openSUSE and Fedora applies.
|
||||
- Note2: A package has been created in the AUR (thanks Mikos!), see: [sdrangel-git](https://aur.archlinux.org/packages/sdrangel-git). It is based on the `205fee6` commit of 8th December 2015.
|
||||
|
||||
<h1>Software installation</h1>
|
||||
|
||||
Simply do `make install` or `sudo make install` depending on you user rights on the target installation directory. On most systems the default installation directory is `/usr/local` a custom installation directory can be specified with the `-DCMAKE_INSTALL_PREFIX=...` option on the `cmake` command line as usual with cmake.
|
||||
|
||||
You can uninstall the software with `make uninstall` or `sudo make uninstall` from the build directory (it needs the `install_manifest.txt` file in the same directory and is automatically created by the `make install`command). Note that this will not remove the possible empty directories.
|
||||
|
||||
<h1>Known Issues</h1>
|
||||
|
||||
- The message queuing model supports a n:1 connection to an object (on its input queue) and a 1:1 connection from an object (on its output queue). Assuming a different model can cause insidious disruptions.
|
||||
@ -202,7 +224,7 @@ See the v1.0.1 first official relase [release notes](https://github.com/f4exb/sd
|
||||
- Headless mode based on a saved configuration in above human readable form
|
||||
- Allow arbitrary sample rate for channelizers and demodulators (not multiple of 48 kHz). Prerequisite for polyphase channelizer
|
||||
- Implement polyphase channelizer
|
||||
- Level calibration
|
||||
- Level calibration
|
||||
- Even more demods ...
|
||||
|
||||
<h1>Developper's notes</h1>
|
||||
|
27
cmake/Modules/FindLZ4.cmake
Normal file
27
cmake/Modules/FindLZ4.cmake
Normal file
@ -0,0 +1,27 @@
|
||||
INCLUDE(FindPkgConfig)
|
||||
PKG_CHECK_MODULES(PC_LZ4 "liblz4")
|
||||
|
||||
FIND_PATH(LZ4_INCLUDE_DIRS
|
||||
NAMES lz4.h
|
||||
HINTS ${PC_LZ4_INCLUDE_DIR}
|
||||
${CMAKE_INSTALL_PREFIX}/include
|
||||
${LIBLZ4_INSTALL_PREFIX}/include
|
||||
PATHS
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
)
|
||||
|
||||
FIND_LIBRARY(LZ4_LIBRARIES
|
||||
NAMES lz4 liblz4
|
||||
HINTS ${PC_LZ4_LIBDIR}
|
||||
${CMAKE_INSTALL_PREFIX}/lib
|
||||
${CMAKE_INSTALL_PREFIX}/lib64
|
||||
PATHS
|
||||
${LZ4_INCLUDE_DIRS}/../lib
|
||||
/usr/local/lib
|
||||
/usr/lib
|
||||
)
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZ4 DEFAULT_MSG LZ4_LIBRARIES LZ4_INCLUDE_DIRS)
|
||||
MARK_AS_ADVANCED(LZ4_LIBRARIES LZ4_INCLUDE_DIRS)
|
21
cmake_uninstall.cmake.in
Normal file
21
cmake_uninstall.cmake.in
Normal file
@ -0,0 +1,21 @@
|
||||
if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||
endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||
|
||||
file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
|
||||
string(REGEX REPLACE "\n" ";" files "${files}")
|
||||
foreach(file ${files})
|
||||
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
|
||||
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
exec_program(
|
||||
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||
OUTPUT_VARIABLE rm_out
|
||||
RETURN_VALUE rm_retval
|
||||
)
|
||||
if(NOT "${rm_retval}" STREQUAL 0)
|
||||
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
|
||||
endif(NOT "${rm_retval}" STREQUAL 0)
|
||||
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
|
||||
endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
endforeach(file)
|
@ -28,3 +28,5 @@ add_library(fcdhid SHARED
|
||||
target_link_libraries(fcdhid
|
||||
${LIBUSB_LIBRARIES}
|
||||
)
|
||||
|
||||
install(TARGETS fcdhid DESTINATION lib)
|
@ -27,3 +27,5 @@ add_library(fcdlib SHARED
|
||||
)
|
||||
|
||||
target_link_libraries(fcdlib)
|
||||
|
||||
install(TARGETS fcdlib DESTINATION lib)
|
38
include/util/CRC64.h
Normal file
38
include/util/CRC64.h
Normal file
@ -0,0 +1,38 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 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 //
|
||||
// //
|
||||
// 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 INCLUDE_CRC64_H_
|
||||
#define INCLUDE_CRC64_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class CRC64
|
||||
{
|
||||
public:
|
||||
CRC64();
|
||||
~CRC64();
|
||||
uint64_t calculate_crc(uint8_t *stream, int length);
|
||||
|
||||
private:
|
||||
void build_crc_table();
|
||||
|
||||
uint64_t m_crcTable[256];
|
||||
static const uint64_t m_poly;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* INCLUDE_CRC64_H_ */
|
@ -44,4 +44,6 @@ target_link_libraries(demodam
|
||||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(demodam Core Widgets OpenGL Multimedia)
|
||||
qt5_use_modules(demodam Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS demodam DESTINATION lib/plugins/channel)
|
@ -51,4 +51,6 @@ target_link_libraries(demodbfm
|
||||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(demodbfm Core Widgets OpenGL Multimedia)
|
||||
qt5_use_modules(demodbfm Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS demodbfm DESTINATION lib/plugins/channel)
|
@ -45,3 +45,5 @@ target_link_libraries(chanalyzer
|
||||
)
|
||||
|
||||
qt5_use_modules(chanalyzer Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS chanalyzer DESTINATION lib/plugins/channel)
|
||||
|
@ -45,3 +45,5 @@ target_link_libraries(demodlora
|
||||
)
|
||||
|
||||
qt5_use_modules(demodlora Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS demodlora DESTINATION lib/plugins/channel)
|
||||
|
@ -44,4 +44,6 @@ target_link_libraries(demodnfm
|
||||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(demodnfm Core Widgets OpenGL Multimedia)
|
||||
qt5_use_modules(demodnfm Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS demodnfm DESTINATION lib/plugins/channel)
|
@ -45,3 +45,5 @@ target_link_libraries(demodssb
|
||||
)
|
||||
|
||||
qt5_use_modules(demodssb Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS demodssb DESTINATION lib/plugins/channel)
|
||||
|
@ -45,3 +45,5 @@ target_link_libraries(demodtcpsrc
|
||||
)
|
||||
|
||||
qt5_use_modules(demodtcpsrc Core Widgets OpenGL Network)
|
||||
|
||||
install(TARGETS demodtcpsrc DESTINATION lib/plugins/channel)
|
||||
|
@ -45,3 +45,5 @@ target_link_libraries(demodudpsrc
|
||||
)
|
||||
|
||||
qt5_use_modules(demodudpsrc Core Widgets OpenGL Network)
|
||||
|
||||
install(TARGETS demodudpsrc DESTINATION lib/plugins/channel)
|
||||
|
@ -305,7 +305,7 @@ void UDPSrcGUI::applySettings()
|
||||
|
||||
int udpPort = ui->udpPort->text().toInt(&ok);
|
||||
|
||||
if((!ok) || (udpPort < 1) || (udpPort > 65535))
|
||||
if((!ok) || (udpPort < 1024) || (udpPort > 65535))
|
||||
{
|
||||
udpPort = 9999;
|
||||
}
|
||||
|
@ -44,4 +44,6 @@ target_link_libraries(demodwfm
|
||||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(demodwfm Core Widgets OpenGL Multimedia)
|
||||
qt5_use_modules(demodwfm Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS demodwfm DESTINATION lib/plugins/channel)
|
@ -43,3 +43,4 @@ if(LIBUSB_FOUND AND LIBHACKRF_FOUND)
|
||||
endif(LIBUSB_FOUND AND LIBHACKRF_FOUND)
|
||||
|
||||
add_subdirectory(filesource)
|
||||
add_subdirectory(sdrdaemon)
|
||||
|
@ -51,3 +51,5 @@ target_link_libraries(inputairspy
|
||||
)
|
||||
|
||||
qt5_use_modules(inputairspy Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS inputairspy DESTINATION lib/plugins/samplesource)
|
||||
|
@ -50,3 +50,5 @@ target_link_libraries(inputbladerf
|
||||
)
|
||||
|
||||
qt5_use_modules(inputbladerf Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS inputbladerf DESTINATION lib/plugins/samplesource)
|
||||
|
@ -53,3 +53,5 @@ target_link_libraries(inputfcdpro
|
||||
)
|
||||
|
||||
qt5_use_modules(inputfcdpro Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS inputfcdpro DESTINATION lib/plugins/samplesource)
|
@ -53,3 +53,5 @@ target_link_libraries(inputfcdproplus
|
||||
)
|
||||
|
||||
qt5_use_modules(inputfcdproplus Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS inputfcdproplus DESTINATION lib/plugins/samplesource)
|
||||
|
@ -45,3 +45,5 @@ target_link_libraries(inputfilesource
|
||||
)
|
||||
|
||||
qt5_use_modules(inputfilesource Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS inputfilesource DESTINATION lib/plugins/samplesource)
|
||||
|
@ -239,52 +239,3 @@ bool FileSourceInput::handleMessage(const Message& message)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileSourceInput::applySettings(const Settings& settings, bool force)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
bool wasRunning = false;
|
||||
|
||||
if((m_settings.m_fileName != settings.m_fileName) || force)
|
||||
{
|
||||
m_settings.m_fileName = settings.m_fileName;
|
||||
|
||||
if (m_fileSourceThread != 0)
|
||||
{
|
||||
wasRunning = m_fileSourceThread->isRunning();
|
||||
|
||||
if (wasRunning)
|
||||
{
|
||||
m_fileSourceThread->stopWork();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ifstream.is_open())
|
||||
{
|
||||
m_ifstream.close();
|
||||
}
|
||||
|
||||
openFileStream();
|
||||
|
||||
if (m_fileSourceThread != 0)
|
||||
{
|
||||
m_fileSourceThread->setSamplerate(m_sampleRate);
|
||||
|
||||
if (wasRunning)
|
||||
{
|
||||
m_fileSourceThread->startWork();
|
||||
}
|
||||
}
|
||||
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(m_sampleRate, m_centerFrequency);
|
||||
DSPEngine::instance()->getInputMessageQueue()->push(notif);
|
||||
|
||||
qDebug() << "FileSourceInput::applySettings:"
|
||||
<< " file name: " << settings.m_fileName.toStdString().c_str()
|
||||
<< " center freq: " << m_centerFrequency << " Hz"
|
||||
<< " sample rate: " << m_sampleRate
|
||||
<< " Unix timestamp: " << m_startingTimeStamp;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -206,7 +206,6 @@ private:
|
||||
std::time_t m_startingTimeStamp;
|
||||
const QTimer& m_masterTimer;
|
||||
|
||||
bool applySettings(const Settings& settings, bool force);
|
||||
void openFileStream();
|
||||
};
|
||||
|
||||
|
@ -51,3 +51,5 @@ target_link_libraries(inputhackrf
|
||||
)
|
||||
|
||||
qt5_use_modules(inputhackrf Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS inputhackrf DESTINATION lib/plugins/samplesource)
|
||||
|
@ -50,3 +50,5 @@ target_link_libraries(inputrtlsdr
|
||||
)
|
||||
|
||||
qt5_use_modules(inputrtlsdr Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS inputrtlsdr DESTINATION lib/plugins/samplesource)
|
||||
|
58
plugins/samplesource/sdrdaemon/CMakeLists.txt
Normal file
58
plugins/samplesource/sdrdaemon/CMakeLists.txt
Normal file
@ -0,0 +1,58 @@
|
||||
project(sdrdaemon)
|
||||
|
||||
find_package(LZ4)
|
||||
|
||||
set(sdrdaemon_SOURCES
|
||||
sdrdaemonbuffer.cpp
|
||||
sdrdaemongui.cpp
|
||||
sdrdaemoninput.cpp
|
||||
sdrdaemonplugin.cpp
|
||||
sdrdaemonudphandler.cpp
|
||||
)
|
||||
|
||||
set(sdrdaemon_HEADERS
|
||||
sdrdaemonbuffer.h
|
||||
sdrdaemongui.h
|
||||
sdrdaemoninput.h
|
||||
sdrdaemonplugin.h
|
||||
sdrdaemonudphandler.h
|
||||
)
|
||||
|
||||
set(sdrdaemon_FORMS
|
||||
sdrdaemongui.ui
|
||||
)
|
||||
|
||||
include_directories(
|
||||
.
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/include-gpl
|
||||
)
|
||||
|
||||
#include(${QT_USE_FILE})
|
||||
add_definitions(${QT_DEFINITIONS})
|
||||
add_definitions(-DQT_PLUGIN)
|
||||
add_definitions(-DQT_SHARED)
|
||||
|
||||
#qt4_wrap_cpp(sdrdaemon_HEADERS_MOC ${sdrdaemon_HEADERS})
|
||||
qt5_wrap_ui(sdrdaemon_FORMS_HEADERS ${sdrdaemon_FORMS})
|
||||
|
||||
add_library(inputsdrdaemon SHARED
|
||||
${sdrdaemon_SOURCES}
|
||||
${sdrdaemon_HEADERS_MOC}
|
||||
${sdrdaemon_FORMS_HEADERS}
|
||||
)
|
||||
|
||||
target_include_directories(inputsdrdaemon PUBLIC
|
||||
${LZ4_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(inputsdrdaemon
|
||||
${QT_LIBRARIES}
|
||||
${LZ4_LIBRARIES}
|
||||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(inputsdrdaemon Core Widgets OpenGL Multimedia)
|
||||
|
||||
install(TARGETS inputsdrdaemon DESTINATION lib/plugins/samplesource)
|
357
plugins/samplesource/sdrdaemon/sdrdaemonbuffer.cpp
Normal file
357
plugins/samplesource/sdrdaemon/sdrdaemonbuffer.cpp
Normal file
@ -0,0 +1,357 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 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 //
|
||||
// //
|
||||
// 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 <cassert>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include "sdrdaemonbuffer.h"
|
||||
|
||||
const int SDRdaemonBuffer::m_udpPayloadSize = 512;
|
||||
const int SDRdaemonBuffer::m_sampleSize = 2;
|
||||
const int SDRdaemonBuffer::m_iqSampleSize = 2 * m_sampleSize;
|
||||
|
||||
SDRdaemonBuffer::SDRdaemonBuffer(uint32_t rateDivider) :
|
||||
m_rateDivider(rateDivider),
|
||||
m_sync(false),
|
||||
m_syncLock(false),
|
||||
m_lz4(false),
|
||||
m_inCount(0),
|
||||
m_lz4InBuffer(0),
|
||||
m_lz4InCount(0),
|
||||
m_lz4InSize(0),
|
||||
m_lz4OutBuffer(0),
|
||||
m_frameSize(0),
|
||||
m_nbLz4Decodes(0),
|
||||
m_nbLz4SuccessfulDecodes(0),
|
||||
m_nbLz4CRCOK(0),
|
||||
m_nbLastLz4SuccessfulDecodes(0),
|
||||
m_nbLastLz4CRCOK(0),
|
||||
m_dataCRC(0),
|
||||
m_sampleRateStream(0),
|
||||
m_sampleRate(0),
|
||||
m_sampleBytes(2),
|
||||
m_sampleBits(12),
|
||||
m_writeIndex(0),
|
||||
m_readChunkIndex(0),
|
||||
m_rawSize(0),
|
||||
m_rawBuffer(0),
|
||||
m_chunkSize(0),
|
||||
m_bytesInBlock(0),
|
||||
m_nbBlocks(0),
|
||||
m_readCycles(0),
|
||||
m_lastWriteIndex(0),
|
||||
m_skewRateSum(0.0),
|
||||
m_skewRate(0.0)
|
||||
{
|
||||
m_currentMeta.init();
|
||||
}
|
||||
|
||||
SDRdaemonBuffer::~SDRdaemonBuffer()
|
||||
{
|
||||
if (m_rawBuffer) {
|
||||
delete[] m_rawBuffer;
|
||||
}
|
||||
|
||||
if (m_lz4InBuffer) {
|
||||
delete[] m_lz4InBuffer;
|
||||
}
|
||||
|
||||
if (m_lz4OutBuffer) {
|
||||
delete[] m_lz4OutBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
bool SDRdaemonBuffer::readMeta(char *array, uint32_t length)
|
||||
{
|
||||
assert(length >= sizeof(MetaData) + 8);
|
||||
MetaData *metaData = (MetaData *) array;
|
||||
|
||||
if (m_crc64.calculate_crc((uint8_t *)array, sizeof(MetaData) - 8) == metaData->m_crc)
|
||||
{
|
||||
// sync condition:
|
||||
if (m_currentMeta.m_blockSize > 0)
|
||||
{
|
||||
uint32_t nbBlocks = m_currentMeta.m_nbBytes / m_currentMeta.m_blockSize;
|
||||
m_syncLock = nbBlocks + (m_lz4 ? 2 : 1) == m_nbBlocks;
|
||||
//qDebug("SDRdaemonBuffer::readMeta: m_nbBlocks: %d:%d %s", nbBlocks, m_nbBlocks, (m_syncLock ? "locked" : "unlocked"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_syncLock = false;
|
||||
}
|
||||
|
||||
memcpy((void *) &m_dataCRC, (const void *) &array[sizeof(MetaData)], 8);
|
||||
m_nbBlocks = 0;
|
||||
m_inCount = 0;
|
||||
|
||||
if (!m_lz4 && !(m_currentMeta == *metaData))
|
||||
{
|
||||
std::cerr << "SDRdaemonBuffer::readMeta: ";
|
||||
printMeta(metaData);
|
||||
}
|
||||
|
||||
m_currentMeta = *metaData;
|
||||
|
||||
// sanity checks
|
||||
if (metaData->m_blockSize == m_udpPayloadSize) // sent blocksize matches given blocksize
|
||||
{
|
||||
m_sampleBytes = metaData->m_sampleBytes & 0x0F;
|
||||
uint32_t frameSize = m_iqSampleSize * metaData->m_nbSamples * metaData->m_nbBlocks;
|
||||
int sampleRate = metaData->m_sampleRate;
|
||||
|
||||
if (sampleRate != m_sampleRateStream)
|
||||
{
|
||||
m_sampleRateStream = sampleRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
sampleRate = m_sampleRate;
|
||||
}
|
||||
|
||||
sampleRate += sampleRate * m_skewRate;
|
||||
sampleRate = (sampleRate / m_rateDivider) * m_rateDivider;
|
||||
|
||||
if (metaData->m_sampleBytes & 0x10)
|
||||
{
|
||||
m_lz4 = true;
|
||||
m_lz4InSize = metaData->m_nbBytes; // compressed input size
|
||||
m_lz4InCount = 0;
|
||||
|
||||
if (frameSize != m_frameSize)
|
||||
{
|
||||
updateLZ4Sizes(frameSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lz4 = false;
|
||||
}
|
||||
|
||||
if (sampleRate != m_sampleRate)
|
||||
{
|
||||
updateBufferSize(sampleRate);
|
||||
}
|
||||
|
||||
m_sampleRate = sampleRate;
|
||||
m_frameSize = frameSize;
|
||||
m_sync = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sync = false;
|
||||
}
|
||||
|
||||
return m_sync;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonBuffer::writeData(char *array, uint32_t length)
|
||||
{
|
||||
if ((m_sync) && (m_nbBlocks > 0))
|
||||
{
|
||||
if (m_lz4)
|
||||
{
|
||||
writeDataLZ4(array, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeToRawBufferUncompressed(array, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonBuffer::writeDataLZ4(const char *array, uint32_t length)
|
||||
{
|
||||
if (m_lz4InCount + length < m_lz4InSize)
|
||||
{
|
||||
std::memcpy((void *) &m_lz4InBuffer[m_lz4InCount], (const void *) array, length);
|
||||
m_lz4InCount += length;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy((void *) &m_lz4InBuffer[m_lz4InCount], (const void *) array, m_lz4InSize - m_lz4InCount); // copy rest of data in compressed Buffer
|
||||
m_lz4InCount += length;
|
||||
}
|
||||
|
||||
if (m_lz4InCount >= m_lz4InSize) // full input compressed block retrieved
|
||||
{
|
||||
if (m_nbLz4Decodes == 100)
|
||||
{
|
||||
std::cerr << "SDRdaemonBuffer::writeAndReadLZ4:"
|
||||
<< " decoding: " << m_nbLz4CRCOK
|
||||
<< ":" << m_nbLz4SuccessfulDecodes
|
||||
<< "/" << m_nbLz4Decodes
|
||||
<< std::endl;
|
||||
|
||||
m_nbLastLz4SuccessfulDecodes = m_nbLz4SuccessfulDecodes;
|
||||
m_nbLastLz4CRCOK = m_nbLz4CRCOK;
|
||||
m_nbLz4Decodes = 0;
|
||||
m_nbLz4SuccessfulDecodes = 0;
|
||||
m_nbLz4CRCOK = 0;
|
||||
}
|
||||
|
||||
writeToRawBufferLZ4();
|
||||
m_lz4InCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonBuffer::writeToRawBufferUncompressed(const char *array, uint32_t length)
|
||||
{
|
||||
// TODO: handle the 1 byte per I or Q sample
|
||||
if (m_writeIndex + length < m_rawSize)
|
||||
{
|
||||
std::memcpy((void *) &m_rawBuffer[m_writeIndex], (const void *) array, length);
|
||||
m_writeIndex += length;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy((void *) &m_rawBuffer[m_writeIndex], (const void *) array, m_rawSize - m_writeIndex);
|
||||
length -= m_rawSize - m_writeIndex;
|
||||
std::memcpy((void *) m_rawBuffer, (const void *) &array[m_rawSize - m_writeIndex], length);
|
||||
m_writeIndex = length;
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonBuffer::writeToRawBufferLZ4()
|
||||
{
|
||||
uint64_t crc64 = m_crc64.calculate_crc(m_lz4InBuffer, m_lz4InSize);
|
||||
|
||||
if (memcmp(&crc64, &m_dataCRC, 8) == 0)
|
||||
{
|
||||
m_nbLz4CRCOK++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int compressedSize = LZ4_decompress_fast((const char*) m_lz4InBuffer, (char*) m_lz4OutBuffer, m_frameSize);
|
||||
m_nbLz4Decodes++;
|
||||
|
||||
if (compressedSize == m_lz4InSize)
|
||||
{
|
||||
m_nbLz4SuccessfulDecodes++;
|
||||
writeToRawBufferUncompressed((const char *) m_lz4OutBuffer, m_frameSize);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *SDRdaemonBuffer::readDataChunk()
|
||||
{
|
||||
// relies on the fact that we always have an integer number of chunks in the raw buffer
|
||||
if (m_readChunkIndex == m_rateDivider * 2) // go back to start of raw buffer
|
||||
{
|
||||
double oneCycleSkew = 0;
|
||||
|
||||
if (m_readCycles > 0)
|
||||
{
|
||||
oneCycleSkew = (double) ((int) m_writeIndex - (int) m_lastWriteIndex) / (double) m_rawSize;
|
||||
m_skewRateSum += oneCycleSkew;
|
||||
}
|
||||
|
||||
//qDebug("SDRdaemonBuffer::readDataChunk: %d / %d (%lf)", m_writeIndex, m_rawSize, oneCycleSkew);
|
||||
|
||||
if (m_readCycles && ((m_writeIndex < m_rawSize / 10) || (m_rawSize - m_writeIndex < m_rawSize / 10)))
|
||||
{
|
||||
m_skewRate = m_skewRateSum / m_readCycles;
|
||||
if (m_skewRate > 0.2) {
|
||||
m_skewRate = 0.2;
|
||||
} else if (m_skewRate < -0.2) {
|
||||
m_skewRate = -0.2;
|
||||
}
|
||||
qDebug("SDRdaemonBuffer::readDataChunk: m_skewRate: %lf", m_skewRate);
|
||||
}
|
||||
|
||||
m_readChunkIndex = 0; // go to start
|
||||
m_lastWriteIndex = m_writeIndex;
|
||||
m_readCycles++;
|
||||
}
|
||||
|
||||
uint32_t readIndex = m_readChunkIndex;
|
||||
m_readChunkIndex++;
|
||||
return &m_rawBuffer[readIndex * m_chunkSize];
|
||||
}
|
||||
|
||||
void SDRdaemonBuffer::updateLZ4Sizes(uint32_t frameSize)
|
||||
{
|
||||
uint32_t maxInputSize = LZ4_compressBound(frameSize);
|
||||
|
||||
if (m_lz4InBuffer) {
|
||||
delete[] m_lz4InBuffer;
|
||||
}
|
||||
|
||||
m_lz4InBuffer = new uint8_t[maxInputSize];
|
||||
|
||||
if (m_lz4OutBuffer) {
|
||||
delete[] m_lz4OutBuffer;
|
||||
}
|
||||
|
||||
m_lz4OutBuffer = new uint8_t[frameSize];
|
||||
}
|
||||
|
||||
void SDRdaemonBuffer::updateBufferSize(uint32_t sampleRate)
|
||||
{
|
||||
assert(sampleRate % m_rateDivider == 0); // make sure we get an integer number of samples in a chunk
|
||||
|
||||
// Store 2 seconds long of samples so we have two one second long half buffers
|
||||
m_chunkSize = (sampleRate * m_iqSampleSize) / m_rateDivider;
|
||||
m_rawSize = m_chunkSize * m_rateDivider * 2;
|
||||
|
||||
if (m_rawBuffer) {
|
||||
delete[] m_rawBuffer;
|
||||
}
|
||||
|
||||
m_rawBuffer = new uint8_t[m_rawSize];
|
||||
|
||||
m_writeIndex = 0;
|
||||
m_readChunkIndex = m_rateDivider;
|
||||
m_readCycles = 0;
|
||||
m_skewRateSum = 0;
|
||||
m_skewRate = 0;
|
||||
|
||||
std::cerr << "SDRdaemonBuffer::updateBufferSize:"
|
||||
<< " sampleRate: " << sampleRate
|
||||
<< " m_chunkSize: " << m_chunkSize
|
||||
<< " m_rawSize: " << m_rawSize
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void SDRdaemonBuffer::updateBlockCounts(uint32_t nbBytesReceived)
|
||||
{
|
||||
m_nbBlocks += m_bytesInBlock + nbBytesReceived > m_udpPayloadSize ? 1 : 0;
|
||||
m_bytesInBlock = m_bytesInBlock + nbBytesReceived > m_udpPayloadSize ? nbBytesReceived : m_bytesInBlock + nbBytesReceived;
|
||||
}
|
||||
|
||||
void SDRdaemonBuffer::printMeta(MetaData *metaData)
|
||||
{
|
||||
std::cerr
|
||||
<< "|" << metaData->m_centerFrequency
|
||||
<< ":" << metaData->m_sampleRate
|
||||
<< ":" << (int) (metaData->m_sampleBytes & 0xF)
|
||||
<< ":" << (int) metaData->m_sampleBits
|
||||
<< ":" << metaData->m_blockSize
|
||||
<< ":" << metaData->m_nbSamples
|
||||
<< "||" << metaData->m_nbBlocks
|
||||
<< ":" << metaData->m_nbBytes
|
||||
<< "|" << metaData->m_tv_sec
|
||||
<< ":" << metaData->m_tv_usec
|
||||
<< std::endl;
|
||||
}
|
||||
|
134
plugins/samplesource/sdrdaemon/sdrdaemonbuffer.h
Normal file
134
plugins/samplesource/sdrdaemon/sdrdaemonbuffer.h
Normal file
@ -0,0 +1,134 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 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 //
|
||||
// //
|
||||
// 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 PLUGINS_SAMPLESOURCE_SDRDAEMON_SDRDAEMONBUFFER_H_
|
||||
#define PLUGINS_SAMPLESOURCE_SDRDAEMON_SDRDAEMONBUFFER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <lz4.h>
|
||||
#include "util/CRC64.h"
|
||||
#include "dsp/samplefifo.h"
|
||||
|
||||
class SDRdaemonBuffer
|
||||
{
|
||||
public:
|
||||
#pragma pack(push, 1)
|
||||
struct MetaData
|
||||
{
|
||||
// critical data
|
||||
uint32_t m_centerFrequency; //!< center frequency in kHz
|
||||
uint32_t m_sampleRate; //!< sample rate in Hz
|
||||
uint8_t m_sampleBytes; //!< MSB(4): indicators, LSB(4) number of bytes per sample
|
||||
uint8_t m_sampleBits; //!< number of effective bits per sample
|
||||
uint16_t m_blockSize; //!< payload size
|
||||
uint32_t m_nbSamples; //!< number of samples in a hardware block
|
||||
// end of critical data
|
||||
uint16_t m_nbBlocks; //!< number of hardware blocks in the frame
|
||||
uint32_t m_nbBytes; //!< total number of bytes in the frame
|
||||
uint32_t m_tv_sec; //!< seconds of timestamp at start time of frame processing
|
||||
uint32_t m_tv_usec; //!< microseconds of timestamp at start time of frame processing
|
||||
uint64_t m_crc; //!< 64 bit CRC of the above
|
||||
|
||||
bool operator==(const MetaData& rhs)
|
||||
{
|
||||
return (memcmp((const void *) this, (const void *) &rhs, 20) == 0); // Only the 20 first bytes are relevant (critical)
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
memset((void *) this, 0, sizeof(MetaData));
|
||||
}
|
||||
|
||||
void operator=(const MetaData& rhs)
|
||||
{
|
||||
memcpy((void *) this, (const void *) &rhs, sizeof(MetaData));
|
||||
}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
SDRdaemonBuffer(uint32_t rateDivider);
|
||||
~SDRdaemonBuffer();
|
||||
bool readMeta(char *array, uint32_t length); //!< Attempt to read meta. Returns true if meta block
|
||||
void writeData(char *array, uint32_t length); //!< Write data into buffer.
|
||||
uint8_t *readDataChunk(); //!< Read a chunk of data from buffer
|
||||
const MetaData& getCurrentMeta() const { return m_currentMeta; }
|
||||
uint32_t getSampleRateStream() const { return m_sampleRateStream; }
|
||||
uint32_t getSampleRate() const { return m_sampleRate; }
|
||||
void updateBlockCounts(uint32_t nbBytesReceived);
|
||||
bool isSync() const { return m_sync; }
|
||||
bool isSyncLocked() const { return m_syncLock; }
|
||||
uint32_t getFrameSize() const { return m_frameSize; }
|
||||
bool isLz4Compressed() const { return m_lz4; }
|
||||
float getCompressionRatio() const { return (m_frameSize > 0 ? (float) m_lz4InSize / (float) m_frameSize : 1.0); }
|
||||
uint32_t getLz4DataCRCOK() const { return m_nbLastLz4CRCOK; }
|
||||
uint32_t getLz4SuccessfulDecodes() const { return m_nbLastLz4SuccessfulDecodes; }
|
||||
|
||||
static const int m_udpPayloadSize;
|
||||
static const int m_sampleSize;
|
||||
static const int m_iqSampleSize;
|
||||
|
||||
private:
|
||||
void updateLZ4Sizes(uint32_t frameSize);
|
||||
void writeDataLZ4(const char *array, uint32_t length);
|
||||
void writeToRawBufferLZ4();
|
||||
void writeToRawBufferUncompressed(const char *array, uint32_t length);
|
||||
void updateBufferSize(uint32_t sampleRate);
|
||||
void printMeta(MetaData *metaData);
|
||||
|
||||
uint32_t m_rateDivider; //!< Number of times per seconds the samples are fetched
|
||||
bool m_sync; //!< Meta data acquired
|
||||
bool m_syncLock; //!< Meta data expected (Stream synchronized)
|
||||
bool m_lz4; //!< Stream is compressed with LZ4
|
||||
MetaData m_currentMeta; //!< Stored current meta data
|
||||
CRC64 m_crc64; //!< CRC64 calculator
|
||||
|
||||
uint32_t m_inCount; //!< Current position of uncompressed input
|
||||
uint8_t *m_lz4InBuffer; //!< Buffer for LZ4 compressed input
|
||||
uint32_t m_lz4InCount; //!< Current position in LZ4 input buffer
|
||||
uint32_t m_lz4InSize; //!< Size in bytes of the LZ4 input data
|
||||
uint8_t *m_lz4OutBuffer; //!< Buffer for LZ4 uncompressed output
|
||||
uint32_t m_frameSize; //!< Size in bytes of one uncompressed frame
|
||||
uint32_t m_nbLz4Decodes;
|
||||
uint32_t m_nbLz4SuccessfulDecodes;
|
||||
uint32_t m_nbLz4CRCOK;
|
||||
uint32_t m_nbLastLz4SuccessfulDecodes;
|
||||
uint32_t m_nbLastLz4CRCOK;
|
||||
uint64_t m_dataCRC;
|
||||
|
||||
uint32_t m_sampleRateStream; //!< Current sample rate from the stream
|
||||
uint32_t m_sampleRate; //!< Current actual sample rate in Hz
|
||||
uint8_t m_sampleBytes; //!< Current number of bytes per I or Q sample
|
||||
uint8_t m_sampleBits; //!< Current number of effective bits per sample
|
||||
|
||||
uint32_t m_writeIndex; //!< Current write position in the raw samples buffer
|
||||
uint32_t m_readChunkIndex; //!< Current read chunk index in the raw samples buffer
|
||||
uint32_t m_rawSize; //!< Size of the raw samples buffer in bytes
|
||||
uint8_t *m_rawBuffer; //!< Buffer for raw samples obtained from UDP (I/Q not in a formal I/Q structure)
|
||||
uint32_t m_chunkSize; //!< Size of a chunk of samples in bytes
|
||||
uint32_t m_bytesInBlock; //!< Number of bytes received in the current UDP block
|
||||
uint32_t m_nbBlocks; //!< Number of UDP blocks received in the current frame
|
||||
|
||||
uint32_t m_readCycles; //!< Count of read cycles over raw buiffer
|
||||
uint32_t m_lastWriteIndex; //!< Write index at last skew estimation
|
||||
double m_skewRateSum;
|
||||
double m_skewRate;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* PLUGINS_SAMPLESOURCE_SDRDAEMON_SDRDAEMONBUFFER_H_ */
|
358
plugins/samplesource/sdrdaemon/sdrdaemongui.cpp
Normal file
358
plugins/samplesource/sdrdaemon/sdrdaemongui.cpp
Normal file
@ -0,0 +1,358 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015 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 //
|
||||
// //
|
||||
// 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 <QFileDialog>
|
||||
#include <stdint.h>
|
||||
#include "ui_sdrdaemongui.h"
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "gui/colormapper.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "mainwindow.h"
|
||||
#include "util/simpleserializer.h"
|
||||
|
||||
#include "sdrdaemongui.h"
|
||||
|
||||
SDRdaemonGui::SDRdaemonGui(PluginAPI* pluginAPI, QWidget* parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::SDRdaemonGui),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_sampleSource(NULL),
|
||||
m_acquisition(false),
|
||||
m_sampleRate(0),
|
||||
m_sampleRateStream(0),
|
||||
m_centerFrequency(0),
|
||||
m_syncLocked(false),
|
||||
m_frameSize(0),
|
||||
m_lz4(false),
|
||||
m_compressionRatio(1.0),
|
||||
m_nbLz4DataCRCOK(0),
|
||||
m_nbLz4SuccessfulDecodes(0),
|
||||
m_samplesCount(0),
|
||||
m_tickCount(0),
|
||||
m_address("127.0.0.1"),
|
||||
m_port(9090),
|
||||
m_dcBlock(false),
|
||||
m_iqCorrection(false)
|
||||
{
|
||||
m_startingTimeStamp.tv_sec = 0;
|
||||
m_startingTimeStamp.tv_usec = 0;
|
||||
ui->setupUi(this);
|
||||
ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::ReverseGold));
|
||||
ui->centerFrequency->setValueRange(7, 0, pow(10,7));
|
||||
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
|
||||
connect(&(m_pluginAPI->getMainWindow()->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick()));
|
||||
|
||||
m_sampleSource = new SDRdaemonInput(m_pluginAPI->getMainWindow()->getMasterTimer());
|
||||
connect(m_sampleSource->getOutputMessageQueueToGUI(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages()));
|
||||
DSPEngine::instance()->setSource(m_sampleSource);
|
||||
|
||||
displaySettings();
|
||||
}
|
||||
|
||||
SDRdaemonGui::~SDRdaemonGui()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void SDRdaemonGui::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void SDRdaemonGui::setName(const QString& name)
|
||||
{
|
||||
setObjectName(name);
|
||||
}
|
||||
|
||||
QString SDRdaemonGui::getName() const
|
||||
{
|
||||
return objectName();
|
||||
}
|
||||
|
||||
void SDRdaemonGui::resetToDefaults()
|
||||
{
|
||||
m_address = "127.0.0.1";
|
||||
m_port = 9090;
|
||||
m_dcBlock = false;
|
||||
m_iqCorrection = false;
|
||||
displaySettings();
|
||||
}
|
||||
|
||||
QByteArray SDRdaemonGui::serialize() const
|
||||
{
|
||||
bool ok;
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeString(1, ui->address->text());
|
||||
uint32_t uintval = ui->port->text().toInt(&ok);
|
||||
|
||||
if((!ok) || (uintval < 1024) || (uintval > 65535)) {
|
||||
uintval = 9090;
|
||||
}
|
||||
|
||||
s.writeU32(2, uintval);
|
||||
s.writeBool(3, m_dcBlock);
|
||||
s.writeBool(4, m_iqCorrection);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool SDRdaemonGui::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
QString address;
|
||||
uint32_t uintval;
|
||||
quint16 port;
|
||||
bool dcBlock;
|
||||
bool iqCorrection;
|
||||
|
||||
if (!d.isValid())
|
||||
{
|
||||
resetToDefaults();
|
||||
displaySettings();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d.getVersion() == 1)
|
||||
{
|
||||
uint32_t uintval;
|
||||
d.readString(1, &address, "127.0.0.1");
|
||||
d.readU32(2, &uintval, 9090);
|
||||
|
||||
if ((uintval > 1024) && (uintval < 65536)) {
|
||||
port = uintval;
|
||||
} else {
|
||||
port = 9090;
|
||||
}
|
||||
|
||||
d.readBool(3, &dcBlock, false);
|
||||
d.readBool(4, &iqCorrection, false);
|
||||
|
||||
if ((address != m_address) || (port != m_port))
|
||||
{
|
||||
m_address = address;
|
||||
m_port = port;
|
||||
configureUDPLink();
|
||||
}
|
||||
|
||||
if ((dcBlock != m_dcBlock) || (iqCorrection != m_iqCorrection))
|
||||
{
|
||||
m_dcBlock = dcBlock;
|
||||
m_iqCorrection = iqCorrection;
|
||||
configureAutoCorrections();
|
||||
}
|
||||
|
||||
displaySettings();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
displaySettings();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
qint64 SDRdaemonGui::getCenterFrequency() const
|
||||
{
|
||||
return m_centerFrequency;
|
||||
}
|
||||
|
||||
void SDRdaemonGui::setCenterFrequency(qint64 centerFrequency)
|
||||
{
|
||||
m_centerFrequency = centerFrequency;
|
||||
displaySettings();
|
||||
}
|
||||
|
||||
bool SDRdaemonGui::handleMessage(const Message& message)
|
||||
{
|
||||
if (SDRdaemonInput::MsgReportSDRdaemonAcquisition::match(message))
|
||||
{
|
||||
m_acquisition = ((SDRdaemonInput::MsgReportSDRdaemonAcquisition&)message).getAcquisition();
|
||||
updateWithAcquisition();
|
||||
return true;
|
||||
}
|
||||
else if (SDRdaemonInput::MsgReportSDRdaemonStreamData::match(message))
|
||||
{
|
||||
m_sampleRateStream = ((SDRdaemonInput::MsgReportSDRdaemonStreamData&)message).getSampleRateStream();
|
||||
m_sampleRate = ((SDRdaemonInput::MsgReportSDRdaemonStreamData&)message).getSampleRate();
|
||||
m_centerFrequency = ((SDRdaemonInput::MsgReportSDRdaemonStreamData&)message).getCenterFrequency();
|
||||
m_startingTimeStamp.tv_sec = ((SDRdaemonInput::MsgReportSDRdaemonStreamData&)message).get_tv_sec();
|
||||
m_startingTimeStamp.tv_usec = ((SDRdaemonInput::MsgReportSDRdaemonStreamData&)message).get_tv_usec();
|
||||
updateWithStreamData();
|
||||
return true;
|
||||
}
|
||||
else if (SDRdaemonInput::MsgReportSDRdaemonStreamTiming::match(message))
|
||||
{
|
||||
m_startingTimeStamp.tv_sec = ((SDRdaemonInput::MsgReportSDRdaemonStreamTiming&)message).get_tv_sec();
|
||||
m_startingTimeStamp.tv_usec = ((SDRdaemonInput::MsgReportSDRdaemonStreamTiming&)message).get_tv_usec();
|
||||
m_syncLocked = ((SDRdaemonInput::MsgReportSDRdaemonStreamTiming&)message).getSyncLock();
|
||||
m_frameSize = ((SDRdaemonInput::MsgReportSDRdaemonStreamTiming&)message).getFrameSize();
|
||||
m_lz4 = ((SDRdaemonInput::MsgReportSDRdaemonStreamTiming&)message).getLz4Compression();
|
||||
|
||||
if (m_lz4) {
|
||||
m_compressionRatio = ((SDRdaemonInput::MsgReportSDRdaemonStreamTiming&)message).getLz4CompressionRatio();
|
||||
} else {
|
||||
m_compressionRatio = 1.0;
|
||||
}
|
||||
|
||||
m_nbLz4DataCRCOK = ((SDRdaemonInput::MsgReportSDRdaemonStreamTiming&)message).getLz4DataCRCOK();
|
||||
m_nbLz4SuccessfulDecodes = ((SDRdaemonInput::MsgReportSDRdaemonStreamTiming&)message).getLz4SuccessfulDecodes();
|
||||
|
||||
updateWithStreamTime();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonGui::handleSourceMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = m_sampleSource->getOutputMessageQueueToGUI()->pop()) != 0)
|
||||
{
|
||||
//qDebug("SDRdaemonGui::handleSourceMessages: message: %s", message->getIdentifier());
|
||||
|
||||
if (handleMessage(*message))
|
||||
{
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonGui::displaySettings()
|
||||
{
|
||||
ui->address->setText(m_address);
|
||||
ui->port->setText(QString::number(m_port));
|
||||
ui->dcOffset->setChecked(m_dcBlock);
|
||||
ui->iqImbalance->setChecked(m_iqCorrection);
|
||||
}
|
||||
|
||||
void SDRdaemonGui::on_applyButton_clicked(bool checked)
|
||||
{
|
||||
bool ok;
|
||||
QString udpAddress = ui->address->text();
|
||||
int udpPort = ui->port->text().toInt(&ok);
|
||||
|
||||
if((!ok) || (udpPort < 1024) || (udpPort > 65535))
|
||||
{
|
||||
udpPort = 9090;
|
||||
}
|
||||
|
||||
m_address = udpAddress;
|
||||
m_port = udpPort;
|
||||
|
||||
configureUDPLink();
|
||||
}
|
||||
|
||||
void SDRdaemonGui::on_dcOffset_toggled(bool checked)
|
||||
{
|
||||
if (m_dcBlock != checked)
|
||||
{
|
||||
m_dcBlock = checked;
|
||||
configureAutoCorrections();
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonGui::on_iqImbalance_toggled(bool checked)
|
||||
{
|
||||
if (m_iqCorrection != checked)
|
||||
{
|
||||
m_iqCorrection = checked;
|
||||
configureAutoCorrections();
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonGui::configureUDPLink()
|
||||
{
|
||||
qDebug() << "SDRdaemonGui::configureUDPLink: " << m_address.toStdString().c_str()
|
||||
<< " : " << m_port;
|
||||
|
||||
SDRdaemonInput::MsgConfigureSDRdaemonUDPLink* message = SDRdaemonInput::MsgConfigureSDRdaemonUDPLink::create(m_address, m_port);
|
||||
m_sampleSource->getInputMessageQueue()->push(message);
|
||||
}
|
||||
|
||||
void SDRdaemonGui::configureAutoCorrections()
|
||||
{
|
||||
SDRdaemonInput::MsgConfigureSDRdaemonAutoCorr* message = SDRdaemonInput::MsgConfigureSDRdaemonAutoCorr::create(m_dcBlock, m_iqCorrection);
|
||||
m_sampleSource->getInputMessageQueue()->push(message);
|
||||
}
|
||||
|
||||
void SDRdaemonGui::updateWithAcquisition()
|
||||
{
|
||||
}
|
||||
|
||||
void SDRdaemonGui::updateWithStreamData()
|
||||
{
|
||||
ui->centerFrequency->setValue(m_centerFrequency / 1000);
|
||||
QString s0 = QString::number(m_sampleRateStream/1000.0, 'f', 2);
|
||||
ui->sampleRateStreamText->setText(tr("%1").arg(s0));
|
||||
QString s1 = QString::number(m_sampleRate/1000.0, 'f', 3);
|
||||
ui->sampleRateText->setText(tr("%1").arg(s1));
|
||||
float skewPerCent = (float) ((m_sampleRate - m_sampleRateStream) * 100) / (float) m_sampleRateStream;
|
||||
QString s2 = QString::number(skewPerCent, 'f', 2);
|
||||
ui->skewRateText->setText(tr("%1").arg(s2));
|
||||
updateWithStreamTime();
|
||||
}
|
||||
|
||||
void SDRdaemonGui::updateWithStreamTime()
|
||||
{
|
||||
quint64 startingTimeStampMsec = (m_startingTimeStamp.tv_sec * 1000) + (m_startingTimeStamp.tv_usec / 1000);
|
||||
QDateTime dt = QDateTime::fromMSecsSinceEpoch(startingTimeStampMsec);
|
||||
QString s_date = dt.toString("yyyy-MM-dd hh:mm:ss.zzz");
|
||||
ui->absTimeText->setText(s_date);
|
||||
|
||||
if (m_syncLocked) {
|
||||
ui->streamLocked->setStyleSheet("QToolButton { background-color : green; }");
|
||||
} else {
|
||||
ui->streamLocked->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
|
||||
}
|
||||
|
||||
QString s = QString::number(m_frameSize / 1024.0, 'f', 0);
|
||||
ui->frameSizeText->setText(tr("%1").arg(s));
|
||||
|
||||
if (m_lz4) {
|
||||
ui->lz4Compressed->setStyleSheet("QToolButton { background-color : green; }");
|
||||
} else {
|
||||
ui->lz4Compressed->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
|
||||
}
|
||||
|
||||
s = QString::number(m_compressionRatio, 'f', 2);
|
||||
ui->compressionRatioText->setText(tr("%1").arg(s));
|
||||
|
||||
s = QString::number(m_nbLz4DataCRCOK, 'f', 0);
|
||||
ui->dataCRCOKText->setText(tr("%1").arg(s));
|
||||
|
||||
s = QString::number(m_nbLz4SuccessfulDecodes, 'f', 0);
|
||||
ui->lz4DecodesOKText->setText(tr("%1").arg(s));
|
||||
}
|
||||
|
||||
void SDRdaemonGui::tick()
|
||||
{
|
||||
if ((++m_tickCount & 0xf) == 0) {
|
||||
SDRdaemonInput::MsgConfigureSDRdaemonStreamTiming* message = SDRdaemonInput::MsgConfigureSDRdaemonStreamTiming::create();
|
||||
m_sampleSource->getInputMessageQueue()->push(message);
|
||||
}
|
||||
}
|
||||
|
93
plugins/samplesource/sdrdaemon/sdrdaemongui.h
Normal file
93
plugins/samplesource/sdrdaemon/sdrdaemongui.h
Normal file
@ -0,0 +1,93 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 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 //
|
||||
// //
|
||||
// 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 INCLUDE_SDRDAEMONGUI_H
|
||||
#define INCLUDE_SDRDAEMONGUI_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <sys/time.h>
|
||||
#include "plugin/plugingui.h"
|
||||
|
||||
#include "sdrdaemoninput.h"
|
||||
|
||||
class PluginAPI;
|
||||
|
||||
namespace Ui {
|
||||
class SDRdaemonGui;
|
||||
}
|
||||
|
||||
class SDRdaemonGui : public QWidget, public PluginGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SDRdaemonGui(PluginAPI* pluginAPI, QWidget* parent = NULL);
|
||||
virtual ~SDRdaemonGui();
|
||||
void destroy();
|
||||
|
||||
void setName(const QString& name);
|
||||
QString getName() const;
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
virtual qint64 getCenterFrequency() const;
|
||||
virtual void setCenterFrequency(qint64 centerFrequency);
|
||||
virtual bool handleMessage(const Message& message);
|
||||
|
||||
private:
|
||||
Ui::SDRdaemonGui* ui;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
QTimer m_updateTimer;
|
||||
SampleSource* m_sampleSource;
|
||||
bool m_acquisition;
|
||||
|
||||
int m_sampleRate;
|
||||
int m_sampleRateStream;
|
||||
quint64 m_centerFrequency;
|
||||
struct timeval m_startingTimeStamp;
|
||||
bool m_syncLocked;
|
||||
uint32_t m_frameSize;
|
||||
bool m_lz4;
|
||||
float m_compressionRatio;
|
||||
uint32_t m_nbLz4DataCRCOK;
|
||||
uint32_t m_nbLz4SuccessfulDecodes;
|
||||
|
||||
int m_samplesCount;
|
||||
std::size_t m_tickCount;
|
||||
|
||||
QString m_address;
|
||||
quint16 m_port;
|
||||
bool m_dcBlock;
|
||||
bool m_iqCorrection;
|
||||
|
||||
void displaySettings();
|
||||
void displayTime();
|
||||
void configureUDPLink();
|
||||
void configureAutoCorrections();
|
||||
void updateWithAcquisition();
|
||||
void updateWithStreamData();
|
||||
void updateWithStreamTime();
|
||||
|
||||
private slots:
|
||||
void handleSourceMessages();
|
||||
void on_applyButton_clicked(bool checked);
|
||||
void on_dcOffset_toggled(bool checked);
|
||||
void on_iqImbalance_toggled(bool checked);
|
||||
void tick();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_SDRDAEMONGUI_H
|
540
plugins/samplesource/sdrdaemon/sdrdaemongui.ui
Normal file
540
plugins/samplesource/sdrdaemon/sdrdaemongui.ui
Normal file
@ -0,0 +1,540 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SDRdaemonGui</class>
|
||||
<widget class="QWidget" name="SDRdaemonGui">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>340</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Sans Serif</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>SDRdaemon</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_freq">
|
||||
<item>
|
||||
<spacer name="freqLeftSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ValueDial" name="centerFrequency" native="true">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>SizeVerCursor</cursorShape>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Record center frequency in kHz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="freqUnits">
|
||||
<property name="text">
|
||||
<string> kHz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="freqRightlSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_address">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="gridLayout_corr">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Auto Corr</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="dcOffset">
|
||||
<property name="toolTip">
|
||||
<string>DC Offset auto correction</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>DC</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="iqImbalance">
|
||||
<property name="toolTip">
|
||||
<string>IQ Imbalance auto correction</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>IQ</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="absTimeText">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Stream timestamp</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>2015-01-01 00:00:00.000</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_freq_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="streamLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="streamLocked">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Stream locked status i.e. synced with meta data</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrbase/resources/res.qrc">
|
||||
<normaloff>:/locked.png</normaloff>:/locked.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="frameSizeText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Frame size in kB</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0000</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineStream1">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="sampleRateStreamText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Nominal sample rate from stream data (kS/s)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0000.00</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineStream2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="sampleRateText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Actual sample rate (kS/s)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0000.000</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineStream3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="skewRateText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Sample rate skew from stream nominal rate (%)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-00.00</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineStream4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_rateTime">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="lz4Layout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="lz4Compressed">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Stream is compressed with LZ4</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrbase/resources/res.qrc">
|
||||
<normaloff>:/compressed.png</normaloff>:/compressed.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="compressionRatioText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Stream comopression ratio (compressed / original)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0.00</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineLz41">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="dataCRCOKText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Data CRC OK (%)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineLz42">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lz4DecodesOKText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>LZ4 successful decodes (%)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineLz43">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_freq">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="addressLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="addressLabel">
|
||||
<property name="text">
|
||||
<string>Addr:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="address">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Data connection IP address</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>127.0.0.1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="portLabel">
|
||||
<property name="text">
|
||||
<string>Port:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="port">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Data connection port</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>9090</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="applyButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="iaddressSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ValueDial</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/valuedial.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ButtonSwitch</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>gui/buttonswitch.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
138
plugins/samplesource/sdrdaemon/sdrdaemoninput.cpp
Normal file
138
plugins/samplesource/sdrdaemon/sdrdaemoninput.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015 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 //
|
||||
// //
|
||||
// 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 "util/simpleserializer.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/filesink.h"
|
||||
|
||||
#include "sdrdaemongui.h"
|
||||
#include "sdrdaemoninput.h"
|
||||
#include "sdrdaemonudphandler.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(SDRdaemonInput::MsgConfigureSDRdaemonUDPLink, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SDRdaemonInput::MsgConfigureSDRdaemonAutoCorr, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SDRdaemonInput::MsgConfigureSDRdaemonWork, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SDRdaemonInput::MsgConfigureSDRdaemonStreamTiming, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SDRdaemonInput::MsgReportSDRdaemonAcquisition, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SDRdaemonInput::MsgReportSDRdaemonStreamData, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SDRdaemonInput::MsgReportSDRdaemonStreamTiming, Message)
|
||||
|
||||
SDRdaemonInput::SDRdaemonInput(const QTimer& masterTimer) :
|
||||
m_address("127.0.0.1"),
|
||||
m_port(9090),
|
||||
m_SDRdaemonUDPHandler(0),
|
||||
m_deviceDescription(),
|
||||
m_sampleRate(0),
|
||||
m_centerFrequency(0),
|
||||
m_startingTimeStamp(0),
|
||||
m_masterTimer(masterTimer)
|
||||
{
|
||||
m_sampleFifo.setSize(96000 * 4);
|
||||
m_SDRdaemonUDPHandler = new SDRdaemonUDPHandler(&m_sampleFifo, getOutputMessageQueueToGUI());
|
||||
m_SDRdaemonUDPHandler->connectTimer(&m_masterTimer);
|
||||
}
|
||||
|
||||
SDRdaemonInput::~SDRdaemonInput()
|
||||
{
|
||||
stop();
|
||||
delete m_SDRdaemonUDPHandler;
|
||||
}
|
||||
|
||||
bool SDRdaemonInput::init(const Message& message)
|
||||
{
|
||||
qDebug() << "SDRdaemonInput::init";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDRdaemonInput::start(int device)
|
||||
{
|
||||
qDebug() << "SDRdaemonInput::start";
|
||||
MsgConfigureSDRdaemonWork *command = MsgConfigureSDRdaemonWork::create(true);
|
||||
getInputMessageQueue()->push(command);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDRdaemonInput::stop()
|
||||
{
|
||||
qDebug() << "SDRdaemonInput::stop";
|
||||
MsgConfigureSDRdaemonWork *command = MsgConfigureSDRdaemonWork::create(false);
|
||||
getInputMessageQueue()->push(command);
|
||||
}
|
||||
|
||||
const QString& SDRdaemonInput::getDeviceDescription() const
|
||||
{
|
||||
return m_deviceDescription;
|
||||
}
|
||||
|
||||
int SDRdaemonInput::getSampleRate() const
|
||||
{
|
||||
return m_sampleRate;
|
||||
}
|
||||
|
||||
quint64 SDRdaemonInput::getCenterFrequency() const
|
||||
{
|
||||
return m_centerFrequency;
|
||||
}
|
||||
|
||||
std::time_t SDRdaemonInput::getStartingTimeStamp() const
|
||||
{
|
||||
return m_startingTimeStamp;
|
||||
}
|
||||
|
||||
bool SDRdaemonInput::handleMessage(const Message& message)
|
||||
{
|
||||
if (MsgConfigureSDRdaemonUDPLink::match(message))
|
||||
{
|
||||
MsgConfigureSDRdaemonUDPLink& conf = (MsgConfigureSDRdaemonUDPLink&) message;
|
||||
m_SDRdaemonUDPHandler->configureUDPLink(conf.getAddress(), conf.getPort());
|
||||
return true;
|
||||
}
|
||||
else if (MsgConfigureSDRdaemonAutoCorr::match(message))
|
||||
{
|
||||
MsgConfigureSDRdaemonAutoCorr& conf = (MsgConfigureSDRdaemonAutoCorr&) message;
|
||||
bool dcBlock = conf.getDCBlock();
|
||||
bool iqImbalance = conf.getIQImbalance();
|
||||
DSPEngine::instance()->configureCorrections(dcBlock, iqImbalance);
|
||||
return true;
|
||||
}
|
||||
else if (MsgConfigureSDRdaemonWork::match(message))
|
||||
{
|
||||
MsgConfigureSDRdaemonWork& conf = (MsgConfigureSDRdaemonWork&) message;
|
||||
bool working = conf.isWorking();
|
||||
|
||||
if (working) {
|
||||
m_SDRdaemonUDPHandler->start();
|
||||
} else {
|
||||
m_SDRdaemonUDPHandler->stop();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgConfigureSDRdaemonStreamTiming::match(message))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
253
plugins/samplesource/sdrdaemon/sdrdaemoninput.h
Normal file
253
plugins/samplesource/sdrdaemon/sdrdaemoninput.h
Normal file
@ -0,0 +1,253 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 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 //
|
||||
// //
|
||||
// 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 INCLUDE_SDRDAEMONINPUT_H
|
||||
#define INCLUDE_SDRDAEMONINPUT_H
|
||||
|
||||
#include "dsp/samplesource.h"
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
|
||||
class SDRdaemonUDPHandler;
|
||||
|
||||
class SDRdaemonInput : public SampleSource {
|
||||
public:
|
||||
class MsgConfigureSDRdaemonUDPLink : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const QString& getAddress() const { return m_address; }
|
||||
quint16 getPort() const { return m_port; }
|
||||
|
||||
static MsgConfigureSDRdaemonUDPLink* create(const QString& address, quint16 port)
|
||||
{
|
||||
return new MsgConfigureSDRdaemonUDPLink(address, port);
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_address;
|
||||
quint16 m_port;
|
||||
|
||||
MsgConfigureSDRdaemonUDPLink(const QString& address, quint16 port) :
|
||||
Message(),
|
||||
m_address(address),
|
||||
m_port(port)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgConfigureSDRdaemonAutoCorr : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
public:
|
||||
bool getDCBlock() const { return m_dcBlock; }
|
||||
bool getIQImbalance() const { return m_iqCorrection; }
|
||||
|
||||
static MsgConfigureSDRdaemonAutoCorr* create(bool dcBlock, bool iqImbalance)
|
||||
{
|
||||
return new MsgConfigureSDRdaemonAutoCorr(dcBlock, iqImbalance);
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_dcBlock;
|
||||
bool m_iqCorrection;
|
||||
|
||||
MsgConfigureSDRdaemonAutoCorr(bool dcBlock, bool iqImbalance) :
|
||||
Message(),
|
||||
m_dcBlock(dcBlock),
|
||||
m_iqCorrection(iqImbalance)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgConfigureSDRdaemonWork : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
bool isWorking() const { return m_working; }
|
||||
|
||||
static MsgConfigureSDRdaemonWork* create(bool working)
|
||||
{
|
||||
return new MsgConfigureSDRdaemonWork(working);
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_working;
|
||||
|
||||
MsgConfigureSDRdaemonWork(bool working) :
|
||||
Message(),
|
||||
m_working(working)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgConfigureSDRdaemonStreamTiming : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
|
||||
static MsgConfigureSDRdaemonStreamTiming* create()
|
||||
{
|
||||
return new MsgConfigureSDRdaemonStreamTiming();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
MsgConfigureSDRdaemonStreamTiming() :
|
||||
Message()
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportSDRdaemonAcquisition : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
bool getAcquisition() const { return m_acquisition; }
|
||||
|
||||
static MsgReportSDRdaemonAcquisition* create(bool acquisition)
|
||||
{
|
||||
return new MsgReportSDRdaemonAcquisition(acquisition);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool m_acquisition;
|
||||
|
||||
MsgReportSDRdaemonAcquisition(bool acquisition) :
|
||||
Message(),
|
||||
m_acquisition(acquisition)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportSDRdaemonStreamData : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
int getSampleRateStream() const { return m_sampleRateStream; }
|
||||
int getSampleRate() const { return m_sampleRate; }
|
||||
quint64 getCenterFrequency() const { return m_centerFrequency; }
|
||||
uint32_t get_tv_sec() const { return m_tv_sec; }
|
||||
uint32_t get_tv_usec() const { return m_tv_usec; }
|
||||
|
||||
static MsgReportSDRdaemonStreamData* create(int sampleRateStream, int sampleRate, quint64 centerFrequency, uint32_t tv_sec, uint32_t tv_usec)
|
||||
{
|
||||
return new MsgReportSDRdaemonStreamData(sampleRateStream, sampleRate, centerFrequency, tv_sec, tv_usec);
|
||||
}
|
||||
|
||||
protected:
|
||||
int m_sampleRateStream;
|
||||
int m_sampleRate;
|
||||
quint64 m_centerFrequency;
|
||||
uint32_t m_tv_sec;
|
||||
uint32_t m_tv_usec;
|
||||
|
||||
MsgReportSDRdaemonStreamData(int sampleRateStream, int sampleRate, quint64 centerFrequency, uint32_t tv_sec, uint32_t tv_usec) :
|
||||
Message(),
|
||||
m_sampleRateStream(sampleRateStream),
|
||||
m_sampleRate(sampleRate),
|
||||
m_centerFrequency(centerFrequency),
|
||||
m_tv_sec(tv_sec),
|
||||
m_tv_usec(tv_usec)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportSDRdaemonStreamTiming : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
uint32_t get_tv_sec() const { return m_tv_sec; }
|
||||
uint32_t get_tv_usec() const { return m_tv_usec; }
|
||||
bool getSyncLock() const { return m_syncLock; }
|
||||
uint32_t getFrameSize() const { return m_frameSize; }
|
||||
bool getLz4Compression() const { return m_lz4; }
|
||||
float getLz4CompressionRatio() const { return m_compressionRatio; }
|
||||
uint32_t getLz4DataCRCOK() const { return m_nbLz4CRCOK; }
|
||||
uint32_t getLz4SuccessfulDecodes() const { return m_nbLz4SuccessfulDecodes; }
|
||||
|
||||
static MsgReportSDRdaemonStreamTiming* create(uint32_t tv_sec,
|
||||
uint32_t tv_usec,
|
||||
bool syncLock,
|
||||
uint32_t frameSize,
|
||||
bool lz4,
|
||||
float compressionRatio,
|
||||
uint32_t nbLz4CRCOK,
|
||||
uint32_t nbLz4SuccessfulDecodes)
|
||||
{
|
||||
return new MsgReportSDRdaemonStreamTiming(tv_sec,
|
||||
tv_usec,
|
||||
syncLock,
|
||||
frameSize,
|
||||
lz4,
|
||||
compressionRatio,
|
||||
nbLz4CRCOK,
|
||||
nbLz4SuccessfulDecodes);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t m_tv_sec;
|
||||
uint32_t m_tv_usec;
|
||||
bool m_syncLock;
|
||||
uint32_t m_frameSize;
|
||||
bool m_lz4;
|
||||
float m_compressionRatio;
|
||||
uint32_t m_nbLz4CRCOK;
|
||||
uint32_t m_nbLz4SuccessfulDecodes;
|
||||
|
||||
MsgReportSDRdaemonStreamTiming(uint32_t tv_sec,
|
||||
uint32_t tv_usec,
|
||||
bool syncLock,
|
||||
uint32_t frameSize,
|
||||
bool lz4,
|
||||
float compressionRatio,
|
||||
uint32_t nbLz4CRCOK,
|
||||
uint32_t nbLz4SuccessfulDecodes) :
|
||||
Message(),
|
||||
m_tv_sec(tv_sec),
|
||||
m_tv_usec(tv_usec),
|
||||
m_syncLock(syncLock),
|
||||
m_frameSize(frameSize),
|
||||
m_lz4(lz4),
|
||||
m_compressionRatio(compressionRatio),
|
||||
m_nbLz4CRCOK(nbLz4CRCOK),
|
||||
m_nbLz4SuccessfulDecodes(nbLz4SuccessfulDecodes)
|
||||
{ }
|
||||
};
|
||||
|
||||
SDRdaemonInput(const QTimer& masterTimer);
|
||||
virtual ~SDRdaemonInput();
|
||||
|
||||
virtual bool init(const Message& message);
|
||||
virtual bool start(int device);
|
||||
virtual void stop();
|
||||
|
||||
virtual const QString& getDeviceDescription() const;
|
||||
virtual int getSampleRate() const;
|
||||
virtual quint64 getCenterFrequency() const;
|
||||
std::time_t getStartingTimeStamp() const;
|
||||
|
||||
virtual bool handleMessage(const Message& message);
|
||||
|
||||
private:
|
||||
QMutex m_mutex;
|
||||
QString m_address;
|
||||
quint16 m_port;
|
||||
SDRdaemonUDPHandler* m_SDRdaemonUDPHandler;
|
||||
QString m_deviceDescription;
|
||||
int m_sampleRate;
|
||||
quint64 m_centerFrequency;
|
||||
std::time_t m_startingTimeStamp;
|
||||
const QTimer& m_masterTimer;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_SDRDAEMONINPUT_H
|
82
plugins/samplesource/sdrdaemon/sdrdaemonplugin.cpp
Normal file
82
plugins/samplesource/sdrdaemon/sdrdaemonplugin.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015 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 //
|
||||
// //
|
||||
// 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 <QAction>
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
|
||||
#include "sdrdaemongui.h"
|
||||
#include "sdrdaemonplugin.h"
|
||||
|
||||
const PluginDescriptor SDRdaemonPlugin::m_pluginDescriptor = {
|
||||
QString("SDRdaemon input"),
|
||||
QString("---"),
|
||||
QString("(c) Edouard Griffiths, F4EXB"),
|
||||
QString("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
QString("https://github.com/f4exb/sdrangel")
|
||||
};
|
||||
|
||||
const QString SDRdaemonPlugin::m_deviceTypeID = SDRDAEMON_DEVICE_TYPE_ID;
|
||||
|
||||
SDRdaemonPlugin::SDRdaemonPlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& SDRdaemonPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void SDRdaemonPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
m_pluginAPI->registerSampleSource(m_deviceTypeID, this);
|
||||
}
|
||||
|
||||
PluginInterface::SampleSourceDevices SDRdaemonPlugin::enumSampleSources()
|
||||
{
|
||||
SampleSourceDevices result;
|
||||
int count = 1;
|
||||
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
QString displayedName(QString("SDRdaemon[%1]").arg(i));
|
||||
|
||||
result.append(SampleSourceDevice(displayedName,
|
||||
m_deviceTypeID,
|
||||
QString::null,
|
||||
i));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PluginGUI* SDRdaemonPlugin::createSampleSourcePluginGUI(const QString& sourceId)
|
||||
{
|
||||
if(sourceId == m_deviceTypeID)
|
||||
{
|
||||
SDRdaemonGui* gui = new SDRdaemonGui(m_pluginAPI);
|
||||
m_pluginAPI->setInputGUI(gui);
|
||||
return gui;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2016 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 //
|
||||
@ -15,53 +14,34 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_SCOPEWINDOW_H
|
||||
#define INCLUDE_SCOPEWINDOW_H
|
||||
#ifndef INCLUDE_SDRDAEMONPLUGIN_H
|
||||
#define INCLUDE_SDRDAEMONPLUGIN_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "util/export.h"
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class DSPEngine;
|
||||
#define SDRDAEMON_DEVICE_TYPE_ID "sdrangel.samplesource.sdrdaemon"
|
||||
|
||||
namespace Ui {
|
||||
class ScopeWindow;
|
||||
}
|
||||
|
||||
class SDRANGEL_API ScopeWindow : public QWidget {
|
||||
class SDRdaemonPlugin : public QObject, public PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID SDRDAEMON_DEVICE_TYPE_ID)
|
||||
|
||||
public:
|
||||
explicit ScopeWindow(DSPEngine* dspEngine, QWidget* parent = NULL);
|
||||
~ScopeWindow();
|
||||
explicit SDRdaemonPlugin(QObject* parent = NULL);
|
||||
|
||||
void setSampleRate(int sampleRate);
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
virtual SampleSourceDevices enumSampleSources();
|
||||
virtual PluginGUI* createSampleSourcePluginGUI(const QString& sourceId);
|
||||
|
||||
private slots:
|
||||
void on_amp_valueChanged(int value);
|
||||
void on_scope_traceSizeChanged(int value);
|
||||
void on_time_valueChanged(int value);
|
||||
void on_timeOfs_valueChanged(int value);
|
||||
void on_displayMode_currentIndexChanged(int index);
|
||||
|
||||
void on_horizView_clicked();
|
||||
void on_vertView_clicked();
|
||||
static const QString m_deviceTypeID;
|
||||
|
||||
private:
|
||||
Ui::ScopeWindow *ui;
|
||||
int m_sampleRate;
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
|
||||
qint32 m_displayData;
|
||||
qint32 m_displayOrientation;
|
||||
qint32 m_timeBase;
|
||||
qint32 m_timeOffset;
|
||||
qint32 m_amplification;
|
||||
|
||||
void applySettings();
|
||||
PluginAPI* m_pluginAPI;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_SCOPEWINDOW_H
|
||||
#endif // INCLUDE_SDRDAEMONPLUGIN_H
|
226
plugins/samplesource/sdrdaemon/sdrdaemonudphandler.cpp
Normal file
226
plugins/samplesource/sdrdaemon/sdrdaemonudphandler.cpp
Normal file
@ -0,0 +1,226 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015 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 //
|
||||
// //
|
||||
// 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 <QUdpSocket>
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <unistd.h>
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "sdrdaemonudphandler.h"
|
||||
#include "sdrdaemoninput.h"
|
||||
|
||||
const int SDRdaemonUDPHandler::m_rateDivider = 1000/SDRDAEMON_THROTTLE_MS;
|
||||
|
||||
SDRdaemonUDPHandler::SDRdaemonUDPHandler(SampleFifo *sampleFifo, MessageQueue *outputMessageQueueToGUI) :
|
||||
m_sdrDaemonBuffer(m_rateDivider),
|
||||
m_dataSocket(0),
|
||||
m_dataAddress(QHostAddress::LocalHost),
|
||||
m_dataPort(9090),
|
||||
m_dataConnected(false),
|
||||
m_udpBuf(0),
|
||||
m_udpReadBytes(0),
|
||||
m_chunksize(0),
|
||||
m_sampleFifo(sampleFifo),
|
||||
m_samplerate(0),
|
||||
m_centerFrequency(0),
|
||||
m_tv_sec(0),
|
||||
m_tv_usec(0),
|
||||
m_outputMessageQueueToGUI(outputMessageQueueToGUI),
|
||||
m_tickCount(0),
|
||||
m_samplesCount(0),
|
||||
m_timer(0)
|
||||
{
|
||||
m_udpBuf = new char[SDRdaemonBuffer::m_udpPayloadSize];
|
||||
}
|
||||
|
||||
SDRdaemonUDPHandler::~SDRdaemonUDPHandler()
|
||||
{
|
||||
stop();
|
||||
delete[] m_udpBuf;
|
||||
}
|
||||
|
||||
void SDRdaemonUDPHandler::start()
|
||||
{
|
||||
qDebug("SDRdaemonUDPHandler::start");
|
||||
|
||||
if (!m_dataSocket)
|
||||
{
|
||||
m_dataSocket = new QUdpSocket(this);
|
||||
}
|
||||
|
||||
if (!m_dataConnected)
|
||||
{
|
||||
if (m_dataSocket->bind(m_dataAddress, m_dataPort))
|
||||
{
|
||||
qDebug("SDRdaemonUDPHandler::start: bind data socket to %s:%d", m_dataAddress.toString().toStdString().c_str(), m_dataPort);
|
||||
connect(m_dataSocket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()), Qt::QueuedConnection); // , Qt::QueuedConnection
|
||||
m_dataConnected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning("SDRdaemonUDPHandler::start: cannot bind data port %d", m_dataPort);
|
||||
m_dataConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to notify the DSP engine to actually start
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(m_samplerate, m_centerFrequency * 1000); // Frequency in Hz for the DSP engine
|
||||
DSPEngine::instance()->getInputMessageQueue()->push(notif);
|
||||
}
|
||||
|
||||
void SDRdaemonUDPHandler::stop()
|
||||
{
|
||||
qDebug("SDRdaemonUDPHandler::stop");
|
||||
|
||||
if (m_dataConnected) {
|
||||
disconnect(m_dataSocket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
|
||||
m_dataConnected = false;
|
||||
}
|
||||
|
||||
if (m_dataSocket)
|
||||
{
|
||||
delete m_dataSocket;
|
||||
m_dataSocket = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonUDPHandler::configureUDPLink(const QString& address, quint16 port)
|
||||
{
|
||||
qDebug("SDRdaemonUDPHandler::configureUDPLink: %s:%d", address.toStdString().c_str(), port);
|
||||
bool addressOK = m_dataAddress.setAddress(address);
|
||||
|
||||
if (!addressOK)
|
||||
{
|
||||
qWarning("SDRdaemonUDPHandler::configureUDPLink: invalid address %s. Set to localhost.", address.toStdString().c_str());
|
||||
m_dataAddress = QHostAddress::LocalHost;
|
||||
}
|
||||
|
||||
stop();
|
||||
m_dataPort = port;
|
||||
start();
|
||||
}
|
||||
|
||||
void SDRdaemonUDPHandler::dataReadyRead()
|
||||
{
|
||||
while (m_dataSocket->hasPendingDatagrams())
|
||||
{
|
||||
qint64 pendingDataSize = m_dataSocket->pendingDatagramSize();
|
||||
m_udpReadBytes = m_dataSocket->readDatagram(m_udpBuf, pendingDataSize, 0, 0);
|
||||
processData();
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonUDPHandler::processData()
|
||||
{
|
||||
if (m_udpReadBytes < 0)
|
||||
{
|
||||
qDebug() << "SDRdaemonThread::processData: read failed";
|
||||
}
|
||||
else if (m_udpReadBytes > 0)
|
||||
{
|
||||
m_sdrDaemonBuffer.updateBlockCounts(m_udpReadBytes);
|
||||
|
||||
if (m_sdrDaemonBuffer.readMeta(m_udpBuf, m_udpReadBytes))
|
||||
{
|
||||
const SDRdaemonBuffer::MetaData& metaData = m_sdrDaemonBuffer.getCurrentMeta();
|
||||
bool change = false;
|
||||
m_tv_sec = metaData.m_tv_sec;
|
||||
m_tv_usec = metaData.m_tv_usec;
|
||||
|
||||
uint32_t sampleRate = m_sdrDaemonBuffer.getSampleRate();
|
||||
|
||||
if (m_samplerate != sampleRate)
|
||||
{
|
||||
setSamplerate(sampleRate);
|
||||
m_samplerate = sampleRate;
|
||||
change = true;
|
||||
}
|
||||
|
||||
if (m_centerFrequency != metaData.m_centerFrequency)
|
||||
{
|
||||
m_centerFrequency = metaData.m_centerFrequency;
|
||||
change = true;
|
||||
}
|
||||
|
||||
if (change)
|
||||
{
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(m_samplerate, m_centerFrequency * 1000); // Frequency in Hz for the DSP engine
|
||||
DSPEngine::instance()->getInputMessageQueue()->push(notif);
|
||||
SDRdaemonInput::MsgReportSDRdaemonStreamData *report = SDRdaemonInput::MsgReportSDRdaemonStreamData::create(
|
||||
m_sdrDaemonBuffer.getSampleRateStream(),
|
||||
m_samplerate,
|
||||
m_centerFrequency * 1000, // Frequency in Hz for the GUI
|
||||
m_tv_sec,
|
||||
m_tv_usec);
|
||||
m_outputMessageQueueToGUI->push(report);
|
||||
}
|
||||
}
|
||||
else if (m_sdrDaemonBuffer.isSync())
|
||||
{
|
||||
m_sdrDaemonBuffer.writeData(m_udpBuf, m_udpReadBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonUDPHandler::setSamplerate(uint32_t samplerate)
|
||||
{
|
||||
qDebug() << "SDRdaemonUDPHandler::setSamplerate:"
|
||||
<< " new:" << samplerate
|
||||
<< " old:" << m_samplerate;
|
||||
|
||||
m_samplerate = samplerate;
|
||||
m_chunksize = (m_samplerate / m_rateDivider) * SDRdaemonBuffer::m_iqSampleSize;
|
||||
|
||||
qDebug() << "SDRdaemonUDPHandler::setSamplerate:"
|
||||
<< " chunk size: " << m_chunksize
|
||||
<< " #samples per chunk: " << (m_chunksize/SDRdaemonBuffer::m_iqSampleSize);
|
||||
}
|
||||
|
||||
void SDRdaemonUDPHandler::connectTimer(const QTimer* timer)
|
||||
{
|
||||
qDebug() << "SDRdaemonUDPHandler::connectTimer";
|
||||
m_timer = timer;
|
||||
connect(timer, SIGNAL(timeout()), this, SLOT(tick()));
|
||||
}
|
||||
|
||||
void SDRdaemonUDPHandler::tick()
|
||||
{
|
||||
// read samples directly feeding the SampleFifo (no callback)
|
||||
m_sampleFifo->write(reinterpret_cast<quint8*>(m_sdrDaemonBuffer.readDataChunk()), m_chunksize);
|
||||
m_samplesCount += m_chunksize / SDRdaemonBuffer::m_iqSampleSize;
|
||||
|
||||
if (m_tickCount < m_rateDivider)
|
||||
{
|
||||
m_tickCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tickCount = 0;
|
||||
SDRdaemonInput::MsgReportSDRdaemonStreamTiming *report = SDRdaemonInput::MsgReportSDRdaemonStreamTiming::create(
|
||||
m_tv_sec,
|
||||
m_tv_usec,
|
||||
m_sdrDaemonBuffer.isSyncLocked(),
|
||||
m_sdrDaemonBuffer.getFrameSize(),
|
||||
m_sdrDaemonBuffer.isLz4Compressed(),
|
||||
m_sdrDaemonBuffer.getCompressionRatio(),
|
||||
m_sdrDaemonBuffer.getLz4DataCRCOK(),
|
||||
m_sdrDaemonBuffer.getLz4SuccessfulDecodes());
|
||||
m_outputMessageQueueToGUI->push(report);
|
||||
}
|
||||
}
|
||||
|
||||
|
76
plugins/samplesource/sdrdaemon/sdrdaemonudphandler.h
Normal file
76
plugins/samplesource/sdrdaemon/sdrdaemonudphandler.h
Normal file
@ -0,0 +1,76 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015 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 //
|
||||
// //
|
||||
// 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 PLUGINS_SAMPLESOURCE_SDRDAEMON_SDRDAEMONUDPHANDLER_H_
|
||||
#define PLUGINS_SAMPLESOURCE_SDRDAEMON_SDRDAEMONUDPHANDLER_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
#include <QHostAddress>
|
||||
#include <QMutex>
|
||||
#include "sdrdaemonbuffer.h"
|
||||
|
||||
#define SDRDAEMON_THROTTLE_MS 50
|
||||
|
||||
class SampleFifo;
|
||||
class MessageQueue;
|
||||
class QTimer;
|
||||
|
||||
class SDRdaemonUDPHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SDRdaemonUDPHandler(SampleFifo* sampleFifo, MessageQueue *outputMessageQueueToGUI);
|
||||
~SDRdaemonUDPHandler();
|
||||
void connectTimer(const QTimer* timer);
|
||||
void start();
|
||||
void stop();
|
||||
void configureUDPLink(const QString& address, quint16 port);
|
||||
|
||||
public slots:
|
||||
void dataReadyRead();
|
||||
|
||||
private:
|
||||
SDRdaemonBuffer m_sdrDaemonBuffer;
|
||||
QUdpSocket *m_dataSocket;
|
||||
QHostAddress m_dataAddress;
|
||||
quint16 m_dataPort;
|
||||
bool m_dataConnected;
|
||||
char *m_udpBuf;
|
||||
qint64 m_udpReadBytes;
|
||||
std::size_t m_chunksize;
|
||||
SampleFifo *m_sampleFifo;
|
||||
uint32_t m_samplerate;
|
||||
uint32_t m_centerFrequency;
|
||||
uint32_t m_tv_sec;
|
||||
uint32_t m_tv_usec;
|
||||
MessageQueue *m_outputMessageQueueToGUI;
|
||||
uint32_t m_tickCount;
|
||||
std::size_t m_samplesCount;
|
||||
const QTimer *m_timer;
|
||||
|
||||
static const int m_rateDivider;
|
||||
|
||||
void setSamplerate(uint32_t samplerate);
|
||||
void processData();
|
||||
|
||||
private slots:
|
||||
void tick();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* PLUGINS_SAMPLESOURCE_SDRDAEMON_SDRDAEMONUDPHANDLER_H_ */
|
@ -84,7 +84,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Copyright (C) 2015 Edouard Griffiths, F4EXB. </p><p>Code at <a href="https://github.com/f4exb/sdrangel"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/f4exb/sdrangel</span></a> This is a complete redesign from RTL-SDRangelove at <a href="https://github.com/hexameron/rtl-sdrangelove"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/hexameron/rtl-sdrangelove</span></a></p><p>Many thanks to the original developers:</p><p>The osmocom developer team - especially horizon, Hoernchen &amp; tnt.</p><p>Christian Daniel from maintech GmbH.</p><p>John Greb (hexameron) for the contributions in RTL-SDRangelove</p><p>The following rules apply to the SDRangel main application and libsdrbase:<br/>This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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. You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/"><span style=" text-decoration: underline; color:#0000ff;">http://www.gnu.org/licenses/</span></a>.</p><p>For the license of installed plugins, look into the plugin list.</p></body></html></string>
|
||||
<string><html><head/><body><p>Version 1.1 Copyright (C) 2015 Edouard Griffiths, F4EXB. </p><p>Code at <a href="https://github.com/f4exb/sdrangel"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/f4exb/sdrangel</span></a> This is a complete redesign from RTL-SDRangelove at <a href="https://github.com/hexameron/rtl-sdrangelove"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/hexameron/rtl-sdrangelove</span></a></p><p>Many thanks to the original developers:</p><p>The osmocom developer team - especially horizon, Hoernchen &amp; tnt.</p><p>Christian Daniel from maintech GmbH.</p><p>John Greb (hexameron) for the contributions in RTL-SDRangelove</p><p>The following rules apply to the SDRangel main application and libsdrbase:<br/>This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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. You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/"><span style=" text-decoration: underline; color:#0000ff;">http://www.gnu.org/licenses/</span></a>.</p><p>For the license of installed plugins, look into the plugin list.</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
|
@ -1,192 +0,0 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 "gui/scopewindow.h"
|
||||
#include "ui_scopewindow.h"
|
||||
#include "util/simpleserializer.h"
|
||||
|
||||
ScopeWindow::ScopeWindow(DSPEngine* dspEngine, QWidget* parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::ScopeWindow),
|
||||
m_sampleRate(0),
|
||||
m_timeBase(1)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->scope->setDSPEngine(dspEngine);
|
||||
}
|
||||
|
||||
ScopeWindow::~ScopeWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ScopeWindow::setSampleRate(int sampleRate)
|
||||
{
|
||||
m_sampleRate = sampleRate;
|
||||
on_scope_traceSizeChanged(0);
|
||||
}
|
||||
|
||||
void ScopeWindow::resetToDefaults()
|
||||
{
|
||||
m_displayData = GLScope::ModeIQ;
|
||||
m_displayOrientation = Qt::Horizontal;
|
||||
m_timeBase = 1;
|
||||
m_timeOffset = 0;
|
||||
m_amplification = 0;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
QByteArray ScopeWindow::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
#if 0
|
||||
s.writeS32(1, m_displayData);
|
||||
s.writeS32(2, m_displayOrientation);
|
||||
s.writeS32(3, m_timeBase);
|
||||
s.writeS32(4, m_timeOffset);
|
||||
s.writeS32(5, m_amplification);
|
||||
#endif
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool ScopeWindow::deserialize(const QByteArray& data)
|
||||
{
|
||||
#if 0
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if(!d.isValid()) {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(d.getVersion() == 1) {
|
||||
d.readS32(1, &m_displayData, GLScope::ModeIQ);
|
||||
d.readS32(2, &m_displayOrientation, Qt::Horizontal);
|
||||
d.readS32(3, &m_timeBase, 1);
|
||||
d.readS32(4, &m_timeOffset, 0);
|
||||
d.readS32(5, &m_amplification, 0);
|
||||
if(m_timeBase < 0)
|
||||
m_timeBase = 1;
|
||||
applySettings();
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
resetToDefaults();
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ScopeWindow::on_amp_valueChanged(int value)
|
||||
{
|
||||
static qreal amps[11] = { 0.2, 0.1, 0.05, 0.02, 0.01, 0.005, 0.002, 0.001, 0.0005, 0.0002, 0.0001 };
|
||||
ui->ampText->setText(tr("%1\n/div").arg(amps[value], 0, 'f', 4));
|
||||
ui->scope->setAmp(0.2 / amps[value]);
|
||||
m_amplification = value;
|
||||
}
|
||||
|
||||
void ScopeWindow::on_scope_traceSizeChanged(int)
|
||||
{
|
||||
qreal t = (ui->scope->getTraceSize() * 0.1 / m_sampleRate) / (qreal)m_timeBase;
|
||||
if(t < 0.000001)
|
||||
ui->timeText->setText(tr("%1\nns/div").arg(t * 1000000000.0));
|
||||
else if(t < 0.001)
|
||||
ui->timeText->setText(tr("%1\nµs/div").arg(t * 1000000.0));
|
||||
else if(t < 1.0)
|
||||
ui->timeText->setText(tr("%1\nms/div").arg(t * 1000.0));
|
||||
else ui->timeText->setText(tr("%1\ns/div").arg(t * 1.0));
|
||||
}
|
||||
|
||||
void ScopeWindow::on_time_valueChanged(int value)
|
||||
{
|
||||
m_timeBase = value;
|
||||
on_scope_traceSizeChanged(0);
|
||||
ui->scope->setTimeBase(m_timeBase);
|
||||
}
|
||||
|
||||
void ScopeWindow::on_timeOfs_valueChanged(int value)
|
||||
{
|
||||
m_timeOffset = value;
|
||||
ui->scope->setTimeOfsProMill(value);
|
||||
}
|
||||
|
||||
void ScopeWindow::on_displayMode_currentIndexChanged(int index)
|
||||
{
|
||||
m_displayData = index;
|
||||
switch(index) {
|
||||
case 0: // i+q
|
||||
ui->scope->setMode(GLScope::ModeIQ);
|
||||
break;
|
||||
case 1: // mag(lin)+pha
|
||||
ui->scope->setMode(GLScope::ModeMagLinPha);
|
||||
break;
|
||||
case 2: // mag(dB)+pha
|
||||
ui->scope->setMode(GLScope::ModeMagdBPha);
|
||||
break;
|
||||
case 3: // derived1+derived2
|
||||
ui->scope->setMode(GLScope::ModeDerived12);
|
||||
break;
|
||||
case 4: // clostationary
|
||||
ui->scope->setMode(GLScope::ModeCyclostationary);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ScopeWindow::on_horizView_clicked()
|
||||
{
|
||||
m_displayOrientation = Qt::Horizontal;
|
||||
if(ui->horizView->isChecked()) {
|
||||
ui->vertView->setChecked(false);
|
||||
ui->scope->setOrientation(Qt::Horizontal);
|
||||
} else {
|
||||
ui->horizView->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
void ScopeWindow::on_vertView_clicked()
|
||||
{
|
||||
m_displayOrientation = Qt::Vertical;
|
||||
if(ui->vertView->isChecked()) {
|
||||
ui->horizView->setChecked(false);
|
||||
ui->scope->setOrientation(Qt::Vertical);
|
||||
} else {
|
||||
ui->vertView->setChecked(true);
|
||||
ui->scope->setOrientation(Qt::Vertical);
|
||||
}
|
||||
}
|
||||
|
||||
void ScopeWindow::applySettings()
|
||||
{
|
||||
ui->displayMode->setCurrentIndex(m_displayData);
|
||||
if(m_displayOrientation == Qt::Horizontal) {
|
||||
ui->scope->setOrientation(Qt::Horizontal);
|
||||
ui->horizView->setChecked(true);
|
||||
ui->vertView->setChecked(false);
|
||||
} else {
|
||||
ui->scope->setOrientation(Qt::Vertical);
|
||||
ui->horizView->setChecked(false);
|
||||
ui->vertView->setChecked(true);
|
||||
}
|
||||
ui->time->setValue(m_timeBase);
|
||||
ui->timeOfs->setValue(m_timeOffset);
|
||||
ui->amp->setValue(m_amplification);
|
||||
}
|
@ -30,11 +30,15 @@ PluginManager::~PluginManager()
|
||||
|
||||
void PluginManager::loadPlugins()
|
||||
{
|
||||
qDebug() << "PluginManager::loadPlugins: " << qPrintable(QApplication::instance()->applicationDirPath());
|
||||
QString applicationDirPath = QApplication::instance()->applicationDirPath();
|
||||
QString applicationLibPath = applicationDirPath + "/../lib";
|
||||
qDebug() << "PluginManager::loadPlugins: " << qPrintable(applicationDirPath) << ", " << qPrintable(applicationLibPath);
|
||||
|
||||
QDir pluginsDir = QDir(QApplication::instance()->applicationDirPath());
|
||||
QDir pluginsBinDir = QDir(applicationDirPath);
|
||||
QDir pluginsLibDir = QDir(applicationLibPath);
|
||||
|
||||
loadPlugins(pluginsDir);
|
||||
loadPlugins(pluginsBinDir);
|
||||
loadPlugins(pluginsLibDir);
|
||||
|
||||
qSort(m_plugins);
|
||||
|
||||
|
BIN
sdrbase/resources/compressed.png
Normal file
BIN
sdrbase/resources/compressed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 755 B |
BIN
sdrbase/resources/locked.png
Normal file
BIN
sdrbase/resources/locked.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 732 B |
@ -1,53 +1,56 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>appicon.png</file>
|
||||
<file>histogram.png</file>
|
||||
<file>waterfall.png</file>
|
||||
<file>preset-load.png</file>
|
||||
<file>preset-save.png</file>
|
||||
<file>preset-update.png</file>
|
||||
<file>preset-delete.png</file>
|
||||
<file>horizontal.png</file>
|
||||
<file>vertical.png</file>
|
||||
<file>maxhold.png</file>
|
||||
<file>grid.png</file>
|
||||
<file>invertspectrum.png</file>
|
||||
<file>preset-last.png</file>
|
||||
<file>display1.png</file>
|
||||
<file>display2.png</file>
|
||||
<file>slopen_icon.png</file>
|
||||
<file>slopep_icon.png</file>
|
||||
<file>display1_w.png</file>
|
||||
<file>display2_w.png</file>
|
||||
<file>horizontal_w.png</file>
|
||||
<file>vertical_w.png</file>
|
||||
<file>current.png</file>
|
||||
<file>slopeb_icon.png</file>
|
||||
<file>clear.png</file>
|
||||
<file>playloop.png</file>
|
||||
<file>play.png</file>
|
||||
<file>pause.png</file>
|
||||
<file>stop.png</file>
|
||||
<file>sdrangel_logo.png</file>
|
||||
<file>sdrangel_icon.png</file>
|
||||
<file>minus.png</file>
|
||||
<file>plus.png</file>
|
||||
<file>record_off.png</file>
|
||||
<file>record_on.png</file>
|
||||
<file>mem.png</file>
|
||||
<file>minusw.png</file>
|
||||
<file>plusw.png</file>
|
||||
<file>mono.png</file>
|
||||
<file>stereo.png</file>
|
||||
<file>sound_off.png</file>
|
||||
<file>sound_on.png</file>
|
||||
<file>dsb.png</file>
|
||||
<file>usb.png</file>
|
||||
<file>flip_lr.png</file>
|
||||
<file>flip_rl.png</file>
|
||||
<file>carrier.png</file>
|
||||
<file>rds.png</file>
|
||||
<file>recycle.png</file>
|
||||
<file>lsb.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/">
|
||||
<file>compressed.png</file>
|
||||
<file>locked.png</file>
|
||||
<file>appicon.png</file>
|
||||
<file>unlocked.png</file>
|
||||
<file>histogram.png</file>
|
||||
<file>waterfall.png</file>
|
||||
<file>preset-load.png</file>
|
||||
<file>preset-save.png</file>
|
||||
<file>preset-update.png</file>
|
||||
<file>preset-delete.png</file>
|
||||
<file>horizontal.png</file>
|
||||
<file>vertical.png</file>
|
||||
<file>maxhold.png</file>
|
||||
<file>grid.png</file>
|
||||
<file>invertspectrum.png</file>
|
||||
<file>preset-last.png</file>
|
||||
<file>display1.png</file>
|
||||
<file>display2.png</file>
|
||||
<file>slopen_icon.png</file>
|
||||
<file>slopep_icon.png</file>
|
||||
<file>display1_w.png</file>
|
||||
<file>display2_w.png</file>
|
||||
<file>horizontal_w.png</file>
|
||||
<file>vertical_w.png</file>
|
||||
<file>current.png</file>
|
||||
<file>slopeb_icon.png</file>
|
||||
<file>clear.png</file>
|
||||
<file>playloop.png</file>
|
||||
<file>play.png</file>
|
||||
<file>pause.png</file>
|
||||
<file>stop.png</file>
|
||||
<file>sdrangel_logo.png</file>
|
||||
<file>sdrangel_icon.png</file>
|
||||
<file>minus.png</file>
|
||||
<file>plus.png</file>
|
||||
<file>record_off.png</file>
|
||||
<file>record_on.png</file>
|
||||
<file>mem.png</file>
|
||||
<file>minusw.png</file>
|
||||
<file>plusw.png</file>
|
||||
<file>mono.png</file>
|
||||
<file>stereo.png</file>
|
||||
<file>sound_off.png</file>
|
||||
<file>sound_on.png</file>
|
||||
<file>dsb.png</file>
|
||||
<file>usb.png</file>
|
||||
<file>flip_lr.png</file>
|
||||
<file>flip_rl.png</file>
|
||||
<file>carrier.png</file>
|
||||
<file>rds.png</file>
|
||||
<file>recycle.png</file>
|
||||
<file>lsb.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
BIN
sdrbase/resources/unlocked.png
Normal file
BIN
sdrbase/resources/unlocked.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 385 B |
159
sdrbase/util/CRC64.cpp
Normal file
159
sdrbase/util/CRC64.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 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 //
|
||||
// //
|
||||
// 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 "util/CRC64.h"
|
||||
|
||||
/**
|
||||
* poly is: x^64 + x^62 + x^57 + x^55 + x^54 + x^53 + x^52 + x^47 + x^46 + x^45 + x^40 + x^39 +
|
||||
* x^38 + x^37 + x^35 + x^33 + x^32 + x^31 + x^29 + x^27 + x^24 + x^23 + x^22 + x^21 +
|
||||
* x^19 + x^17 + x^13 + x^12 + x^10 + x^9 + x^7 + x^4 + x^1 + 1
|
||||
*
|
||||
* represented here with lsb = highest degree term
|
||||
*
|
||||
* 1100100101101100010101111001010111010111100001110000111101000010_
|
||||
* || | | || || | | |||| | | ||| | |||| ||| |||| | | |
|
||||
* || | | || || | | |||| | | ||| | |||| ||| |||| | | +- x^64 (implied)
|
||||
* || | | || || | | |||| | | ||| | |||| ||| |||| | |
|
||||
* || | | || || | | |||| | | ||| | |||| ||| |||| | +--- x^62
|
||||
* || | | || || | | |||| | | ||| | |||| ||| |||| +-------- x^57
|
||||
* .......................................................................
|
||||
* ||
|
||||
* |+---------------------------------------------------------------- x^1
|
||||
* +----------------------------------------------------------------- x^0 (1)
|
||||
*/
|
||||
const uint64_t CRC64::m_poly = 0xC96C5795D7870F42ull;
|
||||
|
||||
CRC64::CRC64()
|
||||
{
|
||||
build_crc_table();
|
||||
}
|
||||
|
||||
CRC64::~CRC64()
|
||||
{}
|
||||
|
||||
/**
|
||||
* input is dividend: as 0000000000000000000000000000000000000000000000000000000000000000<8-bit byte>
|
||||
* where the lsb of the 8-bit byte is the coefficient of the highest degree term (x^71) of the dividend
|
||||
* so division is really for input byte * x^64
|
||||
*
|
||||
* you may wonder how 72 bits will fit in 64-bit data type... well as the shift-right occurs, 0's are supplied
|
||||
* on the left (most significant) side ... when the 8 shifts are done, the right side (where the input
|
||||
* byte was placed) is discarded
|
||||
*
|
||||
* when done, table[XX] (where XX is a byte) is equal to the CRC of 00 00 00 00 00 00 00 00 XX
|
||||
*/
|
||||
void CRC64::build_crc_table()
|
||||
{
|
||||
for(int i = 0; i < 256; ++i)
|
||||
{
|
||||
uint64_t crc = i;
|
||||
|
||||
for(unsigned int j = 0; j < 8; ++j)
|
||||
{
|
||||
if(crc & 1) // is current coefficient set?
|
||||
{
|
||||
crc >>= 1; // yes, then assume it gets zero'd (by implied x^64 coefficient of dividend)
|
||||
crc ^= m_poly; // and add rest of the divisor
|
||||
}
|
||||
else // no? then move to next coefficient
|
||||
{
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
m_crcTable[i] = crc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* will give an example CRC calculation for input array {0xDE, 0xAD}
|
||||
*
|
||||
* each byte represents a group of 8 coefficients for 8 dividend terms
|
||||
*
|
||||
* the actual polynomial dividend is:
|
||||
*
|
||||
* = DE AD 00 00 00 00 00 00 00 00 (hex)
|
||||
* = 11011110 10101101 0000000000000000000...0 (binary)
|
||||
* || |||| | | || |
|
||||
* || |||| | | || +------------------------ x^71
|
||||
* || |||| | | |+-------------------------- x^69
|
||||
* || |||| | | +--------------------------- x^68
|
||||
* || |||| | +----------------------------- x^66
|
||||
* || |||| +------------------------------- x^64
|
||||
* || ||||
|
||||
* || |||+---------------------------------- x^78
|
||||
* || ||+----------------------------------- x^77
|
||||
* || |+------------------------------------ x^76
|
||||
* || +------------------------------------- x^75
|
||||
* |+--------------------------------------- x^73
|
||||
* +---------------------------------------- x^72
|
||||
*
|
||||
*
|
||||
* the basic idea behind how the table lookup results can be used with one
|
||||
* another is that:
|
||||
*
|
||||
* Mod(A * x^n, P(x)) = Mod(x^n * Mod(A, P(X)), P(X))
|
||||
*
|
||||
* in other words, an input data shifted towards the higher degree terms
|
||||
* changes the pre-computed crc of the input data by shifting it also
|
||||
* the same amount towards higher degree terms (mod the polynomial)
|
||||
*
|
||||
* here is an example:
|
||||
*
|
||||
* 1) input:
|
||||
*
|
||||
* 00 00 00 00 00 00 00 00 AD DE
|
||||
*
|
||||
* 2) index crc table for byte DE (really for dividend 00 00 00 00 00 00 00 00 DE)
|
||||
*
|
||||
* we get A8B4AFBDC5A6ACA4
|
||||
*
|
||||
* 3) apply that to the input stream:
|
||||
*
|
||||
* 00 00 00 00 00 00 00 00 AD DE
|
||||
* A8 B4 AF BD C5 A6 AC A4
|
||||
* -----------------------------
|
||||
* 00 A8 B4 AF BD C5 A6 AC 09
|
||||
*
|
||||
* 4) index crc table for byte 09 (really for dividend 00 00 00 00 00 00 00 00 09)
|
||||
*
|
||||
* we get 448FCBB7FCB9E309
|
||||
*
|
||||
* 5) apply that to the input stream
|
||||
*
|
||||
* 00 A8 B4 AF BD C5 A6 AC 09
|
||||
* 44 8F CB B7 FC B9 E3 09
|
||||
* --------------------------
|
||||
* 44 27 7F 18 41 7C 45 A5
|
||||
*
|
||||
*/
|
||||
uint64_t CRC64::calculate_crc(uint8_t *stream, int length)
|
||||
{
|
||||
uint64_t crc = 0;
|
||||
|
||||
for (int i = 0 ; i < length; ++i)
|
||||
{
|
||||
uint8_t index = stream[i] ^ crc;
|
||||
uint64_t lookup = m_crcTable[index];
|
||||
|
||||
crc >>= 8;
|
||||
crc ^= lookup;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user