1
0
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:
f4exb 2016-02-24 15:56:47 +01:00
commit 3bfb2452ba
47 changed files with 2743 additions and 347 deletions

View File

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

View File

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

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

View File

@ -28,3 +28,5 @@ add_library(fcdhid SHARED
target_link_libraries(fcdhid
${LIBUSB_LIBRARIES}
)
install(TARGETS fcdhid DESTINATION lib)

View File

@ -27,3 +27,5 @@ add_library(fcdlib SHARED
)
target_link_libraries(fcdlib)
install(TARGETS fcdlib DESTINATION lib)

38
include/util/CRC64.h Normal file
View 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_ */

View File

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

View File

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

View File

@ -45,3 +45,5 @@ target_link_libraries(chanalyzer
)
qt5_use_modules(chanalyzer Core Widgets OpenGL Multimedia)
install(TARGETS chanalyzer DESTINATION lib/plugins/channel)

View File

@ -45,3 +45,5 @@ target_link_libraries(demodlora
)
qt5_use_modules(demodlora Core Widgets OpenGL Multimedia)
install(TARGETS demodlora DESTINATION lib/plugins/channel)

View File

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

View File

@ -45,3 +45,5 @@ target_link_libraries(demodssb
)
qt5_use_modules(demodssb Core Widgets OpenGL Multimedia)
install(TARGETS demodssb DESTINATION lib/plugins/channel)

View File

@ -45,3 +45,5 @@ target_link_libraries(demodtcpsrc
)
qt5_use_modules(demodtcpsrc Core Widgets OpenGL Network)
install(TARGETS demodtcpsrc DESTINATION lib/plugins/channel)

View File

@ -45,3 +45,5 @@ target_link_libraries(demodudpsrc
)
qt5_use_modules(demodudpsrc Core Widgets OpenGL Network)
install(TARGETS demodudpsrc DESTINATION lib/plugins/channel)

View File

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

View File

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

View File

@ -43,3 +43,4 @@ if(LIBUSB_FOUND AND LIBHACKRF_FOUND)
endif(LIBUSB_FOUND AND LIBHACKRF_FOUND)
add_subdirectory(filesource)
add_subdirectory(sdrdaemon)

View File

@ -51,3 +51,5 @@ target_link_libraries(inputairspy
)
qt5_use_modules(inputairspy Core Widgets OpenGL Multimedia)
install(TARGETS inputairspy DESTINATION lib/plugins/samplesource)

View File

@ -50,3 +50,5 @@ target_link_libraries(inputbladerf
)
qt5_use_modules(inputbladerf Core Widgets OpenGL Multimedia)
install(TARGETS inputbladerf DESTINATION lib/plugins/samplesource)

View File

@ -53,3 +53,5 @@ target_link_libraries(inputfcdpro
)
qt5_use_modules(inputfcdpro Core Widgets OpenGL Multimedia)
install(TARGETS inputfcdpro DESTINATION lib/plugins/samplesource)

View File

@ -53,3 +53,5 @@ target_link_libraries(inputfcdproplus
)
qt5_use_modules(inputfcdproplus Core Widgets OpenGL Multimedia)
install(TARGETS inputfcdproplus DESTINATION lib/plugins/samplesource)

View File

@ -45,3 +45,5 @@ target_link_libraries(inputfilesource
)
qt5_use_modules(inputfilesource Core Widgets OpenGL Multimedia)
install(TARGETS inputfilesource DESTINATION lib/plugins/samplesource)

View File

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

View File

@ -206,7 +206,6 @@ private:
std::time_t m_startingTimeStamp;
const QTimer& m_masterTimer;
bool applySettings(const Settings& settings, bool force);
void openFileStream();
};

View File

@ -51,3 +51,5 @@ target_link_libraries(inputhackrf
)
qt5_use_modules(inputhackrf Core Widgets OpenGL Multimedia)
install(TARGETS inputhackrf DESTINATION lib/plugins/samplesource)

View File

@ -50,3 +50,5 @@ target_link_libraries(inputrtlsdr
)
qt5_use_modules(inputrtlsdr Core Widgets OpenGL Multimedia)
install(TARGETS inputrtlsdr DESTINATION lib/plugins/samplesource)

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

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

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

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

View 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

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

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

View 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

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

View File

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

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

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

View File

@ -84,7 +84,7 @@
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Copyright (C) 2015 Edouard Griffiths, F4EXB. &lt;/p&gt;&lt;p&gt;Code at &lt;a href=&quot;https://github.com/f4exb/sdrangel&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/f4exb/sdrangel&lt;/span&gt;&lt;/a&gt; This is a complete redesign from RTL-SDRangelove at &lt;a href=&quot;https://github.com/hexameron/rtl-sdrangelove&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/hexameron/rtl-sdrangelove&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Many thanks to the original developers:&lt;/p&gt;&lt;p&gt;The osmocom developer team - especially horizon, Hoernchen &amp;amp; tnt.&lt;/p&gt;&lt;p&gt;Christian Daniel from maintech GmbH.&lt;/p&gt;&lt;p&gt;John Greb (hexameron) for the contributions in RTL-SDRangelove&lt;/p&gt;&lt;p&gt;The following rules apply to the SDRangel main application and libsdrbase:&lt;br/&gt;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 &lt;a href=&quot;http://www.gnu.org/licenses/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://www.gnu.org/licenses/&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;For the license of installed plugins, look into the plugin list.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Version 1.1 Copyright (C) 2015 Edouard Griffiths, F4EXB. &lt;/p&gt;&lt;p&gt;Code at &lt;a href=&quot;https://github.com/f4exb/sdrangel&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/f4exb/sdrangel&lt;/span&gt;&lt;/a&gt; This is a complete redesign from RTL-SDRangelove at &lt;a href=&quot;https://github.com/hexameron/rtl-sdrangelove&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/hexameron/rtl-sdrangelove&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Many thanks to the original developers:&lt;/p&gt;&lt;p&gt;The osmocom developer team - especially horizon, Hoernchen &amp;amp; tnt.&lt;/p&gt;&lt;p&gt;Christian Daniel from maintech GmbH.&lt;/p&gt;&lt;p&gt;John Greb (hexameron) for the contributions in RTL-SDRangelove&lt;/p&gt;&lt;p&gt;The following rules apply to the SDRangel main application and libsdrbase:&lt;br/&gt;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 &lt;a href=&quot;http://www.gnu.org/licenses/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://www.gnu.org/licenses/&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;For the license of installed plugins, look into the plugin list.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

159
sdrbase/util/CRC64.cpp Normal file
View 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;
}