mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-03-07 11:59:04 -05:00
Tx ph.1: Added the DSPDeviceSinkEngine class
This commit is contained in:
parent
9cf62833ba
commit
0f7ac00a71
@ -102,6 +102,7 @@ set(sdrbase_SOURCES
|
||||
sdrbase/dsp/dspcommands.cpp
|
||||
sdrbase/dsp/dspengine.cpp
|
||||
sdrbase/dsp/dspdevicesourceengine.cpp
|
||||
sdrbase/dsp/dspdevicesinkengine.cpp
|
||||
sdrbase/dsp/fftengine.cpp
|
||||
sdrbase/dsp/fftfilt.cxx
|
||||
sdrbase/dsp/fftwindow.cpp
|
||||
@ -191,6 +192,7 @@ set(sdrbase_HEADERS
|
||||
sdrbase/dsp/dspcommands.h
|
||||
sdrbase/dsp/dspengine.h
|
||||
sdrbase/dsp/dspdevicesourceengine.h
|
||||
sdrbase/dsp/dspdevicesinkengine.h
|
||||
sdrbase/dsp/dsptypes.h
|
||||
sdrbase/dsp/fftengine.h
|
||||
sdrbase/dsp/fftfilt.h
|
||||
|
@ -1,129 +1,129 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 SDRBASE_DEVICE_DEVICESINKAPI_H_
|
||||
#define SDRBASE_DEVICE_DEVICESINKAPI_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "util/export.h"
|
||||
|
||||
class MainWindow;
|
||||
class DSPDeviceSinkEngine; // TODO: TBD
|
||||
class GLSpectrum;
|
||||
class ChannelWindow;
|
||||
class BasebandSampleSource; // TODO: TBD
|
||||
class ThreadedBasebandSampleSource; // TODO: TBD
|
||||
class DeviceSampleSink; // TODO: TBD
|
||||
class MessageQueue;
|
||||
class ChannelMarker;
|
||||
class QWidget;
|
||||
class PluginGUI;
|
||||
class PluginAPI;
|
||||
class Preset;
|
||||
|
||||
class SDRANGEL_API DeviceSinkAPI : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Device engine stuff
|
||||
void addSource(BasebandSampleSource* source); //!< Add a baseband sample source to device engine
|
||||
void removeSource(BasebandSampleSource* sink); //!< Remove a baseband sample source from device engine
|
||||
void addThreadedSource(ThreadedBasebandSampleSource* sink); //!< Add a baseband sample source that will run on its own thread to device engine
|
||||
void removeThreadedSource(ThreadedBasebandSampleSource* sink); //!< Remove a baseband sample source that runs on its own thread from device engine
|
||||
void setSink(DeviceSampleSink* sink); //!< Set device engine sample sink type
|
||||
bool initGeneration(); //!< Initialize device engine generation sequence
|
||||
bool startGeneration(); //!< Start device engine generation sequence
|
||||
void stopGeneration(); //!< Stop device engine generation sequence
|
||||
//TODO: DSPDeviceSourceEngine::State state() const; //!< device engine state
|
||||
QString errorMessage(); //!< Return the current device engine error message
|
||||
uint getDeviceUID() const; //!< Return the current device engine unique ID
|
||||
MessageQueue *getDeviceInputMessageQueue();
|
||||
MessageQueue *getDeviceOutputMessageQueue();
|
||||
// device related stuff
|
||||
GLSpectrum *getSpectrum(); //!< Direct spectrum getter
|
||||
void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum
|
||||
ChannelWindow *getChannelWindow(); //!< Direct channel window getter
|
||||
void addRollupWidget(QWidget *widget); //!< Add rollup widget to channel window
|
||||
void setOutputGUI(QWidget* outputGUI, const QString& sinkDisplayName);
|
||||
|
||||
void setSampleSinkId(const QString& id);
|
||||
void setSampleSinkSerial(const QString& serial);
|
||||
void setSampleSinkSequence(int sequence);
|
||||
void setSampleSinkPluginGUI(PluginGUI *gui);
|
||||
|
||||
void registerChannelInstance(const QString& channelName, PluginGUI* pluginGUI);
|
||||
void removeChannelInstance(PluginGUI* pluginGUI);
|
||||
|
||||
void freeAll();
|
||||
|
||||
void loadSinkSettings(const Preset* preset);
|
||||
void saveSinkSettings(Preset* preset);
|
||||
void loadChannelSettings(const Preset* preset, PluginAPI *pluginAPI);
|
||||
void saveChannelSettings(Preset* preset);
|
||||
|
||||
MainWindow *getMainWindow() { return m_mainWindow; }
|
||||
|
||||
protected:
|
||||
struct ChannelInstanceRegistration
|
||||
{
|
||||
QString m_channelName;
|
||||
PluginGUI* m_gui;
|
||||
|
||||
ChannelInstanceRegistration() :
|
||||
m_channelName(),
|
||||
m_gui(0)
|
||||
{ }
|
||||
|
||||
ChannelInstanceRegistration(const QString& channelName, PluginGUI* pluginGUI) :
|
||||
m_channelName(channelName),
|
||||
m_gui(pluginGUI)
|
||||
{ }
|
||||
|
||||
bool operator<(const ChannelInstanceRegistration& other) const;
|
||||
};
|
||||
|
||||
typedef QList<ChannelInstanceRegistration> ChannelInstanceRegistrations;
|
||||
|
||||
DeviceSinkAPI(MainWindow *mainWindow,
|
||||
int deviceTabIndex,
|
||||
DSPDeviceSourceEngine *deviceEngine,
|
||||
GLSpectrum *glSpectrum,
|
||||
ChannelWindow *channelWindow);
|
||||
~DeviceSinkAPI();
|
||||
|
||||
void renameChannelInstances();
|
||||
|
||||
MainWindow *m_mainWindow;
|
||||
int m_deviceTabIndex;
|
||||
DSPDeviceSinkEngine *m_deviceEngine;
|
||||
GLSpectrum *m_spectrum;
|
||||
ChannelWindow *m_channelWindow;
|
||||
|
||||
QString m_sampleSinkId;
|
||||
QString m_sampleSinkSerial;
|
||||
int m_sampleSinkSequence;
|
||||
PluginGUI* m_sampleSinkPluginGUI;
|
||||
|
||||
ChannelInstanceRegistrations m_channelInstanceRegistrations;
|
||||
|
||||
friend class MainWindow;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* SDRBASE_DEVICE_DEVICESINKAPI_H_ */
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 SDRBASE_DEVICE_DEVICESINKAPI_H_
|
||||
#define SDRBASE_DEVICE_DEVICESINKAPI_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "dsp/dspdevicesinkengine.h" // TODO: TBD
|
||||
#include "util/export.h"
|
||||
|
||||
class MainWindow;
|
||||
class GLSpectrum;
|
||||
class ChannelWindow;
|
||||
class BasebandSampleSource;
|
||||
class ThreadedBasebandSampleSource;
|
||||
class DeviceSampleSink;
|
||||
class MessageQueue;
|
||||
class ChannelMarker;
|
||||
class QWidget;
|
||||
class PluginGUI;
|
||||
class PluginAPI;
|
||||
class Preset;
|
||||
|
||||
class SDRANGEL_API DeviceSinkAPI : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Device engine stuff
|
||||
void addSource(BasebandSampleSource* source); //!< Add a baseband sample source to device engine
|
||||
void removeSource(BasebandSampleSource* sink); //!< Remove a baseband sample source from device engine
|
||||
void addThreadedSource(ThreadedBasebandSampleSource* sink); //!< Add a baseband sample source that will run on its own thread to device engine
|
||||
void removeThreadedSource(ThreadedBasebandSampleSource* sink); //!< Remove a baseband sample source that runs on its own thread from device engine
|
||||
void setSink(DeviceSampleSink* sink); //!< Set device engine sample sink type
|
||||
bool initGeneration(); //!< Initialize device engine generation sequence
|
||||
bool startGeneration(); //!< Start device engine generation sequence
|
||||
void stopGeneration(); //!< Stop device engine generation sequence
|
||||
DSPDeviceSinkEngine::State state() const; //!< device engine state
|
||||
QString errorMessage(); //!< Return the current device engine error message
|
||||
uint getDeviceUID() const; //!< Return the current device engine unique ID
|
||||
MessageQueue *getDeviceInputMessageQueue();
|
||||
MessageQueue *getDeviceOutputMessageQueue();
|
||||
// device related stuff
|
||||
GLSpectrum *getSpectrum(); //!< Direct spectrum getter
|
||||
void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum
|
||||
ChannelWindow *getChannelWindow(); //!< Direct channel window getter
|
||||
void addRollupWidget(QWidget *widget); //!< Add rollup widget to channel window
|
||||
void setOutputGUI(QWidget* outputGUI, const QString& sinkDisplayName);
|
||||
|
||||
void setSampleSinkId(const QString& id);
|
||||
void setSampleSinkSerial(const QString& serial);
|
||||
void setSampleSinkSequence(int sequence);
|
||||
void setSampleSinkPluginGUI(PluginGUI *gui);
|
||||
|
||||
void registerChannelInstance(const QString& channelName, PluginGUI* pluginGUI);
|
||||
void removeChannelInstance(PluginGUI* pluginGUI);
|
||||
|
||||
void freeAll();
|
||||
|
||||
void loadSinkSettings(const Preset* preset);
|
||||
void saveSinkSettings(Preset* preset);
|
||||
void loadChannelSettings(const Preset* preset, PluginAPI *pluginAPI);
|
||||
void saveChannelSettings(Preset* preset);
|
||||
|
||||
MainWindow *getMainWindow() { return m_mainWindow; }
|
||||
|
||||
protected:
|
||||
struct ChannelInstanceRegistration
|
||||
{
|
||||
QString m_channelName;
|
||||
PluginGUI* m_gui;
|
||||
|
||||
ChannelInstanceRegistration() :
|
||||
m_channelName(),
|
||||
m_gui(0)
|
||||
{ }
|
||||
|
||||
ChannelInstanceRegistration(const QString& channelName, PluginGUI* pluginGUI) :
|
||||
m_channelName(channelName),
|
||||
m_gui(pluginGUI)
|
||||
{ }
|
||||
|
||||
bool operator<(const ChannelInstanceRegistration& other) const;
|
||||
};
|
||||
|
||||
typedef QList<ChannelInstanceRegistration> ChannelInstanceRegistrations;
|
||||
|
||||
DeviceSinkAPI(MainWindow *mainWindow,
|
||||
int deviceTabIndex,
|
||||
DSPDeviceSourceEngine *deviceEngine,
|
||||
GLSpectrum *glSpectrum,
|
||||
ChannelWindow *channelWindow);
|
||||
~DeviceSinkAPI();
|
||||
|
||||
void renameChannelInstances();
|
||||
|
||||
MainWindow *m_mainWindow;
|
||||
int m_deviceTabIndex;
|
||||
DSPDeviceSinkEngine *m_deviceEngine;
|
||||
GLSpectrum *m_spectrum;
|
||||
ChannelWindow *m_channelWindow;
|
||||
|
||||
QString m_sampleSinkId;
|
||||
QString m_sampleSinkSerial;
|
||||
int m_sampleSinkSequence;
|
||||
PluginGUI* m_sampleSinkPluginGUI;
|
||||
|
||||
ChannelInstanceRegistrations m_channelInstanceRegistrations;
|
||||
|
||||
friend class MainWindow;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* SDRBASE_DEVICE_DEVICESINKAPI_H_ */
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include "util/export.h"
|
||||
|
||||
class MainWindow;
|
||||
class DSPDeviceSourceEngine;
|
||||
class GLSpectrum;
|
||||
class ChannelWindow;
|
||||
class BasebandSampleSink;
|
||||
|
@ -21,13 +21,22 @@ MESSAGE_CLASS_DEFINITION(DSPExit, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPAcquisitionInit, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPAcquisitionStart, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPAcquisitionStop, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPGenerationInit, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPGenerationStart, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPGenerationStop, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPGetSourceDeviceDescription, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPGetSinkDeviceDescription, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPGetErrorMessage, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPSetSource, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPSetSink, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPAddSink, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPAddSource, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPRemoveSink, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPRemoveSource, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPAddThreadedSampleSink, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPAddThreadedSampleSource, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPRemoveThreadedSampleSink, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPRemoveThreadedSampleSource, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPAddAudioSink, Message)
|
||||
MESSAGE_CLASS_DEFINITION(DSPRemoveAudioSink, Message)
|
||||
//MESSAGE_CLASS_DEFINITION(DSPConfigureSpectrumVis, Message)
|
||||
|
@ -26,6 +26,9 @@
|
||||
class DeviceSampleSource;
|
||||
class BasebandSampleSink;
|
||||
class ThreadedBasebandSampleSink;
|
||||
class DeviceSampleSink;
|
||||
class BasebandSampleSource;
|
||||
class ThreadedBasebandSampleSource;
|
||||
class AudioFifo;
|
||||
|
||||
class SDRANGEL_API DSPExit : public Message {
|
||||
@ -44,6 +47,18 @@ class SDRANGEL_API DSPAcquisitionStop : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPGenerationInit : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPGenerationStart : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPGenerationStop : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPGetSourceDeviceDescription : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
@ -55,6 +70,17 @@ private:
|
||||
QString m_deviceDescription;
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPGetSinkDeviceDescription : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
void setDeviceDescription(const QString& text) { m_deviceDescription = text; }
|
||||
const QString& getDeviceDescription() const { return m_deviceDescription; }
|
||||
|
||||
private:
|
||||
QString m_deviceDescription;
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPGetErrorMessage : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
@ -78,6 +104,18 @@ private:
|
||||
DeviceSampleSource* m_sampleSource;
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPSetSink : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
DSPSetSink(DeviceSampleSink* sampleSink) : Message(), m_sampleSink(sampleSink) { }
|
||||
|
||||
DeviceSampleSink* getSampleSink() const { return m_sampleSink; }
|
||||
|
||||
private:
|
||||
DeviceSampleSink* m_sampleSink;
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPAddSink : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
@ -90,6 +128,18 @@ private:
|
||||
BasebandSampleSink* m_sampleSink;
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPAddSource : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
DSPAddSource(BasebandSampleSource* sampleSource) : Message(), m_sampleSource(sampleSource) { }
|
||||
|
||||
BasebandSampleSource* getSampleSource() const { return m_sampleSource; }
|
||||
|
||||
private:
|
||||
BasebandSampleSource* m_sampleSource;
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPRemoveSink : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
@ -102,6 +152,18 @@ private:
|
||||
BasebandSampleSink* m_sampleSink;
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPRemoveSource : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
DSPRemoveSource(BasebandSampleSource* sampleSource) : Message(), m_sampleSource(sampleSource) { }
|
||||
|
||||
BasebandSampleSource* getSampleSource() const { return m_sampleSource; }
|
||||
|
||||
private:
|
||||
BasebandSampleSource* m_sampleSource;
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPAddThreadedSampleSink : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
@ -114,6 +176,18 @@ private:
|
||||
ThreadedBasebandSampleSink* m_threadedSampleSink;
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPAddThreadedSampleSource : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
DSPAddThreadedSampleSource(ThreadedBasebandSampleSource* threadedSampleSource) : Message(), m_threadedSampleSource(threadedSampleSource) { }
|
||||
|
||||
ThreadedBasebandSampleSource* getThreadedSampleSource() const { return m_threadedSampleSource; }
|
||||
|
||||
private:
|
||||
ThreadedBasebandSampleSource* m_threadedSampleSource;
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPRemoveThreadedSampleSink : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
@ -126,6 +200,18 @@ private:
|
||||
ThreadedBasebandSampleSink* m_threadedSampleSink;
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPRemoveThreadedSampleSource : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
DSPRemoveThreadedSampleSource(ThreadedBasebandSampleSource* threadedSampleSource) : Message(), m_threadedSampleSource(threadedSampleSource) { }
|
||||
|
||||
ThreadedBasebandSampleSource* getThreadedSampleSource() const { return m_threadedSampleSource; }
|
||||
|
||||
private:
|
||||
ThreadedBasebandSampleSource* m_threadedSampleSource;
|
||||
};
|
||||
|
||||
class SDRANGEL_API DSPAddAudioSink : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
|
554
sdrbase/dsp/dspdevicesinkengine.cpp
Normal file
554
sdrbase/dsp/dspdevicesinkengine.cpp
Normal file
@ -0,0 +1,554 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 <stdio.h>
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
|
||||
#include "dspdevicesinkengine.h"
|
||||
|
||||
#include "dsp/basebandsamplesource.h"
|
||||
#include "dsp/basebandsamplesink.h"
|
||||
#include "dsp/devicesamplesink.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "samplesourcefifo.h"
|
||||
#include "threadedbasebandsamplesource.h"
|
||||
|
||||
DSPDeviceSinkEngine::DSPDeviceSinkEngine(uint uid, QObject* parent) :
|
||||
m_uid(uid),
|
||||
QThread(parent),
|
||||
m_state(StNotStarted),
|
||||
m_deviceSampleSink(0),
|
||||
m_sampleSinkSequence(0),
|
||||
m_basebandSampleSources(),
|
||||
m_sampleRate(0),
|
||||
m_centerFrequency(0)
|
||||
{
|
||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
|
||||
connect(&m_syncMessenger, SIGNAL(messageSent()), this, SLOT(handleSynchronousMessages()), Qt::QueuedConnection);
|
||||
|
||||
moveToThread(this);
|
||||
}
|
||||
|
||||
DSPDeviceSinkEngine::~DSPDeviceSinkEngine()
|
||||
{
|
||||
wait();
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::run()
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::run";
|
||||
|
||||
m_state = StIdle;
|
||||
|
||||
m_syncMessenger.done(); // Release start() that is waiting in main thread
|
||||
exec();
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::start()
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::start";
|
||||
QThread::start();
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::stop()
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::stop";
|
||||
DSPExit cmd;
|
||||
m_syncMessenger.sendWait(cmd);
|
||||
}
|
||||
|
||||
bool DSPDeviceSinkEngine::initGeneration()
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::initGeneration";
|
||||
DSPGenerationInit cmd;
|
||||
|
||||
return m_syncMessenger.sendWait(cmd) == StReady;
|
||||
}
|
||||
|
||||
bool DSPDeviceSinkEngine::startGeneration()
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::startGeneration";
|
||||
DSPGenerationStart cmd;
|
||||
|
||||
return m_syncMessenger.sendWait(cmd) == StRunning;
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::stopGeneration()
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::stopGeneration";
|
||||
DSPGenerationStop cmd;
|
||||
m_syncMessenger.storeMessage(cmd);
|
||||
handleSynchronousMessages();
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::setSink(DeviceSampleSink* sink)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::setSink";
|
||||
DSPSetSink cmd(sink);
|
||||
m_syncMessenger.sendWait(cmd);
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::setSinkSequence(int sequence)
|
||||
{
|
||||
qDebug("DSPDeviceSinkEngine::setSinkSequence: seq: %d", sequence);
|
||||
m_sampleSinkSequence = sequence;
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::addSource(BasebandSampleSource* source)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::addSource: " << source->objectName().toStdString().c_str();
|
||||
DSPAddSource cmd(source);
|
||||
m_syncMessenger.sendWait(cmd);
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::removeSource(BasebandSampleSource* source)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::removeSource: " << source->objectName().toStdString().c_str();
|
||||
DSPRemoveSource cmd(source);
|
||||
m_syncMessenger.sendWait(cmd);
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::addThreadedSource(ThreadedBasebandSampleSource* source)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::addThreadedSource: " << source->objectName().toStdString().c_str();
|
||||
DSPAddThreadedSampleSource cmd(source);
|
||||
m_syncMessenger.sendWait(cmd);
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::removeThreadedSource(ThreadedBasebandSampleSource* source)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::removeThreadedSource: " << source->objectName().toStdString().c_str();
|
||||
DSPRemoveThreadedSampleSource cmd(source);
|
||||
m_syncMessenger.sendWait(cmd);
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::addSink(BasebandSampleSink* sink)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::addSink: " << sink->objectName().toStdString().c_str();
|
||||
DSPAddSink cmd(sink);
|
||||
m_syncMessenger.sendWait(cmd);
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::removeSink(BasebandSampleSink* sink)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::removeSink: " << sink->objectName().toStdString().c_str();
|
||||
DSPRemoveSink cmd(sink);
|
||||
m_syncMessenger.sendWait(cmd);
|
||||
}
|
||||
|
||||
QString DSPDeviceSinkEngine::errorMessage()
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::errorMessage";
|
||||
DSPGetErrorMessage cmd;
|
||||
m_syncMessenger.sendWait(cmd);
|
||||
return cmd.getErrorMessage();
|
||||
}
|
||||
|
||||
QString DSPDeviceSinkEngine::sinkDeviceDescription()
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::sinkDeviceDescription";
|
||||
DSPGetSinkDeviceDescription cmd;
|
||||
m_syncMessenger.sendWait(cmd);
|
||||
return cmd.getDeviceDescription();
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::work()
|
||||
{
|
||||
SampleSourceFifo* sampleFifo = m_deviceSampleSink->getSampleFifo();
|
||||
unsigned int nbWriteSamples = sampleFifo->getChunkSize();
|
||||
SampleVector::iterator writeBegin;
|
||||
sampleFifo->getWriteIterator(writeBegin);
|
||||
SampleVector::iterator writeAt = writeBegin;
|
||||
Sample s;
|
||||
|
||||
if ((m_threadedBasebandSampleSources.size() + m_basebandSampleSources.size()) > 0)
|
||||
{
|
||||
for (int is = 0; is < nbWriteSamples; is++, ++writeAt)
|
||||
{
|
||||
// pull data from threaded sources and merge them in the device sample FIFO
|
||||
for (ThreadedBasebandSampleSources::iterator it = m_threadedBasebandSampleSources.begin(); it != m_threadedBasebandSampleSources.end(); ++it)
|
||||
{
|
||||
(*it)->pull(s);
|
||||
s /= (m_threadedBasebandSampleSources.size() + m_basebandSampleSources.size());
|
||||
*writeAt += s;
|
||||
}
|
||||
|
||||
// pull data from direct sources and merge them in the device sample FIFO
|
||||
for (BasebandSampleSources::iterator it = m_basebandSampleSources.begin(); it != m_basebandSampleSources.end(); ++it)
|
||||
{
|
||||
(*it)->pull(s);
|
||||
s /= (m_threadedBasebandSampleSources.size() + m_basebandSampleSources.size());
|
||||
*writeAt += s;
|
||||
}
|
||||
}
|
||||
|
||||
// feed the mix to the sinks normally just the main spectrum vis
|
||||
for (BasebandSampleSinks::iterator it = m_basebandSampleSinks.begin(); it != m_basebandSampleSinks.end(); ++it)
|
||||
{
|
||||
(*it)->feed(writeBegin, writeBegin + nbWriteSamples, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// notStarted -> idle -> init -> running -+
|
||||
// ^ |
|
||||
// +-----------------------+
|
||||
|
||||
DSPDeviceSinkEngine::State DSPDeviceSinkEngine::gotoIdle()
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoIdle";
|
||||
|
||||
switch(m_state) {
|
||||
case StNotStarted:
|
||||
return StNotStarted;
|
||||
|
||||
case StIdle:
|
||||
case StError:
|
||||
return StIdle;
|
||||
|
||||
case StReady:
|
||||
case StRunning:
|
||||
break;
|
||||
}
|
||||
|
||||
if(m_deviceSampleSink == 0)
|
||||
{
|
||||
return StIdle;
|
||||
}
|
||||
|
||||
// stop everything
|
||||
|
||||
for(BasebandSampleSources::const_iterator it = m_basebandSampleSources.begin(); it != m_basebandSampleSources.end(); it++)
|
||||
{
|
||||
(*it)->stop();
|
||||
}
|
||||
|
||||
for(ThreadedBasebandSampleSources::const_iterator it = m_threadedBasebandSampleSources.begin(); it != m_threadedBasebandSampleSources.end(); it++)
|
||||
{
|
||||
(*it)->stop();
|
||||
}
|
||||
|
||||
for(BasebandSampleSinks::const_iterator it = m_basebandSampleSinks.begin(); it != m_basebandSampleSinks.end(); it++)
|
||||
{
|
||||
(*it)->stop();
|
||||
}
|
||||
|
||||
m_deviceSampleSink->stop();
|
||||
m_deviceDescription.clear();
|
||||
m_sampleRate = 0;
|
||||
|
||||
return StIdle;
|
||||
}
|
||||
|
||||
DSPDeviceSinkEngine::State DSPDeviceSinkEngine::gotoInit()
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoInit";
|
||||
|
||||
switch(m_state) {
|
||||
case StNotStarted:
|
||||
return StNotStarted;
|
||||
|
||||
case StRunning: // FIXME: assumes it goes first through idle state. Could we get back to init from running directly?
|
||||
return StRunning;
|
||||
|
||||
case StReady:
|
||||
return StReady;
|
||||
|
||||
case StIdle:
|
||||
case StError:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_deviceSampleSink == 0)
|
||||
{
|
||||
return gotoError("DSPDeviceSinkEngine::gotoInit: No sample source configured");
|
||||
}
|
||||
|
||||
// init: pass sample rate and center frequency to all sample rate and/or center frequency dependent sinks and wait for completion
|
||||
|
||||
m_deviceDescription = m_deviceSampleSink->getDeviceDescription();
|
||||
m_centerFrequency = m_deviceSampleSink->getCenterFrequency();
|
||||
m_sampleRate = m_deviceSampleSink->getSampleRate();
|
||||
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoInit: " << m_deviceDescription.toStdString().c_str() << ": "
|
||||
<< " sampleRate: " << m_sampleRate
|
||||
<< " centerFrequency: " << m_centerFrequency;
|
||||
|
||||
DSPSignalNotification notif(m_sampleRate, m_centerFrequency);
|
||||
|
||||
for (BasebandSampleSources::const_iterator it = m_basebandSampleSources.begin(); it != m_basebandSampleSources.end(); ++it)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoInit: initializing " << (*it)->objectName().toStdString().c_str();
|
||||
(*it)->handleMessage(notif);
|
||||
}
|
||||
|
||||
for (ThreadedBasebandSampleSources::const_iterator it = m_threadedBasebandSampleSources.begin(); it != m_threadedBasebandSampleSources.end(); ++it)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoInit: initializing ThreadedSampleSource(" << (*it)->getSampleSourceObjectName().toStdString().c_str() << ")";
|
||||
(*it)->handleSourceMessage(notif);
|
||||
}
|
||||
|
||||
for (BasebandSampleSinks::const_iterator it = m_basebandSampleSinks.begin(); it != m_basebandSampleSinks.end(); ++it)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoInit: initializing " << (*it)->objectName().toStdString().c_str();
|
||||
(*it)->handleMessage(notif);
|
||||
}
|
||||
|
||||
// pass data to listeners
|
||||
|
||||
DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy for the output queue
|
||||
m_outputMessageQueue.push(rep);
|
||||
|
||||
return StReady;
|
||||
}
|
||||
|
||||
DSPDeviceSinkEngine::State DSPDeviceSinkEngine::gotoRunning()
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoRunning";
|
||||
|
||||
switch(m_state)
|
||||
{
|
||||
case StNotStarted:
|
||||
return StNotStarted;
|
||||
|
||||
case StIdle:
|
||||
return StIdle;
|
||||
|
||||
case StRunning:
|
||||
return StRunning;
|
||||
|
||||
case StReady:
|
||||
case StError:
|
||||
break;
|
||||
}
|
||||
|
||||
if(m_deviceSampleSink == NULL) {
|
||||
return gotoError("DSPDeviceSinkEngine::gotoRunning: No sample source configured");
|
||||
}
|
||||
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoRunning: " << m_deviceDescription.toStdString().c_str() << " started";
|
||||
|
||||
// Start everything
|
||||
|
||||
if(!m_deviceSampleSink->start(m_sampleSinkSequence))
|
||||
{
|
||||
return gotoError("DSPDeviceSinkEngine::gotoRunning: Could not start sample source");
|
||||
}
|
||||
|
||||
for(BasebandSampleSources::const_iterator it = m_basebandSampleSources.begin(); it != m_basebandSampleSources.end(); it++)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoRunning: starting " << (*it)->objectName().toStdString().c_str();
|
||||
(*it)->start();
|
||||
}
|
||||
|
||||
for (ThreadedBasebandSampleSources::const_iterator it = m_threadedBasebandSampleSources.begin(); it != m_threadedBasebandSampleSources.end(); ++it)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoRunning: starting ThreadedSampleSource(" << (*it)->getSampleSourceObjectName().toStdString().c_str() << ")";
|
||||
(*it)->start();
|
||||
}
|
||||
|
||||
for(BasebandSampleSinks::const_iterator it = m_basebandSampleSinks.begin(); it != m_basebandSampleSinks.end(); it++)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoRunning: starting " << (*it)->objectName().toStdString().c_str();
|
||||
(*it)->start();
|
||||
}
|
||||
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoRunning: input message queue pending: " << m_inputMessageQueue.size();
|
||||
|
||||
return StRunning;
|
||||
}
|
||||
|
||||
DSPDeviceSinkEngine::State DSPDeviceSinkEngine::gotoError(const QString& errorMessage)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::gotoError";
|
||||
|
||||
m_errorMessage = errorMessage;
|
||||
m_deviceDescription.clear();
|
||||
m_state = StError;
|
||||
return StError;
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::handleSetSink(DeviceSampleSink* sink)
|
||||
{
|
||||
gotoIdle();
|
||||
|
||||
// if(m_sampleSource != 0)
|
||||
// {
|
||||
// disconnect(m_sampleSource->getSampleFifo(), SIGNAL(dataReady()), this, SLOT(handleData()));
|
||||
// }
|
||||
|
||||
m_deviceSampleSink = sink;
|
||||
|
||||
if(m_deviceSampleSink != 0)
|
||||
{
|
||||
qDebug("DSPDeviceSinkEngine::handleSetSink: set %s", qPrintable(sink->getDeviceDescription()));
|
||||
connect(m_deviceSampleSink->getSampleFifo(), SIGNAL(dataWrite()), this, SLOT(handleData()), Qt::QueuedConnection);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("DSPDeviceSinkEngine::handleSetSource: set none");
|
||||
}
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::handleData()
|
||||
{
|
||||
if(m_state == StRunning)
|
||||
{
|
||||
work();
|
||||
}
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::handleSynchronousMessages()
|
||||
{
|
||||
Message *message = m_syncMessenger.getMessage();
|
||||
qDebug() << "DSPDeviceSourceEngine::handleSynchronousMessages: " << message->getIdentifier();
|
||||
|
||||
if (DSPExit::match(*message))
|
||||
{
|
||||
gotoIdle();
|
||||
m_state = StNotStarted;
|
||||
exit();
|
||||
}
|
||||
else if (DSPGenerationInit::match(*message))
|
||||
{
|
||||
m_state = gotoIdle();
|
||||
|
||||
if(m_state == StIdle) {
|
||||
m_state = gotoInit(); // State goes ready if init is performed
|
||||
}
|
||||
}
|
||||
else if (DSPGenerationStart::match(*message))
|
||||
{
|
||||
if(m_state == StReady) {
|
||||
m_state = gotoRunning();
|
||||
}
|
||||
}
|
||||
else if (DSPGenerationStop::match(*message))
|
||||
{
|
||||
m_state = gotoIdle();
|
||||
}
|
||||
else if (DSPGetSinkDeviceDescription::match(*message))
|
||||
{
|
||||
((DSPGetSinkDeviceDescription*) message)->setDeviceDescription(m_deviceDescription);
|
||||
}
|
||||
else if (DSPGetErrorMessage::match(*message))
|
||||
{
|
||||
((DSPGetErrorMessage*) message)->setErrorMessage(m_errorMessage);
|
||||
}
|
||||
else if (DSPSetSink::match(*message)) {
|
||||
handleSetSink(((DSPSetSink*) message)->getSampleSink());
|
||||
}
|
||||
else if (DSPAddSink::match(*message))
|
||||
{
|
||||
BasebandSampleSink* sink = ((DSPAddSink*) message)->getSampleSink();
|
||||
m_basebandSampleSinks.push_back(sink);
|
||||
}
|
||||
else if (DSPRemoveSink::match(*message))
|
||||
{
|
||||
BasebandSampleSink* sink = ((DSPRemoveSink*) message)->getSampleSink();
|
||||
|
||||
if(m_state == StRunning) {
|
||||
sink->stop();
|
||||
}
|
||||
|
||||
m_basebandSampleSinks.remove(sink);
|
||||
}
|
||||
else if (DSPAddSource::match(*message))
|
||||
{
|
||||
BasebandSampleSource* source = ((DSPAddSource*) message)->getSampleSource();
|
||||
m_basebandSampleSources.push_back(source);
|
||||
}
|
||||
else if (DSPRemoveSource::match(*message))
|
||||
{
|
||||
BasebandSampleSource* source = ((DSPRemoveSource*) message)->getSampleSource();
|
||||
|
||||
if(m_state == StRunning) {
|
||||
source->stop();
|
||||
}
|
||||
|
||||
m_basebandSampleSources.remove(source);
|
||||
}
|
||||
else if (DSPAddThreadedSampleSource::match(*message))
|
||||
{
|
||||
ThreadedBasebandSampleSource *threadedSource = ((DSPAddThreadedSampleSource*) message)->getThreadedSampleSource();
|
||||
m_threadedBasebandSampleSources.push_back(threadedSource);
|
||||
threadedSource->start();
|
||||
}
|
||||
else if (DSPRemoveThreadedSampleSource::match(*message))
|
||||
{
|
||||
ThreadedBasebandSampleSource* threadedSource = ((DSPRemoveThreadedSampleSource*) message)->getThreadedSampleSource();
|
||||
threadedSource->stop();
|
||||
m_threadedBasebandSampleSources.remove(threadedSource);
|
||||
}
|
||||
|
||||
m_syncMessenger.done(m_state);
|
||||
}
|
||||
|
||||
void DSPDeviceSinkEngine::handleInputMessages()
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::handleInputMessages";
|
||||
|
||||
Message* message;
|
||||
|
||||
while ((message = m_inputMessageQueue.pop()) != 0)
|
||||
{
|
||||
qDebug("DSPDeviceSinkEngine::handleInputMessages: message: %s", message->getIdentifier());
|
||||
|
||||
if (DSPSignalNotification::match(*message))
|
||||
{
|
||||
DSPSignalNotification *notif = (DSPSignalNotification *) message;
|
||||
|
||||
// update DSP values
|
||||
|
||||
m_sampleRate = notif->getSampleRate();
|
||||
m_centerFrequency = notif->getCenterFrequency();
|
||||
|
||||
qDebug() << "DSPDeviceSinkEngine::handleInputMessages: DSPSignalNotification(" << m_sampleRate << "," << m_centerFrequency << ")";
|
||||
|
||||
// forward source changes to sources with immediate execution
|
||||
|
||||
for(BasebandSampleSources::const_iterator it = m_basebandSampleSources.begin(); it != m_basebandSampleSources.end(); it++)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::handleInputMessages: forward message to " << (*it)->objectName().toStdString().c_str();
|
||||
(*it)->handleMessage(*message);
|
||||
}
|
||||
|
||||
for (ThreadedBasebandSampleSources::const_iterator it = m_threadedBasebandSampleSources.begin(); it != m_threadedBasebandSampleSources.end(); ++it)
|
||||
{
|
||||
qDebug() << "DSPDeviceSinkEngine::handleSourceMessages: forward message to ThreadedSampleSource(" << (*it)->getSampleSourceObjectName().toStdString().c_str() << ")";
|
||||
(*it)->handleSourceMessage(*message);
|
||||
}
|
||||
|
||||
// forward source changes to sinks with immediate execution
|
||||
|
||||
for(BasebandSampleSinks::const_iterator it = m_basebandSampleSinks.begin(); it != m_basebandSampleSinks.end(); it++)
|
||||
{
|
||||
qDebug() << "DSPDeviceSourceEngine::handleInputMessages: forward message to " << (*it)->objectName().toStdString().c_str();
|
||||
(*it)->handleMessage(*message);
|
||||
}
|
||||
|
||||
// forward changes to listeners on DSP output queue
|
||||
|
||||
DSPSignalNotification* rep = new DSPSignalNotification(*notif); // make a copy for the output queue
|
||||
m_outputMessageQueue.push(rep);
|
||||
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,121 +1,128 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 SDRBASE_DSP_DSPDEVICESINKENGINE_H_
|
||||
#define SDRBASE_DSP_DSPDEVICESINKENGINE_H_
|
||||
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
#include <list>
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "dsp/fftwindow.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/syncmessenger.h"
|
||||
#include "util/export.h"
|
||||
|
||||
class DeviceSampleSink;
|
||||
class BasebandSampleSource;
|
||||
class ThreadedBasebandSampleSource; // TODO: TBD
|
||||
|
||||
class SDRANGEL_API DSPDeviceSinkEngine : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum State {
|
||||
StNotStarted, //!< engine is before initialization
|
||||
StIdle, //!< engine is idle
|
||||
StReady, //!< engine is ready to run
|
||||
StRunning, //!< engine is running
|
||||
StError //!< engine is in error
|
||||
};
|
||||
|
||||
DSPDeviceSinkEngine(uint uid, QObject* parent = NULL);
|
||||
~DSPDeviceSinkEngine();
|
||||
|
||||
uint getUID() const { return m_uid; }
|
||||
|
||||
MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
MessageQueue* getOutputMessageQueue() { return &m_outputMessageQueue; }
|
||||
|
||||
void start(); //!< This thread start
|
||||
void stop(); //!< This thread stop
|
||||
|
||||
bool initGeneration(); //!< Initialize generation sequence
|
||||
bool startGeneration(); //!< Start generation sequence
|
||||
void stopGeneration(); //!< Stop generation sequence
|
||||
|
||||
void setSink(DeviceSampleSink* sink); //!< Set the sample sink type
|
||||
void setSinkSequence(int sequence); //!< Set the sample sink sequence in type
|
||||
|
||||
void addSource(BasebandSampleSource* source); //!< Add a baseband sample source
|
||||
void removeSink(BasebandSampleSource* source); //!< Remove a baseband sample source
|
||||
|
||||
void addThreadedSource(ThreadedBasebandSampleSource* source); //!< Add a baseband sample source that will run on its own thread
|
||||
void removeThreadedSource(ThreadedBasebandSampleSource* source); //!< Remove a baseband sample source that runs on its own thread
|
||||
|
||||
State state() const { return m_state; } //!< Return DSP engine current state
|
||||
|
||||
QString errorMessage(); //!< Return the current error message
|
||||
QString sinkDeviceDescription(); //!< Return the sink device description
|
||||
|
||||
private:
|
||||
uint m_uid; //!< unique ID
|
||||
|
||||
MessageQueue m_inputMessageQueue; //<! Input message queue. Post here.
|
||||
MessageQueue m_outputMessageQueue; //<! Output message queue. Listen here.
|
||||
SyncMessenger m_syncMessenger; //!< Used to process messages synchronously with the thread
|
||||
|
||||
State m_state;
|
||||
|
||||
QString m_errorMessage;
|
||||
QString m_deviceDescription;
|
||||
|
||||
DeviceSampleSink* m_deviceSampleSink;
|
||||
int m_sampleSinkSequence;
|
||||
|
||||
typedef std::list<BasebandSampleSource*> BasebandSampleSources;
|
||||
BasebandSampleSources m_basebandSampleSources; //!< baseband sample sources within main thread (usually file input)
|
||||
|
||||
typedef std::list<ThreadedBasebandSampleSource*> ThreadedBasebandSampleSources;
|
||||
ThreadedBasebandSampleSources m_threadedBasebandSampleSources; //!< baseband sample sources on their own threads (usually channels)
|
||||
|
||||
uint m_sampleRate;
|
||||
quint64 m_centerFrequency;
|
||||
|
||||
void run();
|
||||
void work(); //!< transfer samples from beseband sources to sink if in running state
|
||||
|
||||
State gotoIdle(); //!< Go to the idle state
|
||||
State gotoInit(); //!< Go to the acquisition init state from idle
|
||||
State gotoRunning(); //!< Go to the running state from ready state
|
||||
State gotoError(const QString& errorMsg); //!< Go to an error state
|
||||
|
||||
void handleSetSink(DeviceSampleSink* sink); //!< Manage sink setting
|
||||
|
||||
private slots:
|
||||
void handleData(); //!< Handle data when samples from source FIFO are ready to be processed
|
||||
void handleInputMessages(); //!< Handle input message queue
|
||||
void handleSynchronousMessages(); //!< Handle synchronous messages with the thread
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* SDRBASE_DSP_DSPDEVICESINKENGINE_H_ */
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 SDRBASE_DSP_DSPDEVICESINKENGINE_H_
|
||||
#define SDRBASE_DSP_DSPDEVICESINKENGINE_H_
|
||||
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
#include <list>
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "dsp/fftwindow.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/syncmessenger.h"
|
||||
#include "util/export.h"
|
||||
|
||||
class DeviceSampleSink;
|
||||
class BasebandSampleSource;
|
||||
class ThreadedBasebandSampleSource;
|
||||
class BasebandSampleSink;
|
||||
|
||||
class SDRANGEL_API DSPDeviceSinkEngine : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum State {
|
||||
StNotStarted, //!< engine is before initialization
|
||||
StIdle, //!< engine is idle
|
||||
StReady, //!< engine is ready to run
|
||||
StRunning, //!< engine is running
|
||||
StError //!< engine is in error
|
||||
};
|
||||
|
||||
DSPDeviceSinkEngine(uint uid, QObject* parent = NULL);
|
||||
~DSPDeviceSinkEngine();
|
||||
|
||||
uint getUID() const { return m_uid; }
|
||||
|
||||
MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
MessageQueue* getOutputMessageQueue() { return &m_outputMessageQueue; }
|
||||
|
||||
void start(); //!< This thread start
|
||||
void stop(); //!< This thread stop
|
||||
|
||||
bool initGeneration(); //!< Initialize generation sequence
|
||||
bool startGeneration(); //!< Start generation sequence
|
||||
void stopGeneration(); //!< Stop generation sequence
|
||||
|
||||
void setSink(DeviceSampleSink* sink); //!< Set the sample sink type
|
||||
void setSinkSequence(int sequence); //!< Set the sample sink sequence in type
|
||||
|
||||
void addSource(BasebandSampleSource* source); //!< Add a baseband sample source
|
||||
void removeSource(BasebandSampleSource* source); //!< Remove a baseband sample source
|
||||
|
||||
void addThreadedSource(ThreadedBasebandSampleSource* source); //!< Add a baseband sample source that will run on its own thread
|
||||
void removeThreadedSource(ThreadedBasebandSampleSource* source); //!< Remove a baseband sample source that runs on its own thread
|
||||
|
||||
void addSink(BasebandSampleSink* sink); //!< Add a baseband sample sink
|
||||
void removeSink(BasebandSampleSink* sink); //!< Remove a baseband sample sink
|
||||
|
||||
State state() const { return m_state; } //!< Return DSP engine current state
|
||||
|
||||
QString errorMessage(); //!< Return the current error message
|
||||
QString sinkDeviceDescription(); //!< Return the sink device description
|
||||
|
||||
private:
|
||||
uint m_uid; //!< unique ID
|
||||
|
||||
MessageQueue m_inputMessageQueue; //<! Input message queue. Post here.
|
||||
MessageQueue m_outputMessageQueue; //<! Output message queue. Listen here.
|
||||
SyncMessenger m_syncMessenger; //!< Used to process messages synchronously with the thread
|
||||
|
||||
State m_state;
|
||||
|
||||
QString m_errorMessage;
|
||||
QString m_deviceDescription;
|
||||
|
||||
DeviceSampleSink* m_deviceSampleSink;
|
||||
int m_sampleSinkSequence;
|
||||
|
||||
typedef std::list<BasebandSampleSource*> BasebandSampleSources;
|
||||
BasebandSampleSources m_basebandSampleSources; //!< baseband sample sources within main thread (usually file input)
|
||||
|
||||
typedef std::list<ThreadedBasebandSampleSource*> ThreadedBasebandSampleSources;
|
||||
ThreadedBasebandSampleSources m_threadedBasebandSampleSources; //!< baseband sample sources on their own threads (usually channels)
|
||||
|
||||
typedef std::list<BasebandSampleSink*> BasebandSampleSinks;
|
||||
BasebandSampleSinks m_basebandSampleSinks; //!< baseband sample sinks within main thread (this is only the spectrum vis normally)
|
||||
|
||||
uint m_sampleRate;
|
||||
quint64 m_centerFrequency;
|
||||
|
||||
void run();
|
||||
void work(); //!< transfer samples from beseband sources to sink if in running state
|
||||
|
||||
State gotoIdle(); //!< Go to the idle state
|
||||
State gotoInit(); //!< Go to the acquisition init state from idle
|
||||
State gotoRunning(); //!< Go to the running state from ready state
|
||||
State gotoError(const QString& errorMsg); //!< Go to an error state
|
||||
|
||||
void handleSetSink(DeviceSampleSink* sink); //!< Manage sink setting
|
||||
|
||||
private slots:
|
||||
void handleData(); //!< Handle data when samples from source FIFO are ready to be processed
|
||||
void handleInputMessages(); //!< Handle input message queue
|
||||
void handleSynchronousMessages(); //!< Handle synchronous messages with the thread
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* SDRBASE_DSP_DSPDEVICESINKENGINE_H_ */
|
||||
|
@ -348,6 +348,8 @@ DSPDeviceSourceEngine::State DSPDeviceSourceEngine::gotoIdle()
|
||||
(*it)->stop();
|
||||
}
|
||||
|
||||
// TODO: why not stopping the threaded baseband sample sinks?
|
||||
|
||||
m_deviceSampleSource->stop();
|
||||
m_deviceDescription.clear();
|
||||
m_sampleRate = 0;
|
||||
|
@ -40,6 +40,7 @@ struct Sample
|
||||
|
||||
inline Sample& operator+=(const Sample& other) { m_real += other.m_real; m_imag += other.m_imag; return *this; }
|
||||
inline Sample& operator-=(const Sample& other) { m_real -= other.m_real; m_imag -= other.m_imag; return *this; }
|
||||
inline Sample& operator/=(const unsigned int& divisor) { m_real /= divisor; m_imag /= divisor; return *this; }
|
||||
|
||||
inline void setReal(FixReal v) { m_real = v; }
|
||||
inline void setImag(FixReal v) { m_imag = v; }
|
||||
|
@ -20,34 +20,6 @@
|
||||
|
||||
#include "dsp/threadedbasebandsamplesource.h"
|
||||
|
||||
ThreadedBasebandSampleSourceFifo::ThreadedBasebandSampleSourceFifo(BasebandSampleSource* sampleSource) :
|
||||
m_sampleSource(sampleSource),
|
||||
m_sampleSourceFifo(1<<19, 1<<17)
|
||||
{
|
||||
m_samplesChunkSize = m_sampleSourceFifo.getChunkSize();
|
||||
connect(&m_sampleSourceFifo, SIGNAL(dataWrite()), this, SLOT(handleFifoData()));
|
||||
}
|
||||
|
||||
ThreadedBasebandSampleSourceFifo::~ThreadedBasebandSampleSourceFifo()
|
||||
{
|
||||
}
|
||||
|
||||
void ThreadedBasebandSampleSourceFifo::readFromFifo(SampleVector::iterator& beginRead, unsigned int nbSamples)
|
||||
{
|
||||
m_sampleSourceFifo.read(beginRead, nbSamples);
|
||||
}
|
||||
|
||||
void ThreadedBasebandSampleSourceFifo::handleFifoData()
|
||||
{
|
||||
for (int i = 0; i < m_samplesChunkSize; i++)
|
||||
{
|
||||
SampleVector::iterator sampleIt;
|
||||
m_sampleSourceFifo.getWriteIterator(sampleIt);
|
||||
m_sampleSource->pull(*sampleIt);
|
||||
m_sampleSourceFifo.bumpIndex();
|
||||
}
|
||||
}
|
||||
|
||||
ThreadedBasebandSampleSource::ThreadedBasebandSampleSource(BasebandSampleSource* sampleSource, QObject *parent) :
|
||||
m_basebandSampleSource(sampleSource)
|
||||
{
|
||||
@ -57,16 +29,13 @@ ThreadedBasebandSampleSource::ThreadedBasebandSampleSource(BasebandSampleSource*
|
||||
qDebug() << "ThreadedBasebandSampleSource::ThreadedBasebandSampleSource: " << name;
|
||||
|
||||
m_thread = new QThread(parent);
|
||||
m_threadedBasebandSampleSourceFifo = new ThreadedBasebandSampleSourceFifo(m_basebandSampleSource);
|
||||
m_basebandSampleSource->moveToThread(m_thread);
|
||||
m_threadedBasebandSampleSourceFifo->moveToThread(m_thread);
|
||||
|
||||
qDebug() << "ThreadedBasebandSampleSource::ThreadedBasebandSampleSource: thread: " << thread() << " m_thread: " << m_thread;
|
||||
}
|
||||
|
||||
ThreadedBasebandSampleSource::~ThreadedBasebandSampleSource()
|
||||
{
|
||||
delete m_threadedBasebandSampleSourceFifo;
|
||||
delete m_thread;
|
||||
}
|
||||
|
||||
@ -85,9 +54,9 @@ void ThreadedBasebandSampleSource::stop()
|
||||
m_thread->wait();
|
||||
}
|
||||
|
||||
void ThreadedBasebandSampleSource::pull(SampleVector::iterator& beginRead, unsigned int nbSamples)
|
||||
void ThreadedBasebandSampleSource::pull(Sample& sample)
|
||||
{
|
||||
m_threadedBasebandSampleSourceFifo->readFromFifo(beginRead, nbSamples);
|
||||
m_basebandSampleSource->pull(sample);
|
||||
}
|
||||
|
||||
bool ThreadedBasebandSampleSource::handleSourceMessage(const Message& cmd)
|
||||
|
@ -1,81 +1,58 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 SDRBASE_DSP_THREADEDBASEBANDSAMPLESOURCE_H_
|
||||
#define SDRBASE_DSP_THREADEDBASEBANDSAMPLESOURCE_H_
|
||||
|
||||
#include <dsp/basebandsamplesource.h>
|
||||
#include <QMutex>
|
||||
|
||||
#include "samplesourcefifo.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/export.h"
|
||||
|
||||
class BasebandSampleSource;
|
||||
class QThread;
|
||||
|
||||
/**
|
||||
* Because Qt is a piece of shit this class cannot be a nested protected class of ThreadedSampleSource
|
||||
* So let's make everything public
|
||||
*/
|
||||
class ThreadedBasebandSampleSourceFifo : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ThreadedBasebandSampleSourceFifo(BasebandSampleSource* sampleSource);
|
||||
~ThreadedBasebandSampleSourceFifo();
|
||||
void readFromFifo(SampleVector::iterator& beginRead, unsigned int nbSamples);
|
||||
|
||||
BasebandSampleSource* m_sampleSource;
|
||||
SampleSourceFifo m_sampleSourceFifo;
|
||||
|
||||
public slots:
|
||||
void handleFifoData();
|
||||
|
||||
private:
|
||||
unsigned int m_samplesChunkSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class is a wrapper for BasebandSampleSource that runs the BasebandSampleSource object in its own thread
|
||||
*/
|
||||
class SDRANGEL_API ThreadedBasebandSampleSource : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ThreadedBasebandSampleSource(BasebandSampleSource* sampleSource, QObject *parent = 0);
|
||||
~ThreadedBasebandSampleSource();
|
||||
|
||||
const BasebandSampleSource *getSource() const { return m_basebandSampleSource; }
|
||||
MessageQueue* getInputMessageQueue() { return m_basebandSampleSource->getInputMessageQueue(); } //!< Return pointer to sample source's input message queue
|
||||
MessageQueue* getOutputMessageQueue() { return m_basebandSampleSource->getOutputMessageQueue(); } //!< Return pointer to sample source's output message queue
|
||||
|
||||
void start(); //!< this thread start()
|
||||
void stop(); //!< this thread exit() and wait()
|
||||
|
||||
bool handleSourceMessage(const Message& cmd); //!< Send message to source synchronously
|
||||
void pull(SampleVector::iterator& beginRead, unsigned int nbSamples); //!< Pull samples from source
|
||||
|
||||
QString getSampleSourceObjectName() const;
|
||||
|
||||
protected:
|
||||
QThread *m_thread; //!< The thead object
|
||||
ThreadedBasebandSampleSourceFifo *m_threadedBasebandSampleSourceFifo;
|
||||
BasebandSampleSource* m_basebandSampleSource;
|
||||
};
|
||||
|
||||
#endif /* SDRBASE_DSP_THREADEDBASEBANDSAMPLESOURCE_H_ */
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 SDRBASE_DSP_THREADEDBASEBANDSAMPLESOURCE_H_
|
||||
#define SDRBASE_DSP_THREADEDBASEBANDSAMPLESOURCE_H_
|
||||
|
||||
#include <dsp/basebandsamplesource.h>
|
||||
#include <QMutex>
|
||||
|
||||
#include "samplesourcefifo.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/export.h"
|
||||
|
||||
class BasebandSampleSource;
|
||||
class QThread;
|
||||
|
||||
/**
|
||||
* This class is a wrapper for BasebandSampleSource that runs the BasebandSampleSource object in its own thread
|
||||
*/
|
||||
class SDRANGEL_API ThreadedBasebandSampleSource : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ThreadedBasebandSampleSource(BasebandSampleSource* sampleSource, QObject *parent = 0);
|
||||
~ThreadedBasebandSampleSource();
|
||||
|
||||
const BasebandSampleSource *getSource() const { return m_basebandSampleSource; }
|
||||
MessageQueue* getInputMessageQueue() { return m_basebandSampleSource->getInputMessageQueue(); } //!< Return pointer to sample source's input message queue
|
||||
MessageQueue* getOutputMessageQueue() { return m_basebandSampleSource->getOutputMessageQueue(); } //!< Return pointer to sample source's output message queue
|
||||
|
||||
void start(); //!< this thread start()
|
||||
void stop(); //!< this thread exit() and wait()
|
||||
|
||||
bool handleSourceMessage(const Message& cmd); //!< Send message to source synchronously
|
||||
void pull(Sample& sample); //!< Pull one sample from source
|
||||
|
||||
QString getSampleSourceObjectName() const;
|
||||
|
||||
protected:
|
||||
QThread *m_thread; //!< The thead object
|
||||
BasebandSampleSource* m_basebandSampleSource;
|
||||
};
|
||||
|
||||
#endif /* SDRBASE_DSP_THREADEDBASEBANDSAMPLESOURCE_H_ */
|
||||
|
@ -40,6 +40,7 @@ SOURCES += mainwindow.cpp\
|
||||
dsp/dspcommands.cpp\
|
||||
dsp/dspengine.cpp\
|
||||
dsp/dspdevicesourceengine.cpp\
|
||||
dsp/dspdevicesinkengine.cpp\
|
||||
dsp/dvserialengine.cpp\
|
||||
dsp/dvserialworker.cpp\
|
||||
dsp/fftengine.cpp\
|
||||
@ -120,6 +121,7 @@ HEADERS += mainwindow.h\
|
||||
dsp/dspcommands.h\
|
||||
dsp/dspengine.h\
|
||||
dsp/dspdevicesourceengine.h\
|
||||
dsp/dspdevicesinkengine.h\
|
||||
dsp/dvserialengine.h\
|
||||
dsp/dvserialworker.h\
|
||||
dsp/dsptypes.h\
|
||||
|
Loading…
Reference in New Issue
Block a user