1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-09-27 15:26:33 -04:00

Removed obsolete SDRdaemon plugin

This commit is contained in:
f4exb 2017-09-05 00:01:26 +02:00
parent 175fd69014
commit 48503d3147
16 changed files with 0 additions and 4267 deletions

View File

@ -1,59 +0,0 @@
project(sdrdaemon)
find_package(LZ4)
find_package(LibNANOMSG)
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}
)
#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}
${LIBNANOMSG_INCLUDE_DIR}
)
target_link_libraries(inputsdrdaemon
${QT_LIBRARIES}
${LZ4_LIBRARIES}
${LIBNANOMSG_LIBRARIES}
sdrbase
)
qt5_use_modules(inputsdrdaemon Core Widgets)
install(TARGETS inputsdrdaemon DESTINATION lib/plugins/samplesource)

View File

@ -1,152 +0,0 @@
<h1>SDRdaemon plugin</h1>
<h2>Introduction</h2>
This input sample source plugin gets its samples over tbe network from a SDRdaemon server using UDP connection. SDRdaemon refers to the SDRdaemon utility found in [this](https://github.com/f4exb/sdrdaemon) Github repostory.
<h2>Interface</h2>
![SDR Daemon plugin GUI](/doc/img/SDRdaemon_plugin.png)
<h3>1: Frequency</h3>
This is the center frequency in kHz sent in the meta data from the distant SDRdaemon instance and corresponds to the center frequency of reception.
<h3>2: Auto correction options</h3>
These buttons control the local DSP auto correction options:
- **DC**: auto remove DC component
- **IQ**: auto make I/Q balance
<h3>3: Date/time</h3>
This is the current timestamp of the block of data sent from the receiver. It is refreshed about every second. This may not and is usually not the timestamp of the samples currently shown in the displays and in the audio since there is a failty large buffer in place to damper the variations of data receiving speed due to the network (see: 5.5: Main buffer length in seconds).
<h3>9: Main buffer R/W pointers gauge</h3>
There are two gauges separated by a dot in the center. Ideally these gauges should not display any value thus read and write pointers are always half a buffer apart.
- The left gauge is the negative gauge. It is the value in percent of buffer size from the write pointer position to the read pointer position when this difference is less than half of a buffer distance. It means that the writes are leading or reads are lagging.
- The right gauge is the positive gauge. It is the value in percent of buffer size of the difference from the read pointer position to the write pointer position when this difference is less than half of a buffer distance. It menas that the writes are lagging or reads are leading.
<h3>4: Lock and sizes</h3>
![SDR Daemon status1 GUI](/doc/img/SDRdaemon_plugin_04.png)
<h4>4.1: Stream lock</h4>
Turns green when stream is locked to meta data. Meta data carries the number of UDP packets in following frame. Lock status is obtained when the correct number of UDP packets have been received when the next meta data block occurs.
<h4>4.2: Frame size</h4>
Data is sent in frames with one meta data header. This is the size of such a frame. Frames may be compressed however the frame will be copied uncompressed to the main buffer.
<h4>4.3: Sample rate as sent in the meta data</h4>
The receiver sends the stream data rate with this nominal value
<h4>4.4: Actual stream sample rate</h4>
When the auto follow sample rate is engaged the actual system sample rate may differ from the nominal sample rate sent in the meta data. This is the actual system sample rate.
<h4>4.5: Skew rate</h4>
This is the difference in percent between nominal sample rate sent in the meta data and actual system sample rate
<h4>4.6: Main buffer R/W pointers positions</h4>
Read and write pointers should always be a half buffer distance buffer apart. This is the difference in percent of the main buffer size from this ideal position.
- When positive it means that the read pointer is leading
- When negative it means that the write pointer is leading (read is lagging)
This corresponds to the value shown in the gauges above (9)
<h3>5: Compressed stream status and auto follow control buttons</h3>
![SDR Daemon status2 GUI](/doc/img/SDRdaemon_plugin_05.png)
<h4>5.1: Compressed stream</h4>
When lit in green it means the stream is compressed using LZ4.
<h4>5.2: Compression ratio</h4>
This is the ratio between the compressed data size and the original data size.
<h4>5.3: CRC OK ratio</h4>
This is the percentage of received data blocks with a correct CRC. It should be as closed to 100% as possible.
<h4>5.4: LZ4 successful decodes ratio</h4>
This is the percentage of LZ4 data blocks that were successfully uncompressed. It should be as closed to 100% as possible.
<h4>5.5: Main buffer length in seconds</h4>
This is the main buffer (writes from UDP / reads from DSP engine) length in units of time (seconds). Initially the write pointer is at the start of buffer and the read pointer is on the middle. Thus it takes half a buffer length in time to get the first useful sample. The minimum length is 8s and can be as long as to fit 50 average read chunks.
<h4>5.6: Reset buffer indexes push button</h4>
This forces the write and read pointers in their initial position regardless of the contents of the buffer. The write pointer position is set at the start of buffer and the read pointer position is set at the middle.
<h4>5.7: Auto lock main buffer R/W pointers position toggle</h4>
When set it engages the auto lock of R/W pointers position. It will try to maintain a half buffer distance between read and write pointers.
<h4>5.8: Auto lock to actual stream sample rate</h4>
This is rarely necessary. Only use it when you suspect that the sender data sample rate is not exactly as advertised in the meta data. This is normally exclusive of the auto lock R/W pointers position however the GUI allows both. You are advised to chose only one of the two.
<h3>6: Network parameters</h3>
![SDR Daemon status3 GUI](/doc/img/SDRdaemon_plugin_06.png)
<h4>6.1: Local interface IP address</h4>
Address of the network interface on the local (your) machine to which the SDRdaemon server sends samples to.
<h4>6.2: Local data port</h4>
UDP port on the local (your) machine to which the SDRdaemon server sends samples to.
<h4>6.3 Distant configuration port</h4>
TCP port on the distant machine hosting the SDRdaemon instance to send control messages to. The IP address of the host where the SDRdaemon instance runs is guessed from the address sending the data blocks hence it does not need to be specified.
<h4>6.4: Validation button</h4>
Whenever the address (6.1), data port (6.2) or configuration port (6.3) change this button is enabled to validate the new values.
<h3>7: Configuration parameters</h3>
![SDR Daemon status4 GUI](/doc/img/SDRdaemon_plugin_07.png)
<h4>7.1: Center frequency in kHz</h4>
This is the center frequency in kHz to which the hardware attached to the SDRdaemon instance will get tuned to.
<h4>7.2: Decimation factor</h4>
These are successive powers of two from 0 (1) to 6 (64). The SDRdaemon instance will decimate the samples coming from the attached hardware by this value. Thus the sample rate (see 7.5) will be decimated by the same value before it is sent over through the network.
<h4>7.3: Center frequency position</h4>
The center frequency in the passband wil be set either:
- below the local oscillator (NCO) or infradyne. Actually -1/4th the bandwidth.
- above the local oscillator (NCO) or supradyne. Actually +1/4th the bandwidth.
- centered on the local oscillator or zero IF.
<h4>7.4: Send data to the distant SDRdaemon instance</h4>
Whenever any of the parameters change this button gets enabled. When clicked a message is sent on the configuration port of the distant machine to which the SDRdaemon listens for instructions. Leave time for the buffering system to stabilize to get the samples flow through normally.
<h4>7.5: Sample rate in kS/s</h4>
The sample rate of the hardware device attached to the SDRdaemon instance will be set to this value in kS/s.
<h4>7.6: Other parameters hardware specific</h4>
THese are the parameters that are specific to the hardware attached to the distant SDRdaemon instance. You have to know which device is attached to send the proper parameters. Please refer to the SDRdaemon documentation or its line help to get information on these parameters.

View File

@ -1,52 +0,0 @@
#--------------------------------------------------------
#
# Pro file for Android and Windows builds with Qt Creator
#
#--------------------------------------------------------
TEMPLATE = lib
CONFIG += plugin
QT += core gui widgets multimedia network opengl
TARGET = inputsdrdaemon
DEFINES += USE_SSE2=1
QMAKE_CXXFLAGS += -msse2
DEFINES += USE_SSE4_1=1
QMAKE_CXXFLAGS += -msse4.1
CONFIG(MINGW32):LIBNANOMSGSRC = "D:\softs\nanomsg-0.8-beta"
CONFIG(MINGW64):LIBNANOMSGSRC = "D:\softs\nanomsg-0.8-beta"
INCLUDEPATH += $$PWD
INCLUDEPATH += ../../../sdrbase
INCLUDEPATH += ../../../lz4
!macx:INCLUDEPATH += $$LIBNANOMSGSRC/src
macx:INCLUDEPATH += /opt/local/include
CONFIG(Release):build_subdir = release
CONFIG(Debug):build_subdir = debug
SOURCES += sdrdaemonbuffer.cpp\
sdrdaemongui.cpp\
sdrdaemoninput.cpp\
sdrdaemonplugin.cpp\
sdrdaemonudphandler.cpp
HEADERS += sdrdaemonbuffer.h\
sdrdaemongui.h\
sdrdaemoninput.h\
sdrdaemonplugin.h\
sdrdaemonudphandler.h
FORMS += sdrdaemongui.ui
LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase
LIBS += -L../../../lz4/$${build_subdir} -llz4
!macx:LIBS += -L../../../nanomsg/$${build_subdir} -lnanomsg
macx:LIBS += -L/opt/local/lib -lnanomsg
RESOURCES = ../../../sdrbase/resources/res.qrc
CONFIG(MINGW32):DEFINES += USE_INTERNAL_TIMER=1

View File

@ -1,453 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "sdrdaemonbuffer.h"
#include <QDebug>
#include <cassert>
#include <cstring>
#include <cmath>
#include <lz4.h>
const int SDRdaemonBuffer::m_udpPayloadSize = 512;
const int SDRdaemonBuffer::m_sampleSize = 2;
const int SDRdaemonBuffer::m_iqSampleSize = 2 * m_sampleSize;
const int SDRdaemonBuffer::m_rawBufferLengthSeconds = 8; // should be even
const int SDRdaemonBuffer::m_rawBufferMinNbFrames = 50;
SDRdaemonBuffer::SDRdaemonBuffer(uint32_t throttlems) :
m_throttlemsNominal(throttlems),
m_rawSize(0),
m_rawBuffer(0),
m_sampleRateStream(0),
m_sampleRate(0),
m_sampleBytes(2),
m_sampleBits(12),
m_sync(false),
m_syncLock(false),
m_lz4(false),
m_nbBlocks(0),
m_bytesInBlock(0),
m_dataCRC(0),
m_inCount(0),
m_lz4InCount(0),
m_lz4InSize(0),
m_lz4InBuffer(0),
m_lz4OutBuffer(0),
m_frameSize(0),
m_bufferLenSec(0.0),
m_nbLz4Decodes(0),
m_nbLz4SuccessfulDecodes(0),
m_nbLz4CRCOK(0),
m_nbLastLz4SuccessfulDecodes(0),
m_nbLastLz4CRCOK(0),
m_writeIndex(0),
m_readIndex(0),
m_readSize(0),
m_readBuffer(0),
m_autoFollowRate(false),
m_autoCorrBuffer(false),
m_skewTest(false),
m_skewCorrection(false),
m_resetIndexes(false),
m_readCount(0),
m_writeCount(0),
m_nbCycles(0),
m_nbReads(0),
m_balCorrection(0),
m_balCorrLimit(0)
{
m_currentMeta.init();
}
SDRdaemonBuffer::~SDRdaemonBuffer()
{
if (m_rawBuffer) {
delete[] m_rawBuffer;
}
if (m_lz4InBuffer) {
delete[] m_lz4InBuffer;
}
if (m_lz4OutBuffer) {
delete[] m_lz4OutBuffer;
}
if (m_readBuffer) {
delete[] m_readBuffer;
}
}
void SDRdaemonBuffer::updateBufferSize(uint32_t sampleRate)
{
int rawSize = sampleRate * m_iqSampleSize * m_rawBufferLengthSeconds; // store worth of this seconds of samples at this sample rate
if ((m_frameSize > 0) && (rawSize / m_frameSize < m_rawBufferMinNbFrames))
{
rawSize = m_rawBufferMinNbFrames * m_frameSize; // ensure a minimal size of this times the write block size so that auto follow ups work fine
}
if (rawSize != m_rawSize)
{
m_rawSize = rawSize;
m_balCorrLimit = sampleRate / 50; // +/- 20 ms correction max per read
m_bufferLenSec = m_rawSize / (sampleRate * m_iqSampleSize);
if (m_rawBuffer) {
delete[] m_rawBuffer;
}
m_rawBuffer = new uint8_t[m_rawSize];
resetIndexes();
qDebug() << "SDRdaemonBuffer::updateBufferSize:"
<< " sampleRate: " << sampleRate
<< " m_frameSize: " << m_frameSize
<< " m_rawSize: " << m_rawSize;
}
}
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::updateReadBufferSize(uint32_t length)
{
if (m_readBuffer) {
delete[] m_readBuffer;
}
m_readBuffer = new uint8_t[length];
}
bool SDRdaemonBuffer::readMeta(char *array)
{
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))
{
printMeta(QString("SDRdaemonBuffer::readMeta"), 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;
uint32_t sampleRate = metaData->m_sampleRate;
if (sampleRate != m_sampleRateStream) // change of nominal stream sample rate
{
updateBufferSize(sampleRate);
m_sampleRateStream = sampleRate;
m_sampleRate = sampleRate;
}
// auto skew rate compensation
if (m_autoFollowRate)
{
if (m_skewCorrection)
{
int64_t deltaRate = (m_writeCount - m_readCount) / (m_nbCycles * m_rawBufferLengthSeconds * m_iqSampleSize);
m_sampleRate = ((m_sampleRate + deltaRate) / m_iqSampleSize) * m_iqSampleSize; // ensure it is a multiple of the I/Q sample size
resetIndexes();
}
}
else
{
m_sampleRate = sampleRate;
}
// Reset indexes if requested
if (m_resetIndexes)
{
resetIndexes();
m_resetIndexes = false;
}
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 (frameSize != m_frameSize) {
m_frameSize = frameSize;
updateBufferSize(m_sampleRate);
}
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);
}
}
}
uint8_t *SDRdaemonBuffer::readData(int32_t length)
{
// auto compensation calculations
if (m_skewTest && ((m_readIndex + length) > (m_rawSize / 2)))
{
// auto follow sample rate calculation
int dIndex = (m_readIndex - m_writeIndex > 0 ? m_readIndex - m_writeIndex : m_writeIndex - m_readIndex); // absolute delta
m_skewCorrection = (dIndex < m_rawSize / 10); // close by 10%
m_nbCycles++;
// auto R/W balance calculation
if ((m_nbReads > 5*m_rawBufferLengthSeconds) && m_autoCorrBuffer)
{
int32_t dBytes;
int32_t dI = (m_rawSize / 2) - m_readIndex; // delta of read index to the middle of buffer (positive)
if (m_readIndex > m_writeIndex) { // write leads
dBytes = m_writeIndex + dI; // positive from start of buffer + delta read index
} else { // read leads
dBytes = m_writeIndex - (int32_t) m_rawSize + dI; // negative from end of buffer minus delta read index
}
m_balCorrection = (m_balCorrection / 4) + ((int32_t) dBytes / (int32_t) (m_nbReads * m_iqSampleSize)); // correction is in number of samples. Alpha = 0.25
if (m_balCorrection < -m_balCorrLimit) {
m_balCorrection = -m_balCorrLimit;
} else if (m_balCorrection > m_balCorrLimit) {
m_balCorrection = m_balCorrLimit;
}
}
else
{
m_balCorrection = 0;
}
m_nbReads = 0;
// un-arm
m_skewTest = false;
}
m_readCount += length;
m_nbReads++;
if (m_readIndex + length < m_rawSize)
{
uint32_t readIndex = m_readIndex;
m_readIndex += length;
return &m_rawBuffer[readIndex];
}
else if (m_readIndex + length == m_rawSize)
{
uint32_t readIndex = m_readIndex;
m_readIndex = 0;
m_skewTest = true; // re-arm
return &m_rawBuffer[readIndex];
}
else
{
if (length > m_readSize)
{
updateReadBufferSize(length);
m_readSize = length;
}
std::memcpy((void *) m_readBuffer, (const void *) &m_rawBuffer[m_readIndex], m_rawSize - m_readIndex);
length -= m_rawSize - m_readIndex;
std::memcpy((void *) &m_readBuffer[m_rawSize - m_readIndex], (const void *) m_rawBuffer, length);
m_readIndex = length;
m_skewTest = true; // re-arm
return m_readBuffer;
}
}
void SDRdaemonBuffer::writeDataLZ4(const char *array, int 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)
{
qDebug() << "SDRdaemonBuffer::writeAndReadLZ4:"
<< " decoding: " << m_nbLz4CRCOK
<< ":" << m_nbLz4SuccessfulDecodes
<< "/" << m_nbLz4Decodes;
m_nbLastLz4SuccessfulDecodes = m_nbLz4SuccessfulDecodes;
m_nbLastLz4CRCOK = m_nbLz4CRCOK;
m_nbLz4Decodes = 0;
m_nbLz4SuccessfulDecodes = 0;
m_nbLz4CRCOK = 0;
}
writeToRawBufferLZ4();
m_lz4InCount = 0;
}
}
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);
}
}
void SDRdaemonBuffer::writeToRawBufferUncompressed(const char *array, int 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 if (m_writeIndex + length == m_rawSize)
{
std::memcpy((void *) &m_rawBuffer[m_writeIndex], (const void *) array, length);
m_writeIndex = 0;
}
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;
}
m_writeCount += length;
}
void SDRdaemonBuffer::resetIndexes()
{
m_writeIndex = 0;
m_readIndex = m_rawSize / 2;
m_readCount = 0;
m_writeCount = 0;
m_nbCycles = 0;
m_skewTest = false;
m_skewCorrection = false;
m_nbReads = 0;
m_balCorrection = 0;
}
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(const QString& header, MetaData *metaData)
{
qDebug() << header << ": "
<< "|" << 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;
}

View File

@ -1,179 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QString>
#include <cstdlib>
#include "util/CRC64.h"
class SDRdaemonBuffer
{
public:
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 throttlems);
~SDRdaemonBuffer();
bool readMeta(char *array); //!< Attempt to read meta. Returns true if meta block
void writeData(char *array, uint32_t length); //!< Write data into buffer.
uint8_t *readData(int32_t length);
void updateBlockCounts(uint32_t nbBytesReceived);
const MetaData& getCurrentMeta() const { return m_currentMeta; }
uint32_t getSampleRateStream() const { return m_sampleRateStream; }
uint32_t getSampleRate() const { return m_sampleRate; }
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; }
float getBufferLengthInSecs() const { return m_bufferLenSec; }
void setAutoFollowRate(bool autoFollowRate) { m_autoFollowRate = autoFollowRate; }
void setAutoCorrBuffer(bool autoCorrBuffer) { m_autoCorrBuffer = autoCorrBuffer; }
void setResetIndexes() { m_resetIndexes = true; }
int32_t getRWBalanceCorrection() const { return m_balCorrection; }
/** Get buffer gauge value in % of buffer size ([-50:50])
* [-50:0] : write leads or read lags
* [0:50] : read leads or write lags
*/
inline int32_t getBufferGauge() const
{
if (m_rawSize)
{
int32_t val = ((m_writeIndex - m_readIndex) * 100) / (int32_t) m_rawSize;
if (val < -50) {
return val + 100; // read leads (positive)
} else if (val < 50) {
return val; // read leads (positive) or write leads (negative)
} else {
return val - 100; // write leads (negative)
}
}
else
{
return -50; // default position
}
}
static const int m_udpPayloadSize;
static const int m_sampleSize;
static const int m_iqSampleSize;
static const int m_rawBufferLengthSeconds;
static const int m_rawBufferMinNbFrames; //!< Minimum number of frames for the length of buffer
private:
void updateBufferSize(uint32_t sampleRate);
void updateLZ4Sizes(uint32_t frameSize);
void updateReadBufferSize(uint32_t length);
void writeDataLZ4(const char *array, int length);
void writeToRawBufferLZ4();
void writeToRawBufferUncompressed(const char *array, int length);
void resetIndexes();
static void printMeta(const QString& header, MetaData *metaData);
uint32_t m_throttlemsNominal; //!< Initial throttle in ms
int 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_sampleRateStream; //!< Current sample rate from the stream meta data
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
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_nbBlocks; //!< Number of UDP blocks received in the current frame
uint32_t m_bytesInBlock; //!< Number of bytes received in the current UDP block
uint64_t m_dataCRC; //!< CRC64 of the data block
uint32_t m_inCount; //!< Current position of uncompressed input
int m_lz4InCount; //!< Current position in LZ4 input buffer
int m_lz4InSize; //!< Size in bytes of the LZ4 input data
uint8_t *m_lz4InBuffer; //!< Buffer for LZ4 compressed input
uint8_t *m_lz4OutBuffer; //!< Buffer for LZ4 uncompressed output
uint32_t m_frameSize; //!< Size in bytes of one uncompressed frame
float m_bufferLenSec; //!< Raw buffer length in seconds
uint32_t m_nbLz4Decodes;
uint32_t m_nbLz4SuccessfulDecodes;
uint32_t m_nbLz4CRCOK;
uint32_t m_nbLastLz4SuccessfulDecodes;
uint32_t m_nbLastLz4CRCOK;
int32_t m_writeIndex; //!< Current write position in the raw samples buffer
int32_t m_readIndex; //!< Current read position in the raw samples buffer
int m_readSize; //!< Read buffer size
uint8_t *m_readBuffer; //!< Read buffer to hold samples when looping back to beginning of raw buffer
bool m_autoFollowRate; //!< Auto follow stream sample rate else stick with meta data sample rate
bool m_autoCorrBuffer; //!< Auto correct buffer read / write balance (attempt to ...)
bool m_skewTest;
bool m_skewCorrection; //!< Do a skew rate correction at next meta data reception
bool m_resetIndexes; //!< Do a reset indexes at next meta data reception
int64_t m_readCount; //!< Number of bytes read for auto skew compensation
int64_t m_writeCount; //!< Number of bytes written for auto skew compensation
uint32_t m_nbCycles; //!< Number of buffer cycles since start of auto skew compensation byte counting
uint32_t m_nbReads; //!< Number of buffer reads since start of auto R/W balance correction period
int32_t m_balCorrection; //!< R/W balance correction in number of samples
int32_t m_balCorrLimit; //!< Correction absolute value limit in number of samples
};
#endif /* PLUGINS_SAMPLESOURCE_SDRDAEMON_SDRDAEMONBUFFER_H_ */

View File

@ -1,370 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "sdrdaemonbufferold.h"
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <iostream>
const int SDRdaemonBufferOld::m_udpPayloadSize = 512;
const int SDRdaemonBufferOld::m_sampleSize = 2;
const int SDRdaemonBufferOld::m_iqSampleSize = 2 * m_sampleSize;
SDRdaemonBufferOld::SDRdaemonBufferOld(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_autoFollowRate(false)
{
m_currentMeta.init();
}
SDRdaemonBufferOld::~SDRdaemonBufferOld()
{
if (m_rawBuffer) {
delete[] m_rawBuffer;
}
if (m_lz4InBuffer) {
delete[] m_lz4InBuffer;
}
if (m_lz4OutBuffer) {
delete[] m_lz4OutBuffer;
}
}
bool SDRdaemonBufferOld::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 (m_autoFollowRate)
{
if (sampleRate != m_sampleRateStream)
{
m_sampleRateStream = sampleRate;
}
else
{
sampleRate = m_sampleRate;
}
sampleRate += sampleRate * m_skewRate;
sampleRate = (sampleRate / m_rateDivider) * m_rateDivider;
}
else
{
m_sampleRateStream = sampleRate;
}
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 SDRdaemonBufferOld::writeData(char *array, uint32_t length)
{
if ((m_sync) && (m_nbBlocks > 0))
{
if (m_lz4)
{
writeDataLZ4(array, length);
}
else
{
writeToRawBufferUncompressed(array, length);
}
}
}
void SDRdaemonBufferOld::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 SDRdaemonBufferOld::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 SDRdaemonBufferOld::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 *SDRdaemonBufferOld::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_autoFollowRate)
{
m_skewRate = 0.0;
}
else 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 SDRdaemonBufferOld::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 SDRdaemonBufferOld::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 SDRdaemonBufferOld::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 SDRdaemonBufferOld::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

@ -1,136 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_SDRDAEMONBUFFEROLD_H_
#define PLUGINS_SAMPLESOURCE_SDRDAEMON_SDRDAEMONBUFFEROLD_H_
#include <stdint.h>
#include <cstring>
#include <cstddef>
#include <lz4.h>
#include "../../../sdrbase/dsp/samplesinkfifo.h"
#include "util/CRC64.h"
class SDRdaemonBufferOld
{
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)
SDRdaemonBufferOld(uint32_t rateDivider);
~SDRdaemonBufferOld();
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; }
void setAutoFollowRate(bool autoFollowRate) { m_autoFollowRate = autoFollowRate; }
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;
bool m_autoFollowRate; //!< Aito follow stream sample rate else stick with meta data sample rate
};
#endif /* PLUGINS_SAMPLESOURCE_SDRDAEMON_SDRDAEMONBUFFEROLD_H_ */

View File

@ -1,736 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <stdint.h>
#include <sstream>
#include <iostream>
#include <cassert>
#include <QDebug>
#include <QMessageBox>
#include <QTime>
#include <QDateTime>
#include <QString>
#include <QFileDialog>
#include <nanomsg/nn.h>
#include <nanomsg/pair.h>
#include "ui_sdrdaemongui.h"
#include "gui/colormapper.h"
#include "gui/glspectrum.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "mainwindow.h"
#include "util/simpleserializer.h"
#include "sdrdaemongui.h"
#include <device/devicesourceapi.h>
#include <dsp/filerecord.h>
SDRdaemonGui::SDRdaemonGui(DeviceSourceAPI *deviceAPI, QWidget* parent) :
QWidget(parent),
ui(new Ui::SDRdaemonGui),
m_deviceAPI(deviceAPI),
m_sampleSource(NULL),
m_acquisition(false),
m_lastEngineState((DSPDeviceSourceEngine::State)-1),
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_bufferLengthInSecs(0.0),
m_bufferGauge(-50),
m_samplesCount(0),
m_tickCount(0),
m_address("127.0.0.1"),
m_dataPort(9090),
m_controlPort(9091),
m_addressEdited(false),
m_dataPortEdited(false),
m_initSendConfiguration(false),
m_dcBlock(false),
m_iqCorrection(false),
m_autoFollowRate(false)
{
m_sender = nn_socket(AF_SP, NN_PAIR);
assert(m_sender != -1);
int millis = 500;
int rc __attribute__((unused)) = nn_setsockopt (m_sender, NN_SOL_SOCKET, NN_SNDTIMEO, &millis, sizeof (millis));
assert (rc == 0);
m_startingTimeStamp.tv_sec = 0;
m_startingTimeStamp.tv_usec = 0;
ui->setupUi(this);
ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->centerFrequency->setValueRange(7, 0, pow(10,7));
//connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); does not exist in this class
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(500);
connect(&(deviceAPI->getMainWindow()->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick()));
m_sampleSource = new SDRdaemonInput(deviceAPI->getMainWindow()->getMasterTimer(), m_deviceAPI);
connect(m_sampleSource->getOutputMessageQueueToGUI(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages()));
m_deviceAPI->setSource(m_sampleSource);
displaySettings();
ui->applyButton->setEnabled(false);
ui->sendButton->setEnabled(false);
char recFileNameCStr[30];
sprintf(recFileNameCStr, "test_%d.sdriq", m_deviceAPI->getDeviceUID());
m_fileSink = new FileRecord(std::string(recFileNameCStr));
m_deviceAPI->addSink(m_fileSink);
connect(m_deviceAPI->getDeviceOutputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleDSPMessages()), Qt::QueuedConnection);
}
SDRdaemonGui::~SDRdaemonGui()
{
m_deviceAPI->removeSink(m_fileSink);
delete m_fileSink;
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_remoteAddress = "127.0.0.1";
m_dataPort = 9090;
m_controlPort = 9091;
m_dcBlock = false;
m_iqCorrection = false;
m_autoFollowRate = false;
displaySettings();
}
QByteArray SDRdaemonGui::serialize() const
{
bool ok;
SimpleSerializer s(1);
s.writeString(1, ui->address->text());
uint32_t uintval = ui->dataPort->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);
s.writeBool(5, m_autoFollowRate);
s.writeBool(6, m_autoCorrBuffer);
uintval = ui->controlPort->text().toInt(&ok);
if((!ok) || (uintval < 1024) || (uintval > 65535)) {
uintval = 9091;
}
s.writeU32(7, uintval);
uint32_t confFrequency = ui->freq->text().toInt(&ok);
if (ok) {
s.writeU32(8, confFrequency);
}
s.writeU32(9, ui->decim->currentIndex());
s.writeU32(10, ui->fcPos->currentIndex());
uint32_t sampleRate = ui->sampleRate->text().toInt(&ok);
if (ok) {
s.writeU32(11, sampleRate);
}
s.writeString(12, ui->specificParms->text());
return s.final();
}
bool SDRdaemonGui::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
QString address;
quint16 dataPort;
bool dcBlock;
bool iqCorrection;
bool autoFollowRate;
bool autoCorrBuffer;
uint32_t confFrequency;
uint32_t confSampleRate;
uint32_t confDecim;
uint32_t confFcPos;
QString confSpecificParms;
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)) {
dataPort = uintval;
} else {
dataPort = 9090;
}
d.readBool(3, &dcBlock, false);
d.readBool(4, &iqCorrection, false);
d.readBool(5, &autoFollowRate, false);
d.readBool(6, &autoCorrBuffer, false);
d.readU32(7, &uintval, 9091);
if ((uintval > 1024) && (uintval < 65536)) {
m_controlPort = uintval;
} else {
m_controlPort = 9091;
}
d.readU32(8, &confFrequency, 435000);
d.readU32(9, &confDecim, 3);
d.readU32(10, &confFcPos, 2);
d.readU32(11, &confSampleRate, 1000);
d.readString(12, &confSpecificParms, "");
if ((address != m_address) || (dataPort != m_dataPort))
{
m_address = address;
m_dataPort = dataPort;
configureUDPLink();
}
if ((dcBlock != m_dcBlock) || (iqCorrection != m_iqCorrection))
{
m_dcBlock = dcBlock;
m_iqCorrection = iqCorrection;
configureAutoCorrections();
}
if ((m_autoFollowRate != autoFollowRate) || (m_autoCorrBuffer != autoCorrBuffer))
{
m_autoFollowRate = autoFollowRate;
m_autoCorrBuffer = autoCorrBuffer;
configureAutoFollowPolicy();
}
displaySettings();
displayConfigurationParameters(confFrequency, confDecim, confFcPos, confSampleRate, confSpecificParms);
m_initSendConfiguration = true;
return true;
}
else
{
resetToDefaults();
displaySettings();
QString defaultSpecificParameters("");
displayConfigurationParameters(435000, 3, 2, 1000, defaultSpecificParameters);
m_initSendConfiguration = true;
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();
m_bufferLengthInSecs = ((SDRdaemonInput::MsgReportSDRdaemonStreamTiming&)message).getBufferLengthInSecs();
m_bufferGauge = ((SDRdaemonInput::MsgReportSDRdaemonStreamTiming&)message).getBufferGauge();
updateWithStreamTime();
return true;
}
else
{
return false;
}
}
void SDRdaemonGui::handleDSPMessages()
{
Message* message;
while ((message = m_deviceAPI->getDeviceOutputMessageQueue()->pop()) != 0)
{
qDebug("SDRdaemonGui::handleDSPMessages: message: %s", message->getIdentifier());
if (DSPSignalNotification::match(*message))
{
DSPSignalNotification* notif = (DSPSignalNotification*) message;
m_deviceSampleRate = notif->getSampleRate();
m_deviceCenterFrequency = notif->getCenterFrequency();
qDebug("SDRdaemonGui::handleDSPMessages: SampleRate:%d, CenterFrequency:%llu", notif->getSampleRate(), notif->getCenterFrequency());
updateSampleRateAndFrequency();
m_fileSink->handleMessage(*notif); // forward to file sink
delete message;
}
}
}
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::updateSampleRateAndFrequency()
{
m_deviceAPI->getSpectrum()->setSampleRate(m_deviceSampleRate);
m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency);
ui->deviceRateText->setText(tr("%1k").arg((float)m_deviceSampleRate / 1000));
}
void SDRdaemonGui::displaySettings()
{
ui->address->setText(m_address);
ui->dataPort->setText(QString::number(m_dataPort));
ui->controlPort->setText(QString::number(m_controlPort));
ui->dcOffset->setChecked(m_dcBlock);
ui->iqImbalance->setChecked(m_iqCorrection);
ui->autoFollowRate->setChecked(m_autoFollowRate);
ui->autoCorrBuffer->setChecked(m_autoCorrBuffer);
}
void SDRdaemonGui::displayConfigurationParameters(uint32_t freq,
uint32_t log2Decim,
uint32_t fcPos,
uint32_t sampleRate,
QString& specParms)
{
ui->freq->setText(QString::number(freq));
ui->decim->setCurrentIndex(log2Decim);
ui->fcPos->setCurrentIndex(fcPos);
ui->sampleRate->setText(QString::number(sampleRate));
ui->specificParms->setText(specParms);
ui->specificParms->setCursorPosition(0);
}
void SDRdaemonGui::on_applyButton_clicked(bool checked __attribute__((unused)))
{
bool dataOk, ctlOk;
QString udpAddress = ui->address->text();
int udpDataPort = ui->dataPort->text().toInt(&dataOk);
int tcpCtlPort = ui->controlPort->text().toInt(&ctlOk);
if((!dataOk) || (udpDataPort < 1024) || (udpDataPort > 65535))
{
udpDataPort = 9090;
}
if((!ctlOk) || (tcpCtlPort < 1024) || (tcpCtlPort > 65535))
{
tcpCtlPort = 9091;
}
m_address = udpAddress;
m_dataPort = udpDataPort;
m_controlPort = tcpCtlPort;
if (m_addressEdited || m_dataPortEdited)
{
configureUDPLink();
m_addressEdited = false;
m_dataPortEdited = false;
}
ui->applyButton->setEnabled(false);
}
void SDRdaemonGui::on_sendButton_clicked(bool checked __attribute__((unused)))
{
sendConfiguration();
ui->specificParms->setCursorPosition(0);
ui->sendButton->setEnabled(false);
}
void SDRdaemonGui::sendConfiguration()
{
QString remoteAddress;
((SDRdaemonInput *) m_sampleSource)->getRemoteAddress(remoteAddress);
if (remoteAddress != m_remoteAddress)
{
m_remoteAddress = remoteAddress;
std::ostringstream os;
os << "tcp://" << m_remoteAddress.toStdString() << ":" << m_controlPort;
std::string addrstrng = os.str();
int rc = nn_connect(m_sender, addrstrng.c_str());
if (rc < 0) {
QMessageBox::information(this, tr("Message"), tr("Cannot connect to remote control port"));
} else {
qDebug() << "SDRdaemonGui::sendConfiguration: connexion to " << addrstrng.c_str() << " successful";
}
}
std::ostringstream os;
bool ok;
os << "decim=" << ui->decim->currentIndex()
<< ",fcpos=" << ui->fcPos->currentIndex();
uint64_t freq = ui->freq->text().toInt(&ok);
if (ok) {
os << ",freq=" << freq*1000LL;
} else {
QMessageBox::information(this, tr("Message"), tr("Invalid frequency"));
}
uint32_t srate = ui->sampleRate->text().toInt(&ok);
if (ok) {
os << ",srate=" << srate*1000;
} else {
QMessageBox::information(this, tr("Message"), tr("invalid sample rate"));
}
if ((ui->specificParms->text()).size() > 0) {
os << "," << ui->specificParms->text().toStdString();
}
int config_size = os.str().size();
int rc = nn_send(m_sender, (void *) os.str().c_str(), config_size, 0);
if (rc != config_size)
{
QMessageBox::information(this, tr("Message"), tr("Cannot send message to remote control port"));
}
else
{
qDebug() << "SDRdaemonGui::sendConfiguration:"
<< " remoteAddress: " << remoteAddress
<< " message: " << os.str().c_str();
}
}
void SDRdaemonGui::on_address_textEdited(const QString& arg1 __attribute__((unused)))
{
ui->applyButton->setEnabled(true);
m_addressEdited = true;
}
void SDRdaemonGui::on_dataPort_textEdited(const QString& arg1 __attribute__((unused)))
{
ui->applyButton->setEnabled(true);
m_dataPortEdited = true;
}
void SDRdaemonGui::on_controlPort_textEdited(const QString& arg1 __attribute__((unused)))
{
ui->applyButton->setEnabled(true);
}
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::on_autoFollowRate_toggled(bool checked)
{
if (m_autoFollowRate != checked) {
m_autoFollowRate = checked;
configureAutoFollowPolicy();
}
}
void SDRdaemonGui::on_autoCorrBuffer_toggled(bool checked)
{
if (m_autoCorrBuffer != checked) {
m_autoCorrBuffer = checked;
configureAutoFollowPolicy();
}
}
void SDRdaemonGui::on_resetIndexes_clicked(bool checked __attribute__((unused)))
{
SDRdaemonInput::MsgConfigureSDRdaemonResetIndexes* message = SDRdaemonInput::MsgConfigureSDRdaemonResetIndexes::create();
m_sampleSource->getInputMessageQueue()->push(message);
}
void SDRdaemonGui::on_freq_textEdited(const QString& arg1 __attribute__((unused)))
{
ui->sendButton->setEnabled(true);
}
void SDRdaemonGui::on_sampleRate_textEdited(const QString& arg1 __attribute__((unused)))
{
ui->sendButton->setEnabled(true);
}
void SDRdaemonGui::on_specificParms_textEdited(const QString& arg1 __attribute__((unused)))
{
ui->sendButton->setEnabled(true);
}
void SDRdaemonGui::on_decim_currentIndexChanged(int index __attribute__((unused)))
{
ui->sendButton->setEnabled(true);
}
void SDRdaemonGui::on_fcPos_currentIndexChanged(int index __attribute__((unused)))
{
ui->sendButton->setEnabled(true);
}
void SDRdaemonGui::on_startStop_toggled(bool checked)
{
if (checked)
{
if (m_deviceAPI->initAcquisition())
{
m_deviceAPI->startAcquisition();
DSPEngine::instance()->startAudioOutput();
}
}
else
{
m_deviceAPI->stopAcquisition();
DSPEngine::instance()->stopAudioOutput();
}
}
void SDRdaemonGui::on_record_toggled(bool checked)
{
if (checked)
{
ui->record->setStyleSheet("QToolButton { background-color : red; }");
m_fileSink->startRecording();
}
else
{
ui->record->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
m_fileSink->stopRecording();
}
}
void SDRdaemonGui::configureUDPLink()
{
qDebug() << "SDRdaemonGui::configureUDPLink: " << m_address.toStdString().c_str()
<< " : " << m_dataPort;
SDRdaemonInput::MsgConfigureSDRdaemonUDPLink* message = SDRdaemonInput::MsgConfigureSDRdaemonUDPLink::create(m_address, m_dataPort);
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::configureAutoFollowPolicy()
{
SDRdaemonInput::MsgConfigureSDRdaemonAutoFollowPolicy* message = SDRdaemonInput::MsgConfigureSDRdaemonAutoFollowPolicy::create(m_autoFollowRate, m_autoCorrBuffer);
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()
{
if (m_initSendConfiguration)
{
sendConfiguration();
m_initSendConfiguration = false;
}
quint64 startingTimeStampMsec = ((quint64) m_startingTimeStamp.tv_sec * 1000LL) + ((quint64) m_startingTimeStamp.tv_usec / 1000LL);
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));
s = QString::number(m_bufferLengthInSecs, 'f', 1);
ui->bufferLenSecsText->setText(tr("%1").arg(s));
s = QString::number((m_bufferGauge < 0 ? -50 - m_bufferGauge : 50 - m_bufferGauge), 'f', 0);
ui->bufferRWBalanceText->setText(tr("%1").arg(s));
ui->bufferGaugeNegative->setValue((m_bufferGauge < 0 ? 50 + m_bufferGauge : 0));
ui->bufferGaugePositive->setValue((m_bufferGauge < 0 ? 0 : 50 - m_bufferGauge));
}
void SDRdaemonGui::updateStatus()
{
int state = m_deviceAPI->state();
if(m_lastEngineState != state)
{
switch(state)
{
case DSPDeviceSourceEngine::StNotStarted:
ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
break;
case DSPDeviceSourceEngine::StIdle:
ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
break;
case DSPDeviceSourceEngine::StRunning:
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
break;
case DSPDeviceSourceEngine::StError:
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage());
break;
default:
break;
}
m_lastEngineState = state;
}
}
void SDRdaemonGui::tick()
{
if ((++m_tickCount & 0xf) == 0) {
SDRdaemonInput::MsgConfigureSDRdaemonStreamTiming* message = SDRdaemonInput::MsgConfigureSDRdaemonStreamTiming::create();
m_sampleSource->getInputMessageQueue()->push(message);
}
}

View File

@ -1,137 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <plugin/plugininstanceui.h>
#include <QTimer>
#include <QWidget>
#include <sys/time.h>
#include "sdrdaemoninput.h"
class DeviceSourceAPI;
class FileRecord;
namespace Ui {
class SDRdaemonGui;
}
class SDRdaemonGui : public QWidget, public PluginInstanceUI {
Q_OBJECT
public:
explicit SDRdaemonGui(DeviceSourceAPI *deviceAPI, 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;
DeviceSourceAPI* m_deviceAPI;
QTimer m_updateTimer;
QTimer m_statusTimer;
DeviceSampleSource* m_sampleSource;
bool m_acquisition;
FileRecord *m_fileSink; //!< File sink to record device I/Q output
int m_deviceSampleRate;
quint64 m_deviceCenterFrequency; //!< Center frequency in device
int m_lastEngineState;
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;
float m_bufferLengthInSecs;
int32_t m_bufferGauge;
int m_samplesCount;
std::size_t m_tickCount;
QString m_address;
QString m_remoteAddress;
quint16 m_dataPort;
quint16 m_controlPort;
bool m_addressEdited;
bool m_dataPortEdited;
bool m_initSendConfiguration;
int m_sender;
bool m_dcBlock;
bool m_iqCorrection;
bool m_autoFollowRate;
bool m_autoCorrBuffer;
void displaySettings();
void displayConfigurationParameters(uint32_t freq,
uint32_t log2Decim,
uint32_t fcPos,
uint32_t sampleRate,
QString& specParms);
void displayTime();
void configureUDPLink();
void configureAutoCorrections();
void configureAutoFollowPolicy();
void updateWithAcquisition();
void updateWithStreamData();
void updateWithStreamTime();
void sendConfiguration();
void updateSampleRateAndFrequency();
private slots:
void handleDSPMessages();
void handleSourceMessages();
void on_applyButton_clicked(bool checked);
void on_dcOffset_toggled(bool checked);
void on_iqImbalance_toggled(bool checked);
void on_autoFollowRate_toggled(bool checked);
void on_autoCorrBuffer_toggled(bool checked);
void on_resetIndexes_clicked(bool checked);
void on_address_textEdited(const QString& arg1);
void on_dataPort_textEdited(const QString& arg1);
void on_controlPort_textEdited(const QString& arg1);
void on_sendButton_clicked(bool checked);
void on_freq_textEdited(const QString& arg1);
void on_sampleRate_textEdited(const QString& arg1);
void on_specificParms_textEdited(const QString& arg1);
void on_decim_currentIndexChanged(int index);
void on_fcPos_currentIndexChanged(int index);
void on_startStop_toggled(bool checked);
void on_record_toggled(bool checked);
void updateStatus();
void tick();
};
#endif // INCLUDE_SDRDAEMONGUI_H

File diff suppressed because it is too large Load Diff

View File

@ -1,167 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "sdrdaemongui.h"
#include "sdrdaemoninput.h"
#include <device/devicesourceapi.h>
#include <dsp/filerecord.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::MsgConfigureSDRdaemonAutoFollowPolicy, Message)
MESSAGE_CLASS_DEFINITION(SDRdaemonInput::MsgConfigureSDRdaemonResetIndexes, 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, DeviceSourceAPI *deviceAPI) :
m_deviceAPI(deviceAPI),
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_autoFollowRate(false),
m_autoCorrBuffer(false)
{
m_sampleFifo.setSize(96000 * 4);
m_SDRdaemonUDPHandler = new SDRdaemonUDPHandler(&m_sampleFifo, getOutputMessageQueueToGUI(), m_deviceAPI);
m_SDRdaemonUDPHandler->connectTimer(&m_masterTimer);
}
SDRdaemonInput::~SDRdaemonInput()
{
stop();
delete m_SDRdaemonUDPHandler;
}
bool SDRdaemonInput::start()
{
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;
}
void SDRdaemonInput::getRemoteAddress(QString &s)
{
if (m_SDRdaemonUDPHandler) {
m_SDRdaemonUDPHandler->getRemoteAddress(s);
}
}
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();
m_deviceAPI->configureCorrections(dcBlock, iqImbalance);
return true;
}
else if (MsgConfigureSDRdaemonAutoFollowPolicy::match(message))
{
MsgConfigureSDRdaemonAutoFollowPolicy& conf = (MsgConfigureSDRdaemonAutoFollowPolicy&) message;
bool autoFollowRate = conf.autoFollowRate();
bool autoCorrBuffer = conf.autoCorrBuffer();
if (autoFollowRate != m_autoFollowRate) {
m_SDRdaemonUDPHandler->setAutoFollowRate(autoFollowRate);
m_autoFollowRate = autoFollowRate;
}
if (autoCorrBuffer != m_autoCorrBuffer) {
m_SDRdaemonUDPHandler->setAutoCorrBuffer(autoCorrBuffer);
m_autoCorrBuffer = autoCorrBuffer;
}
return true;
}
else if (MsgConfigureSDRdaemonResetIndexes::match(message))
{
m_SDRdaemonUDPHandler->resetIndexes();
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

@ -1,306 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/devicesamplesource.h>
#include <QString>
#include <QTimer>
#include <ctime>
#include <iostream>
#include <stdint.h>
class DeviceSourceAPI;
class SDRdaemonUDPHandler;
class SDRdaemonInput : public DeviceSampleSource {
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 MsgConfigureSDRdaemonAutoFollowPolicy : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool autoFollowRate() const { return m_autoFollowRate; }
bool autoCorrBuffer() const { return m_autoCorrBuffer; }
static MsgConfigureSDRdaemonAutoFollowPolicy* create(bool autoFollowRate, bool autoCorrBuffer)
{
return new MsgConfigureSDRdaemonAutoFollowPolicy(autoFollowRate, autoCorrBuffer);
}
private:
bool m_autoFollowRate;
bool m_autoCorrBuffer;
MsgConfigureSDRdaemonAutoFollowPolicy(bool autoFollowRate, bool autoCorrBuffer) :
Message(),
m_autoFollowRate(autoFollowRate),
m_autoCorrBuffer(autoCorrBuffer)
{ }
};
class MsgConfigureSDRdaemonResetIndexes : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgConfigureSDRdaemonResetIndexes* create()
{
return new MsgConfigureSDRdaemonResetIndexes();
}
private:
MsgConfigureSDRdaemonResetIndexes() :
Message()
{ }
};
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; }
float getBufferLengthInSecs() const { return m_bufferLenSec; }
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; }
int32_t getBufferGauge() const { return m_bufferGauge; }
static MsgReportSDRdaemonStreamTiming* create(uint32_t tv_sec,
uint32_t tv_usec,
bool syncLock,
uint32_t frameSize,
float bufferLenSec,
bool lz4,
float compressionRatio,
uint32_t nbLz4CRCOK,
uint32_t nbLz4SuccessfulDecodes,
int32_t bufferGauge)
{
return new MsgReportSDRdaemonStreamTiming(tv_sec,
tv_usec,
syncLock,
frameSize,
bufferLenSec,
lz4,
compressionRatio,
nbLz4CRCOK,
nbLz4SuccessfulDecodes,
bufferGauge);
}
protected:
uint32_t m_tv_sec;
uint32_t m_tv_usec;
bool m_syncLock;
uint32_t m_frameSize;
float m_bufferLenSec;
bool m_lz4;
float m_compressionRatio;
uint32_t m_nbLz4CRCOK;
uint32_t m_nbLz4SuccessfulDecodes;
int32_t m_bufferGauge;
MsgReportSDRdaemonStreamTiming(uint32_t tv_sec,
uint32_t tv_usec,
bool syncLock,
uint32_t frameSize,
float bufferLenSec,
bool lz4,
float compressionRatio,
uint32_t nbLz4CRCOK,
uint32_t nbLz4SuccessfulDecodes,
int32_t bufferGauge) :
Message(),
m_tv_sec(tv_sec),
m_tv_usec(tv_usec),
m_syncLock(syncLock),
m_frameSize(frameSize),
m_bufferLenSec(bufferLenSec),
m_lz4(lz4),
m_compressionRatio(compressionRatio),
m_nbLz4CRCOK(nbLz4CRCOK),
m_nbLz4SuccessfulDecodes(nbLz4SuccessfulDecodes),
m_bufferGauge(bufferGauge)
{ }
};
SDRdaemonInput(const QTimer& masterTimer, DeviceSourceAPI *deviceAPI);
virtual ~SDRdaemonInput();
virtual bool start();
virtual void stop();
virtual const QString& getDeviceDescription() const;
virtual int getSampleRate() const;
virtual quint64 getCenterFrequency() const;
std::time_t getStartingTimeStamp() const;
void getRemoteAddress(QString &s);
virtual bool handleMessage(const Message& message);
private:
DeviceSourceAPI *m_deviceAPI;
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;
bool m_autoFollowRate;
bool m_autoCorrBuffer;
};
#endif // INCLUDE_SDRDAEMONINPUT_H

View File

@ -1,84 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// 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"
#include <device/devicesourceapi.h>
const PluginDescriptor SDRdaemonPlugin::m_pluginDescriptor = {
QString("SDRdaemon input"),
QString("3.5.0"),
QString("(c) Edouard Griffiths, F4EXB"),
QString("https://github.com/f4exb/sdrangel"),
true,
QString("https://github.com/f4exb/sdrangel")
};
const QString SDRdaemonPlugin::m_hardwareID = "SDRdaemon";
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)
{
pluginAPI->registerSampleSource(m_deviceTypeID, this);
}
PluginInterface::SamplingDevices SDRdaemonPlugin::enumSampleSources()
{
SamplingDevices result;
int count = 1;
for(int i = 0; i < count; i++)
{
QString displayedName(QString("SDRdaemon[%1]").arg(i));
result.append(SamplingDevice(displayedName,
m_hardwareID,
m_deviceTypeID,
QString::null,
i));
}
return result;
}
PluginInstanceUI* SDRdaemonPlugin::createSampleSourcePluginInstanceUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI)
{
if(sourceId == m_deviceTypeID)
{
SDRdaemonGui* gui = new SDRdaemonGui(deviceAPI);
*widget = gui;
return gui;
}
else
{
return NULL;
}
}

View File

@ -1,48 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_SDRDAEMONPLUGIN_H
#define INCLUDE_SDRDAEMONPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
#define SDRDAEMON_DEVICE_TYPE_ID "sdrangel.samplesource.sdrdaemon"
class PluginAPI;
class SDRdaemonPlugin : public QObject, public PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID SDRDAEMON_DEVICE_TYPE_ID)
public:
explicit SDRdaemonPlugin(QObject* parent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual SamplingDevices enumSampleSources();
virtual PluginInstanceUI* createSampleSourcePluginInstanceUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI);
static const QString m_hardwareID;
static const QString m_deviceTypeID;
private:
static const PluginDescriptor m_pluginDescriptor;
};
#endif // INCLUDE_SDRDAEMONPLUGIN_H

View File

@ -1,263 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <device/devicesourceapi.h>
#include "sdrdaemoninput.h"
SDRdaemonUDPHandler::SDRdaemonUDPHandler(SampleSinkFifo *sampleFifo, MessageQueue *outputMessageQueueToGUI, DeviceSourceAPI *devieAPI) :
m_deviceAPI(devieAPI),
m_sdrDaemonBuffer(m_rateDivider),
m_dataSocket(0),
m_dataAddress(QHostAddress::LocalHost),
m_remoteAddress(QHostAddress::LocalHost),
m_dataPort(9090),
m_dataConnected(false),
m_udpBuf(0),
m_udpReadBytes(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_throttlems(SDRDAEMON_THROTTLE_MS),
m_readLengthSamples(0),
m_readLength(0),
m_throttleToggle(false),
m_rateDivider(1000/SDRDAEMON_THROTTLE_MS),
m_autoCorrBuffer(false)
{
m_udpBuf = new char[SDRdaemonBuffer::m_udpPayloadSize];
}
SDRdaemonUDPHandler::~SDRdaemonUDPHandler()
{
stop();
delete[] m_udpBuf;
#ifdef USE_INTERNAL_TIMER
if (m_timer) {
delete m_timer;
}
#endif
}
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
m_deviceAPI->getDeviceInputMessageQueue()->push(notif);
m_elapsedTimer.start();
}
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, &m_remoteAddress, 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))
{
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
m_deviceAPI->getDeviceInputMessageQueue()->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;
}
void SDRdaemonUDPHandler::connectTimer(const QTimer* timer)
{
qDebug() << "SDRdaemonUDPHandler::connectTimer";
#ifdef USE_INTERNAL_TIMER
#warning "Uses internal timer"
m_timer = new QTimer();
m_timer->start(50);
m_throttlems = m_timer->interval();
connect(m_timer, SIGNAL(timeout()), this, SLOT(tick()));
#else
m_throttlems = timer->interval();
connect(timer, SIGNAL(timeout()), this, SLOT(tick()));
#endif
m_rateDivider = 1000 / m_throttlems;
}
void SDRdaemonUDPHandler::tick()
{
// auto throttling
int throttlems = m_elapsedTimer.restart();
if (throttlems != m_throttlems)
{
m_throttlems = throttlems;
m_readLengthSamples = (m_sdrDaemonBuffer.getSampleRate() * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000;
m_throttleToggle = !m_throttleToggle;
}
if (m_autoCorrBuffer) {
m_readLengthSamples += m_sdrDaemonBuffer.getRWBalanceCorrection();
}
m_readLength = m_readLengthSamples * SDRdaemonBuffer::m_iqSampleSize;
// read samples directly feeding the SampleFifo (no callback)
m_sampleFifo->write(reinterpret_cast<quint8*>(m_sdrDaemonBuffer.readData(m_readLength)), m_readLength);
m_samplesCount += m_readLengthSamples;
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.getBufferLengthInSecs(),
m_sdrDaemonBuffer.isLz4Compressed(),
m_sdrDaemonBuffer.getCompressionRatio(),
m_sdrDaemonBuffer.getLz4DataCRCOK(),
m_sdrDaemonBuffer.getLz4SuccessfulDecodes(),
m_sdrDaemonBuffer.getBufferGauge());
m_outputMessageQueueToGUI->push(report);
}
}

View File

@ -1,89 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QElapsedTimer>
#include "sdrdaemonbuffer.h"
#define SDRDAEMON_THROTTLE_MS 50
class SampleSinkFifo;
class MessageQueue;
class QTimer;
class DeviceSourceAPI;
class SDRdaemonUDPHandler : public QObject
{
Q_OBJECT
public:
SDRdaemonUDPHandler(SampleSinkFifo* sampleFifo, MessageQueue *outputMessageQueueToGUI, DeviceSourceAPI *deviceAPI);
~SDRdaemonUDPHandler();
void connectTimer(const QTimer* timer);
void start();
void stop();
void configureUDPLink(const QString& address, quint16 port);
void getRemoteAddress(QString& s) const { s = m_remoteAddress.toString(); }
void setAutoFollowRate(bool autoFollowRate) { m_sdrDaemonBuffer.setAutoFollowRate(autoFollowRate); }
void setAutoCorrBuffer(bool autoCorrBuffer) { m_autoCorrBuffer = autoCorrBuffer; m_sdrDaemonBuffer.setAutoCorrBuffer(autoCorrBuffer); }
void resetIndexes() { m_sdrDaemonBuffer.setResetIndexes(); }
public slots:
void dataReadyRead();
private:
DeviceSourceAPI *m_deviceAPI;
SDRdaemonBuffer m_sdrDaemonBuffer;
QUdpSocket *m_dataSocket;
QHostAddress m_dataAddress;
QHostAddress m_remoteAddress;
quint16 m_dataPort;
bool m_dataConnected;
char *m_udpBuf;
qint64 m_udpReadBytes;
SampleSinkFifo *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;
QTimer *m_timer;
QElapsedTimer m_elapsedTimer;
int m_throttlems;
uint32_t m_readLengthSamples;
uint32_t m_readLength;
bool m_throttleToggle;
uint32_t m_rateDivider;
bool m_autoCorrBuffer;
void setSamplerate(uint32_t samplerate);
void processData();
private slots:
void tick();
};
#endif /* PLUGINS_SAMPLESOURCE_SDRDAEMON_SDRDAEMONUDPHANDLER_H_ */