kopia lustrzana https://github.com/f4exb/sdrangel
				
				
				
			DaemonSink (1)
							rodzic
							
								
									dc51f96b3f
								
							
						
					
					
						commit
						96e7d49fbe
					
				| 
						 | 
				
			
			@ -25,6 +25,12 @@ if (FFMPEG_FOUND)
 | 
			
		|||
	endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
find_package(CM256cc)
 | 
			
		||||
if(CM256CC_FOUND)
 | 
			
		||||
    add_subdirectory(daemonsink)
 | 
			
		||||
endif(CM256CC_FOUND)
 | 
			
		||||
 | 
			
		||||
if (BUILD_DEBIAN)
 | 
			
		||||
    add_subdirectory(demoddsd)
 | 
			
		||||
    add_subdirectory(daemonsink)
 | 
			
		||||
endif (BUILD_DEBIAN)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
project(daemonsink)
 | 
			
		||||
 | 
			
		||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
 | 
			
		||||
 | 
			
		||||
set(daemonsink_SOURCES
 | 
			
		||||
	daemonsink.cpp
 | 
			
		||||
#	daemonsinkgui.cpp
 | 
			
		||||
	daemonsinksettings.cpp
 | 
			
		||||
	daemonsinkthread.cpp
 | 
			
		||||
#	daemonsinkplugin.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set(daemonsink_HEADERS
 | 
			
		||||
	daemonsink.h
 | 
			
		||||
#	daemonsinkgui.h
 | 
			
		||||
	daemonsinksettings.h
 | 
			
		||||
	daemonsinkthread.h
 | 
			
		||||
#	daemonsinkplugin.h
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set(daemonsink_FORMS
 | 
			
		||||
	daemonsinkgui.ui
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
include_directories(
 | 
			
		||||
	.
 | 
			
		||||
	${CMAKE_CURRENT_BINARY_DIR}
 | 
			
		||||
	${CMAKE_SOURCE_DIR}/sdrdaemon
 | 
			
		||||
	${CM256CC_INCLUDE_DIR}
 | 
			
		||||
    ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client		
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
#include(${QT_USE_FILE})
 | 
			
		||||
add_definitions(${QT_DEFINITIONS})
 | 
			
		||||
add_definitions(-DQT_PLUGIN)
 | 
			
		||||
add_definitions(-DQT_SHARED)
 | 
			
		||||
 | 
			
		||||
qt5_wrap_ui(daemonsink_FORMS_HEADERS ${daemonsink_FORMS})
 | 
			
		||||
 | 
			
		||||
add_library(daemonsink SHARED
 | 
			
		||||
	${daemonsink_SOURCES}
 | 
			
		||||
	${daemonsink_HEADERS_MOC}
 | 
			
		||||
	${daemonsink_FORMS_HEADERS}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(daemonsink
 | 
			
		||||
	${QT_LIBRARIES}
 | 
			
		||||
	${CM256CC_LIBRARIES}
 | 
			
		||||
	sdrbase
 | 
			
		||||
	sdrdaemon
 | 
			
		||||
	sdrgui
 | 
			
		||||
	swagger
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(daemonsink Qt5::Core Qt5::Widgets)
 | 
			
		||||
 | 
			
		||||
install(TARGETS daemonsink DESTINATION lib/plugins/channelrx)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,400 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2018 Edouard Griffiths, F4EXB.                                  //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// SDRdaemon sink channel (Rx)                                                   //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// SDRdaemon is a detached SDR front end that handles the interface with a       //
 | 
			
		||||
// physical device and sends or receives the I/Q samples stream to or from a     //
 | 
			
		||||
// SDRangel instance via UDP. It is controlled via a Web REST API.               //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// 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 <sys/time.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <boost/crc.hpp>
 | 
			
		||||
#include <boost/cstdint.hpp>
 | 
			
		||||
 | 
			
		||||
#include "SWGChannelSettings.h"
 | 
			
		||||
 | 
			
		||||
#include "util/simpleserializer.h"
 | 
			
		||||
#include "dsp/threadedbasebandsamplesink.h"
 | 
			
		||||
#include "dsp/downchannelizer.h"
 | 
			
		||||
#include "dsp/dspcommands.h"
 | 
			
		||||
#include "device/devicesourceapi.h"
 | 
			
		||||
#include "daemonsinkthread.h"
 | 
			
		||||
#include "daemonsink.h"
 | 
			
		||||
 | 
			
		||||
MESSAGE_CLASS_DEFINITION(DaemonSink::MsgConfigureDaemonSink, Message)
 | 
			
		||||
 | 
			
		||||
const QString DaemonSink::m_channelIdURI = "sdrangel.channel.daemonsink";
 | 
			
		||||
const QString DaemonSink::m_channelId = "DaemonSink";
 | 
			
		||||
 | 
			
		||||
DaemonSink::DaemonSink(DeviceSourceAPI *deviceAPI) :
 | 
			
		||||
        ChannelSinkAPI(m_channelIdURI),
 | 
			
		||||
        m_deviceAPI(deviceAPI),
 | 
			
		||||
        m_running(false),
 | 
			
		||||
        m_sinkThread(0),
 | 
			
		||||
        m_txBlockIndex(0),
 | 
			
		||||
        m_frameCount(0),
 | 
			
		||||
        m_sampleIndex(0),
 | 
			
		||||
        m_dataBlock(0),
 | 
			
		||||
        m_centerFrequency(0),
 | 
			
		||||
        m_sampleRate(48000),
 | 
			
		||||
        m_sampleBytes(SDR_RX_SAMP_SZ == 24 ? 4 : 2),
 | 
			
		||||
        m_nbBlocksFEC(0),
 | 
			
		||||
        m_txDelay(100),
 | 
			
		||||
        m_dataAddress("127.0.0.1"),
 | 
			
		||||
        m_dataPort(9090)
 | 
			
		||||
{
 | 
			
		||||
    setObjectName(m_channelId);
 | 
			
		||||
 | 
			
		||||
    m_channelizer = new DownChannelizer(this);
 | 
			
		||||
    m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this);
 | 
			
		||||
    m_deviceAPI->addThreadedSink(m_threadedChannelizer);
 | 
			
		||||
    m_deviceAPI->addChannelAPI(this);
 | 
			
		||||
 | 
			
		||||
    m_cm256p = m_cm256.isInitialized() ? &m_cm256 : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DaemonSink::~DaemonSink()
 | 
			
		||||
{
 | 
			
		||||
    m_dataBlockMutex.lock();
 | 
			
		||||
    if (m_dataBlock && !m_dataBlock->m_txControlBlock.m_complete) {
 | 
			
		||||
        delete m_dataBlock;
 | 
			
		||||
    }
 | 
			
		||||
    m_dataBlockMutex.unlock();
 | 
			
		||||
    m_deviceAPI->removeChannelAPI(this);
 | 
			
		||||
    m_deviceAPI->removeThreadedSink(m_threadedChannelizer);
 | 
			
		||||
    delete m_threadedChannelizer;
 | 
			
		||||
    delete m_channelizer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSink::setTxDelay(int txDelay)
 | 
			
		||||
{
 | 
			
		||||
    qDebug() << "DaemonSink::setTxDelay: txDelay: " << txDelay;
 | 
			
		||||
    m_txDelay = txDelay;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSink::setNbBlocksFEC(int nbBlocksFEC)
 | 
			
		||||
{
 | 
			
		||||
    qDebug() << "DaemonSink::setNbBlocksFEC: nbBlocksFEC: " << nbBlocksFEC;
 | 
			
		||||
    m_nbBlocksFEC = nbBlocksFEC;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst __attribute__((unused)))
 | 
			
		||||
{
 | 
			
		||||
    SampleVector::const_iterator it = begin;
 | 
			
		||||
 | 
			
		||||
    while (it != end)
 | 
			
		||||
    {
 | 
			
		||||
        int inSamplesIndex = it - begin;
 | 
			
		||||
        int inRemainingSamples = end - it;
 | 
			
		||||
 | 
			
		||||
        if (m_txBlockIndex == 0)
 | 
			
		||||
        {
 | 
			
		||||
            struct timeval tv;
 | 
			
		||||
            SDRDaemonMetaDataFEC metaData;
 | 
			
		||||
            gettimeofday(&tv, 0);
 | 
			
		||||
 | 
			
		||||
            metaData.m_centerFrequency = m_centerFrequency;
 | 
			
		||||
            metaData.m_sampleRate = m_sampleRate;
 | 
			
		||||
            metaData.m_sampleBytes = m_sampleBytes;
 | 
			
		||||
            metaData.m_sampleBits = 0; // TODO: deprecated
 | 
			
		||||
            metaData.m_nbOriginalBlocks = SDRDaemonNbOrginalBlocks;
 | 
			
		||||
            metaData.m_nbFECBlocks = m_nbBlocksFEC;
 | 
			
		||||
            metaData.m_tv_sec = tv.tv_sec;
 | 
			
		||||
            metaData.m_tv_usec = tv.tv_usec;
 | 
			
		||||
 | 
			
		||||
            if (!m_dataBlock) { // on the very first cycle there is no data block allocated
 | 
			
		||||
                m_dataBlock = new SDRDaemonDataBlock();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            boost::crc_32_type crc32;
 | 
			
		||||
            crc32.process_bytes(&metaData, 20);
 | 
			
		||||
            metaData.m_crc32 = crc32.checksum();
 | 
			
		||||
            SDRDaemonSuperBlock& superBlock = m_dataBlock->m_superBlocks[0]; // first block
 | 
			
		||||
            superBlock.init();
 | 
			
		||||
            superBlock.m_header.m_frameIndex = m_frameCount;
 | 
			
		||||
            superBlock.m_header.m_blockIndex = m_txBlockIndex;
 | 
			
		||||
            memcpy((void *) &superBlock.m_protectedBlock, (const void *) &metaData, sizeof(SDRDaemonMetaDataFEC));
 | 
			
		||||
 | 
			
		||||
            if (!(metaData == m_currentMetaFEC))
 | 
			
		||||
            {
 | 
			
		||||
                qDebug() << "SDRDaemonChannelSink::feed: meta: "
 | 
			
		||||
                        << "|" << metaData.m_centerFrequency
 | 
			
		||||
                        << ":" << metaData.m_sampleRate
 | 
			
		||||
                        << ":" << (int) (metaData.m_sampleBytes & 0xF)
 | 
			
		||||
                        << ":" << (int) metaData.m_sampleBits
 | 
			
		||||
                        << "|" << (int) metaData.m_nbOriginalBlocks
 | 
			
		||||
                        << ":" << (int) metaData.m_nbFECBlocks
 | 
			
		||||
                        << "|" << metaData.m_tv_sec
 | 
			
		||||
                        << ":" << metaData.m_tv_usec;
 | 
			
		||||
 | 
			
		||||
                m_currentMetaFEC = metaData;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            m_txBlockIndex = 1; // next Tx block with data
 | 
			
		||||
        } // block zero
 | 
			
		||||
 | 
			
		||||
        // TODO: handle different sample sizes...
 | 
			
		||||
        if (m_sampleIndex + inRemainingSamples < SDRDaemonSamplesPerBlock) // there is still room in the current super block
 | 
			
		||||
        {
 | 
			
		||||
            memcpy((void *) &m_superBlock.m_protectedBlock.m_samples[m_sampleIndex],
 | 
			
		||||
                    (const void *) &(*(begin+inSamplesIndex)),
 | 
			
		||||
                    inRemainingSamples * sizeof(Sample));
 | 
			
		||||
            m_sampleIndex += inRemainingSamples;
 | 
			
		||||
            it = end; // all input samples are consumed
 | 
			
		||||
        }
 | 
			
		||||
        else // complete super block and initiate the next if not end of frame
 | 
			
		||||
        {
 | 
			
		||||
            memcpy((void *) &m_superBlock.m_protectedBlock.m_samples[m_sampleIndex],
 | 
			
		||||
                    (const void *) &(*(begin+inSamplesIndex)),
 | 
			
		||||
                    (SDRDaemonSamplesPerBlock - m_sampleIndex) * sizeof(Sample));
 | 
			
		||||
            it += SDRDaemonSamplesPerBlock - m_sampleIndex;
 | 
			
		||||
            m_sampleIndex = 0;
 | 
			
		||||
 | 
			
		||||
            m_superBlock.m_header.m_frameIndex = m_frameCount;
 | 
			
		||||
            m_superBlock.m_header.m_blockIndex = m_txBlockIndex;
 | 
			
		||||
            m_dataBlock->m_superBlocks[m_txBlockIndex] = m_superBlock;
 | 
			
		||||
 | 
			
		||||
            if (m_txBlockIndex == SDRDaemonNbOrginalBlocks - 1) // frame complete
 | 
			
		||||
            {
 | 
			
		||||
                m_dataBlockMutex.lock();
 | 
			
		||||
                m_dataBlock->m_txControlBlock.m_frameIndex = m_frameCount;
 | 
			
		||||
                m_dataBlock->m_txControlBlock.m_processed = false;
 | 
			
		||||
                m_dataBlock->m_txControlBlock.m_complete = true;
 | 
			
		||||
                m_dataBlock->m_txControlBlock.m_nbBlocksFEC = m_nbBlocksFEC;
 | 
			
		||||
                m_dataBlock->m_txControlBlock.m_txDelay = m_txDelay;
 | 
			
		||||
                m_dataBlock->m_txControlBlock.m_dataAddress = m_dataAddress;
 | 
			
		||||
                m_dataBlock->m_txControlBlock.m_dataPort = m_dataPort;
 | 
			
		||||
 | 
			
		||||
                m_dataQueue.push(m_dataBlock);
 | 
			
		||||
                m_dataBlock = new SDRDaemonDataBlock(); // create a new one immediately
 | 
			
		||||
                m_dataBlockMutex.unlock();
 | 
			
		||||
 | 
			
		||||
                m_txBlockIndex = 0;
 | 
			
		||||
                m_frameCount++;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                m_txBlockIndex++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSink::start()
 | 
			
		||||
{
 | 
			
		||||
    qDebug("DaemonSink::start");
 | 
			
		||||
 | 
			
		||||
    memset((void *) &m_currentMetaFEC, 0, sizeof(SDRDaemonMetaDataFEC));
 | 
			
		||||
 | 
			
		||||
    if (m_running) {
 | 
			
		||||
        stop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_sinkThread = new DaemonSinkThread(&m_dataQueue, m_cm256p);
 | 
			
		||||
    m_sinkThread->startStop(true);
 | 
			
		||||
    m_running = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSink::stop()
 | 
			
		||||
{
 | 
			
		||||
    qDebug("DaemonSink::stop");
 | 
			
		||||
 | 
			
		||||
    if (m_sinkThread != 0)
 | 
			
		||||
    {
 | 
			
		||||
        m_sinkThread->startStop(false);
 | 
			
		||||
        m_sinkThread->deleteLater();
 | 
			
		||||
        m_sinkThread = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_running = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DaemonSink::handleMessage(const Message& cmd __attribute__((unused)))
 | 
			
		||||
{
 | 
			
		||||
	if (DownChannelizer::MsgChannelizerNotification::match(cmd))
 | 
			
		||||
	{
 | 
			
		||||
		DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd;
 | 
			
		||||
 | 
			
		||||
        qDebug() << "DaemonSink::handleMessage: MsgChannelizerNotification:"
 | 
			
		||||
                << " channelSampleRate: " << notif.getSampleRate()
 | 
			
		||||
                << " offsetFrequency: " << notif.getFrequencyOffset();
 | 
			
		||||
 | 
			
		||||
        if (notif.getSampleRate() > 0) {
 | 
			
		||||
            setSampleRate(notif.getSampleRate());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
    else if (DSPSignalNotification::match(cmd))
 | 
			
		||||
    {
 | 
			
		||||
        DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
 | 
			
		||||
 | 
			
		||||
        qDebug() << "DaemonSink::handleMessage: DSPSignalNotification:"
 | 
			
		||||
                << " inputSampleRate: " << notif.getSampleRate()
 | 
			
		||||
                << " centerFrequency: " << notif.getCenterFrequency();
 | 
			
		||||
 | 
			
		||||
        setCenterFrequency(notif.getCenterFrequency());
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else if (MsgConfigureDaemonSink::match(cmd))
 | 
			
		||||
    {
 | 
			
		||||
        MsgConfigureDaemonSink& cfg = (MsgConfigureDaemonSink&) cmd;
 | 
			
		||||
        qDebug() << "DaemonSink::handleMessage: MsgConfigureDaemonSink";
 | 
			
		||||
        applySettings(cfg.getSettings(), cfg.getForce());
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QByteArray DaemonSink::serialize() const
 | 
			
		||||
{
 | 
			
		||||
    return m_settings.serialize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DaemonSink::deserialize(const QByteArray& data __attribute__((unused)))
 | 
			
		||||
{
 | 
			
		||||
    if (m_settings.deserialize(data))
 | 
			
		||||
    {
 | 
			
		||||
        MsgConfigureDaemonSink *msg = MsgConfigureDaemonSink::create(m_settings, true);
 | 
			
		||||
        m_inputMessageQueue.push(msg);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        m_settings.resetToDefaults();
 | 
			
		||||
        MsgConfigureDaemonSink *msg = MsgConfigureDaemonSink::create(m_settings, true);
 | 
			
		||||
        m_inputMessageQueue.push(msg);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSink::applySettings(const DaemonSinkSettings& settings, bool force)
 | 
			
		||||
{
 | 
			
		||||
    qDebug() << "DaemonSink::applySettings:"
 | 
			
		||||
            << " m_nbFECBlocks: " << settings.m_nbFECBlocks
 | 
			
		||||
            << " m_txDelay: " << settings.m_txDelay
 | 
			
		||||
            << " m_dataAddress: " << settings.m_dataAddress
 | 
			
		||||
            << " m_dataPort: " << settings.m_dataPort
 | 
			
		||||
            << " force: " << force;
 | 
			
		||||
 | 
			
		||||
    if ((m_settings.m_nbFECBlocks != settings.m_nbFECBlocks) || force) {
 | 
			
		||||
        m_nbBlocksFEC = settings.m_nbFECBlocks;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((m_settings.m_txDelay != settings.m_txDelay) || force) {
 | 
			
		||||
        m_txDelay = settings.m_txDelay;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) {
 | 
			
		||||
        m_dataAddress = settings.m_dataAddress;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((m_settings.m_dataPort != settings.m_dataPort) || force) {
 | 
			
		||||
        m_dataPort = settings.m_dataPort;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_settings = settings;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int DaemonSink::webapiSettingsGet(
 | 
			
		||||
        SWGSDRangel::SWGChannelSettings& response,
 | 
			
		||||
        QString& errorMessage __attribute__((unused)))
 | 
			
		||||
{
 | 
			
		||||
    response.setDaemonSinkSettings(new SWGSDRangel::SWGDaemonSinkSettings());
 | 
			
		||||
    response.getDaemonSinkSettings()->init();
 | 
			
		||||
    webapiFormatChannelSettings(response, m_settings);
 | 
			
		||||
    return 200;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int DaemonSink::webapiSettingsPutPatch(
 | 
			
		||||
        bool force,
 | 
			
		||||
        const QStringList& channelSettingsKeys,
 | 
			
		||||
        SWGSDRangel::SWGChannelSettings& response,
 | 
			
		||||
        QString& errorMessage __attribute__((unused)))
 | 
			
		||||
{
 | 
			
		||||
    DaemonSinkSettings settings = m_settings;
 | 
			
		||||
 | 
			
		||||
    if (channelSettingsKeys.contains("nbFECBlocks"))
 | 
			
		||||
    {
 | 
			
		||||
        int nbFECBlocks = response.getDaemonSinkSettings()->getNbFecBlocks();
 | 
			
		||||
 | 
			
		||||
        if ((nbFECBlocks < 0) || (nbFECBlocks > 127)) {
 | 
			
		||||
            settings.m_nbFECBlocks = 8;
 | 
			
		||||
        } else {
 | 
			
		||||
            settings.m_nbFECBlocks = response.getDaemonSinkSettings()->getNbFecBlocks();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (channelSettingsKeys.contains("txDelay"))
 | 
			
		||||
    {
 | 
			
		||||
        int txDelay = response.getDaemonSinkSettings()->getTxDelay();
 | 
			
		||||
 | 
			
		||||
        if (txDelay < 0) {
 | 
			
		||||
            settings.m_txDelay = 100;
 | 
			
		||||
        } else {
 | 
			
		||||
            settings.m_txDelay = txDelay;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (channelSettingsKeys.contains("dataAddress")) {
 | 
			
		||||
        settings.m_dataAddress = *response.getDaemonSinkSettings()->getDataAddress();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (channelSettingsKeys.contains("dataPort"))
 | 
			
		||||
    {
 | 
			
		||||
        int dataPort = response.getDaemonSinkSettings()->getDataPort();
 | 
			
		||||
 | 
			
		||||
        if ((dataPort < 1024) || (dataPort > 65535)) {
 | 
			
		||||
            settings.m_dataPort = 9090;
 | 
			
		||||
        } else {
 | 
			
		||||
            settings.m_dataPort = dataPort;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MsgConfigureDaemonSink *msg = MsgConfigureDaemonSink::create(settings, force);
 | 
			
		||||
    m_inputMessageQueue.push(msg);
 | 
			
		||||
 | 
			
		||||
    qDebug("DaemonSink::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
 | 
			
		||||
    if (m_guiMessageQueue) // forward to GUI if any
 | 
			
		||||
    {
 | 
			
		||||
        MsgConfigureDaemonSink *msgToGUI = MsgConfigureDaemonSink::create(settings, force);
 | 
			
		||||
        m_guiMessageQueue->push(msgToGUI);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    webapiFormatChannelSettings(response, settings);
 | 
			
		||||
 | 
			
		||||
    return 200;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSink::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const DaemonSinkSettings& settings)
 | 
			
		||||
{
 | 
			
		||||
    response.getDaemonSinkSettings()->setNbFecBlocks(settings.m_nbFECBlocks);
 | 
			
		||||
    response.getDaemonSinkSettings()->setTxDelay(settings.m_txDelay);
 | 
			
		||||
 | 
			
		||||
    if (response.getDaemonSinkSettings()->getDataAddress()) {
 | 
			
		||||
        *response.getDaemonSinkSettings()->getDataAddress() = settings.m_dataAddress;
 | 
			
		||||
    } else {
 | 
			
		||||
        response.getDaemonSinkSettings()->setDataAddress(new QString(settings.m_dataAddress));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    response.getDaemonSinkSettings()->setDataPort(settings.m_dataPort);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,139 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2018 Edouard Griffiths, F4EXB.                                  //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// SDRdaemon sink channel (Rx)                                                   //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// SDRdaemon is a detached SDR front end that handles the interface with a       //
 | 
			
		||||
// physical device and sends or receives the I/Q samples stream to or from a     //
 | 
			
		||||
// SDRangel instance via UDP. It is controlled via a Web REST API.               //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// 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_DAEMONSINK_H_
 | 
			
		||||
#define INCLUDE_DAEMONSINK_H_
 | 
			
		||||
 | 
			
		||||
#include <QMutex>
 | 
			
		||||
 | 
			
		||||
#include "cm256.h"
 | 
			
		||||
 | 
			
		||||
#include "dsp/basebandsamplesink.h"
 | 
			
		||||
#include "channel/channelsinkapi.h"
 | 
			
		||||
#include "channel/sdrdaemondataqueue.h"
 | 
			
		||||
#include "channel/sdrdaemondatablock.h"
 | 
			
		||||
#include "daemonsinksettings.h"
 | 
			
		||||
 | 
			
		||||
class DeviceSourceAPI;
 | 
			
		||||
class ThreadedBasebandSampleSink;
 | 
			
		||||
class DownChannelizer;
 | 
			
		||||
class DaemonSinkThread;
 | 
			
		||||
 | 
			
		||||
class DaemonSink : public BasebandSampleSink, public ChannelSinkAPI {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
public:
 | 
			
		||||
    class MsgConfigureDaemonSink : public Message {
 | 
			
		||||
        MESSAGE_CLASS_DECLARATION
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        const DaemonSinkSettings& getSettings() const { return m_settings; }
 | 
			
		||||
        bool getForce() const { return m_force; }
 | 
			
		||||
 | 
			
		||||
        static MsgConfigureDaemonSink* create(const DaemonSinkSettings& settings, bool force)
 | 
			
		||||
        {
 | 
			
		||||
            return new MsgConfigureDaemonSink(settings, force);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        DaemonSinkSettings m_settings;
 | 
			
		||||
        bool m_force;
 | 
			
		||||
 | 
			
		||||
        MsgConfigureDaemonSink(const DaemonSinkSettings& settings, bool force) :
 | 
			
		||||
            Message(),
 | 
			
		||||
            m_settings(settings),
 | 
			
		||||
            m_force(force)
 | 
			
		||||
        { }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    DaemonSink(DeviceSourceAPI *deviceAPI);
 | 
			
		||||
    virtual ~DaemonSink();
 | 
			
		||||
    virtual void destroy() { delete this; }
 | 
			
		||||
 | 
			
		||||
    virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
 | 
			
		||||
    virtual void start();
 | 
			
		||||
    virtual void stop();
 | 
			
		||||
    virtual bool handleMessage(const Message& cmd);
 | 
			
		||||
 | 
			
		||||
    virtual void getIdentifier(QString& id) { id = objectName(); }
 | 
			
		||||
    virtual void getTitle(QString& title) { title = "SDRDaemon Sink"; }
 | 
			
		||||
    virtual qint64 getCenterFrequency() const { return 0; }
 | 
			
		||||
 | 
			
		||||
    virtual QByteArray serialize() const;
 | 
			
		||||
    virtual bool deserialize(const QByteArray& data);
 | 
			
		||||
 | 
			
		||||
    virtual int webapiSettingsGet(
 | 
			
		||||
            SWGSDRangel::SWGChannelSettings& response,
 | 
			
		||||
            QString& errorMessage);
 | 
			
		||||
 | 
			
		||||
    virtual int webapiSettingsPutPatch(
 | 
			
		||||
            bool force,
 | 
			
		||||
            const QStringList& channelSettingsKeys,
 | 
			
		||||
            SWGSDRangel::SWGChannelSettings& response,
 | 
			
		||||
            QString& errorMessage);
 | 
			
		||||
 | 
			
		||||
    /** Set center frequency given in Hz */
 | 
			
		||||
    void setCenterFrequency(uint64_t centerFrequency) { m_centerFrequency = centerFrequency / 1000; }
 | 
			
		||||
 | 
			
		||||
    /** Set sample rate given in Hz */
 | 
			
		||||
    void setSampleRate(uint32_t sampleRate) { m_sampleRate = sampleRate; }
 | 
			
		||||
 | 
			
		||||
    void setNbBlocksFEC(int nbBlocksFEC);
 | 
			
		||||
    void setTxDelay(int txDelay);
 | 
			
		||||
    void setDataAddress(const QString& address) { m_dataAddress = address; }
 | 
			
		||||
    void setDataPort(uint16_t port) { m_dataPort = port; }
 | 
			
		||||
 | 
			
		||||
    static const QString m_channelIdURI;
 | 
			
		||||
    static const QString m_channelId;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    DeviceSourceAPI *m_deviceAPI;
 | 
			
		||||
    ThreadedBasebandSampleSink* m_threadedChannelizer;
 | 
			
		||||
    DownChannelizer* m_channelizer;
 | 
			
		||||
    bool m_running;
 | 
			
		||||
 | 
			
		||||
    DaemonSinkSettings m_settings;
 | 
			
		||||
    SDRDaemonDataQueue m_dataQueue;
 | 
			
		||||
    DaemonSinkThread *m_sinkThread;
 | 
			
		||||
    CM256 m_cm256;
 | 
			
		||||
    CM256 *m_cm256p;
 | 
			
		||||
 | 
			
		||||
    int m_txBlockIndex;                  //!< Current index in blocks to transmit in the Tx row
 | 
			
		||||
    uint16_t m_frameCount;               //!< transmission frame count
 | 
			
		||||
    int m_sampleIndex;                   //!< Current sample index in protected block data
 | 
			
		||||
    SDRDaemonSuperBlock m_superBlock;
 | 
			
		||||
    SDRDaemonMetaDataFEC m_currentMetaFEC;
 | 
			
		||||
    SDRDaemonDataBlock *m_dataBlock;
 | 
			
		||||
    QMutex m_dataBlockMutex;
 | 
			
		||||
 | 
			
		||||
    uint64_t m_centerFrequency;
 | 
			
		||||
    uint32_t m_sampleRate;
 | 
			
		||||
    uint8_t m_sampleBytes;
 | 
			
		||||
    int m_nbBlocksFEC;
 | 
			
		||||
    int m_txDelay;
 | 
			
		||||
    QString m_dataAddress;
 | 
			
		||||
    uint16_t m_dataPort;
 | 
			
		||||
 | 
			
		||||
    void applySettings(const DaemonSinkSettings& settings, bool force = false);
 | 
			
		||||
    void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const DaemonSinkSettings& settings);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* INCLUDE_DAEMONSINK_H_ */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,300 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<ui version="4.0">
 | 
			
		||||
 <class>DaemonSinkGUI</class>
 | 
			
		||||
 <widget class="RollupWidget" name="DaemonSinkGUI">
 | 
			
		||||
  <property name="geometry">
 | 
			
		||||
   <rect>
 | 
			
		||||
    <x>0</x>
 | 
			
		||||
    <y>0</y>
 | 
			
		||||
    <width>320</width>
 | 
			
		||||
    <height>100</height>
 | 
			
		||||
   </rect>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="sizePolicy">
 | 
			
		||||
   <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
 | 
			
		||||
    <horstretch>0</horstretch>
 | 
			
		||||
    <verstretch>0</verstretch>
 | 
			
		||||
   </sizepolicy>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="minimumSize">
 | 
			
		||||
   <size>
 | 
			
		||||
    <width>320</width>
 | 
			
		||||
    <height>100</height>
 | 
			
		||||
   </size>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="maximumSize">
 | 
			
		||||
   <size>
 | 
			
		||||
    <width>320</width>
 | 
			
		||||
    <height>16777215</height>
 | 
			
		||||
   </size>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="font">
 | 
			
		||||
   <font>
 | 
			
		||||
    <family>Liberation Sans</family>
 | 
			
		||||
    <pointsize>9</pointsize>
 | 
			
		||||
   </font>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="windowTitle">
 | 
			
		||||
   <string>Daemon sink</string>
 | 
			
		||||
  </property>
 | 
			
		||||
  <widget class="QWidget" name="settingsContainer" native="true">
 | 
			
		||||
   <property name="geometry">
 | 
			
		||||
    <rect>
 | 
			
		||||
     <x>10</x>
 | 
			
		||||
     <y>10</y>
 | 
			
		||||
     <width>301</width>
 | 
			
		||||
     <height>81</height>
 | 
			
		||||
    </rect>
 | 
			
		||||
   </property>
 | 
			
		||||
   <property name="windowTitle">
 | 
			
		||||
    <string>Settings</string>
 | 
			
		||||
   </property>
 | 
			
		||||
   <layout class="QVBoxLayout" name="verticalLayout">
 | 
			
		||||
    <property name="spacing">
 | 
			
		||||
     <number>3</number>
 | 
			
		||||
    </property>
 | 
			
		||||
    <property name="leftMargin">
 | 
			
		||||
     <number>2</number>
 | 
			
		||||
    </property>
 | 
			
		||||
    <property name="topMargin">
 | 
			
		||||
     <number>2</number>
 | 
			
		||||
    </property>
 | 
			
		||||
    <property name="rightMargin">
 | 
			
		||||
     <number>2</number>
 | 
			
		||||
    </property>
 | 
			
		||||
    <property name="bottomMargin">
 | 
			
		||||
     <number>2</number>
 | 
			
		||||
    </property>
 | 
			
		||||
    <item>
 | 
			
		||||
     <layout class="QHBoxLayout" name="dataAddressLayout">
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="dataAddressLabel">
 | 
			
		||||
        <property name="minimumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>30</width>
 | 
			
		||||
          <height>0</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Data</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLineEdit" name="dataAddress">
 | 
			
		||||
        <property name="minimumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>120</width>
 | 
			
		||||
          <height>0</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Local data listener address</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="inputMask">
 | 
			
		||||
         <string>000.000.000.000</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>0...</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="dataPortSeparator">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>:</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLineEdit" name="dataPort">
 | 
			
		||||
        <property name="maximumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>50</width>
 | 
			
		||||
          <height>16777215</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Local data listener port</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="inputMask">
 | 
			
		||||
         <string>00000</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>0</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <spacer name="horizontalSpacer">
 | 
			
		||||
        <property name="orientation">
 | 
			
		||||
         <enum>Qt::Horizontal</enum>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="sizeHint" stdset="0">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>40</width>
 | 
			
		||||
          <height>20</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
       </spacer>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QPushButton" name="pushButton">
 | 
			
		||||
        <property name="maximumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>30</width>
 | 
			
		||||
          <height>16777215</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Set local data listener address and port</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Set</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </item>
 | 
			
		||||
    <item>
 | 
			
		||||
     <layout class="QHBoxLayout" name="nominalValuesLayout">
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QDial" name="nbFECBlocks">
 | 
			
		||||
        <property name="maximumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>24</width>
 | 
			
		||||
          <height>24</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Number of FEC blocks per frame</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="maximum">
 | 
			
		||||
         <number>32</number>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="pageStep">
 | 
			
		||||
         <number>1</number>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="value">
 | 
			
		||||
         <number>0</number>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="nominalNbBlocksText">
 | 
			
		||||
        <property name="minimumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>50</width>
 | 
			
		||||
          <height>0</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Nb total blocks / Nb FEC blocks</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>000/00</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="alignment">
 | 
			
		||||
         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="Line" name="line">
 | 
			
		||||
        <property name="orientation">
 | 
			
		||||
         <enum>Qt::Vertical</enum>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="txDelayLabel">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Udly</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QDial" name="txDelay">
 | 
			
		||||
        <property name="maximumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>24</width>
 | 
			
		||||
          <height>24</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Delay between consecutive UDP packets in percentage of nominal UDP packet process time</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="minimum">
 | 
			
		||||
         <number>10</number>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="maximum">
 | 
			
		||||
         <number>90</number>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="pageStep">
 | 
			
		||||
         <number>1</number>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="value">
 | 
			
		||||
         <number>50</number>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="txDelayText">
 | 
			
		||||
        <property name="minimumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>20</width>
 | 
			
		||||
          <height>0</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>90</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="alignment">
 | 
			
		||||
         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <spacer name="horizontalSpacer_3">
 | 
			
		||||
        <property name="orientation">
 | 
			
		||||
         <enum>Qt::Horizontal</enum>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="sizeHint" stdset="0">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>40</width>
 | 
			
		||||
          <height>20</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
       </spacer>
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </item>
 | 
			
		||||
    <item>
 | 
			
		||||
     <spacer name="verticalSpacer">
 | 
			
		||||
      <property name="orientation">
 | 
			
		||||
       <enum>Qt::Vertical</enum>
 | 
			
		||||
      </property>
 | 
			
		||||
      <property name="sizeHint" stdset="0">
 | 
			
		||||
       <size>
 | 
			
		||||
        <width>20</width>
 | 
			
		||||
        <height>40</height>
 | 
			
		||||
       </size>
 | 
			
		||||
      </property>
 | 
			
		||||
     </spacer>
 | 
			
		||||
    </item>
 | 
			
		||||
   </layout>
 | 
			
		||||
  </widget>
 | 
			
		||||
 </widget>
 | 
			
		||||
 <customwidgets>
 | 
			
		||||
  <customwidget>
 | 
			
		||||
   <class>RollupWidget</class>
 | 
			
		||||
   <extends>QWidget</extends>
 | 
			
		||||
   <header>gui/rollupwidget.h</header>
 | 
			
		||||
   <container>1</container>
 | 
			
		||||
  </customwidget>
 | 
			
		||||
 </customwidgets>
 | 
			
		||||
 <resources>
 | 
			
		||||
  <include location="../../../sdrgui/resources/res.qrc"/>
 | 
			
		||||
 </resources>
 | 
			
		||||
 <connections/>
 | 
			
		||||
</ui>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,96 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2018 Edouard Griffiths, F4EXB.                                  //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// SDRdaemon sink channel (Rx) main settings                                     //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// SDRdaemon is a detached SDR front end that handles the interface with a       //
 | 
			
		||||
// physical device and sends or receives the I/Q samples stream to or from a     //
 | 
			
		||||
// SDRangel instance via UDP. It is controlled via a Web REST API.               //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// This program is free software; you can redistribute it and/or modify          //
 | 
			
		||||
// it under the terms of the GNU General Public License as published by          //
 | 
			
		||||
// the Free Software Foundation as version 3 of the License, or                  //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// This program is distributed in the hope that it will be useful,               //
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of                //
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
 | 
			
		||||
// GNU General Public License V3 for more details.                               //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// You should have received a copy of the GNU General Public License             //
 | 
			
		||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
#include "util/simpleserializer.h"
 | 
			
		||||
#include "settings/serializable.h"
 | 
			
		||||
#include "daemonsinksettings.h"
 | 
			
		||||
 | 
			
		||||
DaemonSinkSettings::DaemonSinkSettings()
 | 
			
		||||
{
 | 
			
		||||
    resetToDefaults();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSinkSettings::resetToDefaults()
 | 
			
		||||
{
 | 
			
		||||
    m_nbFECBlocks = 0;
 | 
			
		||||
    m_txDelay = 100;
 | 
			
		||||
    m_dataAddress = "127.0.0.1";
 | 
			
		||||
    m_dataPort = 9090;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QByteArray DaemonSinkSettings::serialize() const
 | 
			
		||||
{
 | 
			
		||||
    SimpleSerializer s(1);
 | 
			
		||||
    s.writeU32(1, m_nbFECBlocks);
 | 
			
		||||
    s.writeU32(2, m_txDelay);
 | 
			
		||||
    s.writeString(3, m_dataAddress);
 | 
			
		||||
    s.writeU32(4, m_dataPort);
 | 
			
		||||
 | 
			
		||||
    return s.final();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DaemonSinkSettings::deserialize(const QByteArray& data)
 | 
			
		||||
{
 | 
			
		||||
    SimpleDeserializer d(data);
 | 
			
		||||
 | 
			
		||||
    if(!d.isValid())
 | 
			
		||||
    {
 | 
			
		||||
        resetToDefaults();
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(d.getVersion() == 1)
 | 
			
		||||
    {
 | 
			
		||||
        uint32_t tmp;
 | 
			
		||||
        QString strtmp;
 | 
			
		||||
 | 
			
		||||
        d.readU32(1, &tmp, 0);
 | 
			
		||||
 | 
			
		||||
        if (tmp < 128) {
 | 
			
		||||
            m_nbFECBlocks = tmp;
 | 
			
		||||
        } else {
 | 
			
		||||
            m_nbFECBlocks = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        d.readU32(2, &m_txDelay, 100);
 | 
			
		||||
        d.readString(3, &m_dataAddress, "127.0.0.1");
 | 
			
		||||
        d.readU32(4, &tmp, 0);
 | 
			
		||||
 | 
			
		||||
        if ((tmp > 1023) && (tmp < 65535)) {
 | 
			
		||||
            m_dataPort = tmp;
 | 
			
		||||
        } else {
 | 
			
		||||
            m_dataPort = 9090;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        resetToDefaults();
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2018 Edouard Griffiths, F4EXB.                                  //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// SDRdaemon sink channel (Rx) main settings                                     //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// SDRdaemon is a detached SDR front end that handles the interface with a       //
 | 
			
		||||
// physical device and sends or receives the I/Q samples stream to or from a     //
 | 
			
		||||
// SDRangel instance via UDP. It is controlled via a Web REST API.               //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// 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_SDRDAEMONCHANNELSINKSETTINGS_H_
 | 
			
		||||
#define INCLUDE_SDRDAEMONCHANNELSINKSETTINGS_H_
 | 
			
		||||
 | 
			
		||||
#include <QByteArray>
 | 
			
		||||
 | 
			
		||||
class Serializable;
 | 
			
		||||
 | 
			
		||||
struct DaemonSinkSettings
 | 
			
		||||
{
 | 
			
		||||
    uint16_t m_nbFECBlocks;
 | 
			
		||||
    uint32_t m_txDelay;
 | 
			
		||||
    QString  m_dataAddress;
 | 
			
		||||
    uint16_t m_dataPort;
 | 
			
		||||
 | 
			
		||||
    DaemonSinkSettings();
 | 
			
		||||
    void resetToDefaults();
 | 
			
		||||
    QByteArray serialize() const;
 | 
			
		||||
    bool deserialize(const QByteArray& data);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* INCLUDE_SDRDAEMONCHANNELSINKSETTINGS_H_ */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,198 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2018 Edouard Griffiths, F4EXB.                                  //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// SDRdaemon sink channel (Rx) UDP sender thread                                 //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// SDRdaemon is a detached SDR front end that handles the interface with a       //
 | 
			
		||||
// physical device and sends or receives the I/Q samples stream to or from a     //
 | 
			
		||||
// SDRangel instance via UDP. It is controlled via a Web REST API.               //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// 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 "channel/sdrdaemondataqueue.h"
 | 
			
		||||
#include "channel/sdrdaemondatablock.h"
 | 
			
		||||
#include "daemonsinkthread.h"
 | 
			
		||||
 | 
			
		||||
#include "cm256.h"
 | 
			
		||||
 | 
			
		||||
MESSAGE_CLASS_DEFINITION(DaemonSinkThread::MsgStartStop, Message)
 | 
			
		||||
 | 
			
		||||
DaemonSinkThread::DaemonSinkThread(SDRDaemonDataQueue *dataQueue, CM256 *cm256, QObject* parent) :
 | 
			
		||||
    QThread(parent),
 | 
			
		||||
    m_running(false),
 | 
			
		||||
    m_dataQueue(dataQueue),
 | 
			
		||||
    m_cm256(cm256),
 | 
			
		||||
    m_address(QHostAddress::LocalHost),
 | 
			
		||||
    m_socket(0)
 | 
			
		||||
{
 | 
			
		||||
    connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
 | 
			
		||||
    connect(m_dataQueue, SIGNAL(dataBlockEnqueued()), this, SLOT(handleData()), Qt::QueuedConnection);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DaemonSinkThread::~DaemonSinkThread()
 | 
			
		||||
{
 | 
			
		||||
    qDebug("DaemonSinkThread::~DaemonSinkThread");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSinkThread::startStop(bool start)
 | 
			
		||||
{
 | 
			
		||||
    MsgStartStop *msg = MsgStartStop::create(start);
 | 
			
		||||
    m_inputMessageQueue.push(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSinkThread::startWork()
 | 
			
		||||
{
 | 
			
		||||
    qDebug("DaemonSinkThread::startWork");
 | 
			
		||||
	m_startWaitMutex.lock();
 | 
			
		||||
	m_socket = new QUdpSocket(this);
 | 
			
		||||
	start();
 | 
			
		||||
	while(!m_running)
 | 
			
		||||
		m_startWaiter.wait(&m_startWaitMutex, 100);
 | 
			
		||||
	m_startWaitMutex.unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSinkThread::stopWork()
 | 
			
		||||
{
 | 
			
		||||
	qDebug("DaemonSinkThread::stopWork");
 | 
			
		||||
    delete m_socket;
 | 
			
		||||
    m_socket = 0;
 | 
			
		||||
	m_running = false;
 | 
			
		||||
	wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSinkThread::run()
 | 
			
		||||
{
 | 
			
		||||
    qDebug("DaemonSinkThread::run: begin");
 | 
			
		||||
	m_running = true;
 | 
			
		||||
	m_startWaiter.wakeAll();
 | 
			
		||||
 | 
			
		||||
    while (m_running)
 | 
			
		||||
    {
 | 
			
		||||
        sleep(1); // Do nothing as everything is in the data handler (dequeuer)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_running = false;
 | 
			
		||||
    qDebug("DaemonSinkThread::run: end");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DaemonSinkThread::handleDataBlock(SDRDaemonDataBlock& dataBlock)
 | 
			
		||||
{
 | 
			
		||||
	CM256::cm256_encoder_params cm256Params;  //!< Main interface with CM256 encoder
 | 
			
		||||
	CM256::cm256_block descriptorBlocks[256]; //!< Pointers to data for CM256 encoder
 | 
			
		||||
	SDRDaemonProtectedBlock fecBlocks[256];   //!< FEC data
 | 
			
		||||
 | 
			
		||||
    uint16_t frameIndex = dataBlock.m_txControlBlock.m_frameIndex;
 | 
			
		||||
    int nbBlocksFEC = dataBlock.m_txControlBlock.m_nbBlocksFEC;
 | 
			
		||||
    int txDelay = dataBlock.m_txControlBlock.m_txDelay;
 | 
			
		||||
    m_address.setAddress(dataBlock.m_txControlBlock.m_dataAddress);
 | 
			
		||||
    uint16_t dataPort = dataBlock.m_txControlBlock.m_dataPort;
 | 
			
		||||
    SDRDaemonSuperBlock *txBlockx = dataBlock.m_superBlocks;
 | 
			
		||||
 | 
			
		||||
    if ((nbBlocksFEC == 0) || !m_cm256) // Do not FEC encode
 | 
			
		||||
    {
 | 
			
		||||
        if (m_socket)
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < SDRDaemonNbOrginalBlocks; i++)
 | 
			
		||||
            {
 | 
			
		||||
                // send block via UDP
 | 
			
		||||
                m_socket->writeDatagram((const char*)&txBlockx[i], (qint64 ) SDRDaemonUdpSize, m_address, dataPort);
 | 
			
		||||
                usleep(txDelay);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        cm256Params.BlockBytes = sizeof(SDRDaemonProtectedBlock);
 | 
			
		||||
        cm256Params.OriginalCount = SDRDaemonNbOrginalBlocks;
 | 
			
		||||
        cm256Params.RecoveryCount = nbBlocksFEC;
 | 
			
		||||
 | 
			
		||||
        // Fill pointers to data
 | 
			
		||||
        for (int i = 0; i < cm256Params.OriginalCount + cm256Params.RecoveryCount; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            if (i >= cm256Params.OriginalCount) {
 | 
			
		||||
                memset((void *) &txBlockx[i].m_protectedBlock, 0, sizeof(SDRDaemonProtectedBlock));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            txBlockx[i].m_header.m_frameIndex = frameIndex;
 | 
			
		||||
            txBlockx[i].m_header.m_blockIndex = i;
 | 
			
		||||
            descriptorBlocks[i].Block = (void *) &(txBlockx[i].m_protectedBlock);
 | 
			
		||||
            descriptorBlocks[i].Index = txBlockx[i].m_header.m_blockIndex;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Encode FEC blocks
 | 
			
		||||
        if (m_cm256->cm256_encode(cm256Params, descriptorBlocks, fecBlocks))
 | 
			
		||||
        {
 | 
			
		||||
            qWarning("SDRDaemonChannelSinkThread::handleDataBlock: CM256 encode failed. No transmission.");
 | 
			
		||||
            // TODO: send without FEC changing meta data to set indication of no FEC
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Merge FEC with data to transmit
 | 
			
		||||
        for (int i = 0; i < cm256Params.RecoveryCount; i++)
 | 
			
		||||
        {
 | 
			
		||||
            txBlockx[i + cm256Params.OriginalCount].m_protectedBlock = fecBlocks[i];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Transmit all blocks
 | 
			
		||||
        if (m_socket)
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < cm256Params.OriginalCount + cm256Params.RecoveryCount; i++)
 | 
			
		||||
            {
 | 
			
		||||
                // send block via UDP
 | 
			
		||||
                m_socket->writeDatagram((const char*)&txBlockx[i], (qint64 ) SDRDaemonUdpSize, m_address, dataPort);
 | 
			
		||||
                usleep(txDelay);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dataBlock.m_txControlBlock.m_processed = true;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSinkThread::handleData()
 | 
			
		||||
{
 | 
			
		||||
    SDRDaemonDataBlock* dataBlock;
 | 
			
		||||
 | 
			
		||||
    while (m_running && ((dataBlock = m_dataQueue->pop()) != 0))
 | 
			
		||||
    {
 | 
			
		||||
        if (handleDataBlock(*dataBlock))
 | 
			
		||||
        {
 | 
			
		||||
            delete dataBlock;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DaemonSinkThread::handleInputMessages()
 | 
			
		||||
{
 | 
			
		||||
    Message* message;
 | 
			
		||||
 | 
			
		||||
    while ((message = m_inputMessageQueue.pop()) != 0)
 | 
			
		||||
    {
 | 
			
		||||
        if (MsgStartStop::match(*message))
 | 
			
		||||
        {
 | 
			
		||||
            MsgStartStop* notif = (MsgStartStop*) message;
 | 
			
		||||
            qDebug("DaemonSinkThread::handleInputMessages: MsgStartStop: %s", notif->getStartStop() ? "start" : "stop");
 | 
			
		||||
 | 
			
		||||
            if (notif->getStartStop()) {
 | 
			
		||||
                startWork();
 | 
			
		||||
            } else {
 | 
			
		||||
                stopWork();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            delete message;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,86 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2018 Edouard Griffiths, F4EXB.                                  //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// SDRdaemon sink channel (Rx) UDP sender thread                                 //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// SDRdaemon is a detached SDR front end that handles the interface with a       //
 | 
			
		||||
// physical device and sends or receives the I/Q samples stream to or from a     //
 | 
			
		||||
// SDRangel instance via UDP. It is controlled via a Web REST API.               //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// 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 <QThread>
 | 
			
		||||
#include <QMutex>
 | 
			
		||||
#include <QWaitCondition>
 | 
			
		||||
#include <QHostAddress>
 | 
			
		||||
 | 
			
		||||
#include "util/message.h"
 | 
			
		||||
#include "util/messagequeue.h"
 | 
			
		||||
 | 
			
		||||
class SDRDaemonDataQueue;
 | 
			
		||||
class SDRDaemonDataBlock;
 | 
			
		||||
class CM256;
 | 
			
		||||
class QUdpSocket;
 | 
			
		||||
 | 
			
		||||
class DaemonSinkThread : public QThread {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    class MsgStartStop : public Message {
 | 
			
		||||
        MESSAGE_CLASS_DECLARATION
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        bool getStartStop() const { return m_startStop; }
 | 
			
		||||
 | 
			
		||||
        static MsgStartStop* create(bool startStop) {
 | 
			
		||||
            return new MsgStartStop(startStop);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    protected:
 | 
			
		||||
        bool m_startStop;
 | 
			
		||||
 | 
			
		||||
        MsgStartStop(bool startStop) :
 | 
			
		||||
            Message(),
 | 
			
		||||
            m_startStop(startStop)
 | 
			
		||||
        { }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    DaemonSinkThread(SDRDaemonDataQueue *dataQueue, CM256 *cm256, QObject* parent = 0);
 | 
			
		||||
    ~DaemonSinkThread();
 | 
			
		||||
 | 
			
		||||
    void startStop(bool start);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	QMutex m_startWaitMutex;
 | 
			
		||||
	QWaitCondition m_startWaiter;
 | 
			
		||||
	bool m_running;
 | 
			
		||||
 | 
			
		||||
    SDRDaemonDataQueue *m_dataQueue;
 | 
			
		||||
    CM256 *m_cm256;                       //!< CM256 library object
 | 
			
		||||
 | 
			
		||||
    QHostAddress m_address;
 | 
			
		||||
    QUdpSocket *m_socket;
 | 
			
		||||
 | 
			
		||||
    MessageQueue m_inputMessageQueue;
 | 
			
		||||
 | 
			
		||||
    void startWork();
 | 
			
		||||
    void stopWork();
 | 
			
		||||
 | 
			
		||||
    void run();
 | 
			
		||||
    bool handleDataBlock(SDRDaemonDataBlock& dataBlock);
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
    void handleData();
 | 
			
		||||
    void handleInputMessages();
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -253,7 +253,7 @@
 | 
			
		|||
        <number>90</number>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="pageStep">
 | 
			
		||||
        <number>0</number>
 | 
			
		||||
        <number>1</number>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="value">
 | 
			
		||||
        <number>50</number>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue