diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index f871e7958..b2f4ed908 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -69,6 +69,7 @@ set(sdrbase_SOURCES device/devicesourceapi.cpp device/devicesinkapi.cpp + device/deviceapi.cpp device/deviceenumerator.cpp settings/preferences.cpp @@ -190,6 +191,7 @@ set(sdrbase_HEADERS device/devicesourceapi.h device/devicesinkapi.h + device/deviceapi.h device/deviceenumerator.h plugin/plugininstancegui.h diff --git a/sdrbase/device/deviceapi.cpp b/sdrbase/device/deviceapi.cpp new file mode 100644 index 000000000..60bef4790 --- /dev/null +++ b/sdrbase/device/deviceapi.cpp @@ -0,0 +1,749 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 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 // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "plugin/plugininstancegui.h" +#include "plugin/plugininterface.h" +#include "device/devicesourceapi.h" +#include "device/devicesinkapi.h" +#include "dsp/dspengine.h" +#include "dsp/devicesamplesource.h" +#include "dsp/devicesamplesink.h" +#include "settings/preset.h" +#include "channel/channelsinkapi.h" +#include "channel/channelsourceapi.h" + +#include "deviceapi.h" + +DeviceAPI::DeviceAPI( + StreamType streamType, + int deviceTabIndex, + DSPDeviceSourceEngine *deviceSourceEngine, + DSPDeviceSinkEngine *deviceSinkEngine +) : + m_streamType(streamType), + m_deviceTabIndex(deviceTabIndex), + m_nbItems(1), + m_itemIndex(0), + m_pluginInterface(0), + m_masterTimer(DSPEngine::instance()->getMasterTimer()), + m_buddySharedPtr(0), + m_isBuddyLeader(false), + m_deviceSourceEngine(deviceSourceEngine), + m_sampleSourceSequence(0), + m_sampleSourcePluginInstanceUI(0), + m_deviceSinkEngine(deviceSinkEngine), + m_sampleSinkSequence(0), + m_sampleSinkPluginInstanceUI(0) +{ +} + +DeviceAPI::~DeviceAPI() +{ +} + +void DeviceAPI::addAncillarySink(BasebandSampleSink *sink) +{ + if (m_deviceSourceEngine) { + m_deviceSourceEngine->addSink(sink); + } else if (m_deviceSinkEngine) { + m_deviceSinkEngine->addSpectrumSink(sink); + } +} + +void DeviceAPI::removeAncillarySink(BasebandSampleSink* sink) +{ + if (m_deviceSourceEngine) { + m_deviceSourceEngine->removeSink(sink); + } else if (m_deviceSinkEngine) { + m_deviceSinkEngine->removeSpectrumSink(sink); + } +} + +void DeviceAPI::addChannelSink(ThreadedBasebandSampleSink* sink, int streamIndex) +{ + (void) streamIndex; + + if (m_deviceSourceEngine) { + m_deviceSourceEngine->addThreadedSink(sink); + } +} + +void DeviceAPI::removeChannelSink(ThreadedBasebandSampleSink* sink, int streamIndex) +{ + (void) streamIndex; + + if (m_deviceSourceEngine) { + m_deviceSourceEngine->removeThreadedSink(sink); + } +} + +void DeviceAPI::addChannelSource(ThreadedBasebandSampleSource* source, int streamIndex) +{ + (void) streamIndex; + + if (m_deviceSinkEngine) { + m_deviceSinkEngine->addThreadedSource(source); + } +} + +void DeviceAPI::removeChannelSource(ThreadedBasebandSampleSource* source, int streamIndex) +{ + (void) streamIndex; + + if (m_deviceSinkEngine) { + m_deviceSinkEngine->removeThreadedSource(source); + } +} + +void DeviceAPI::addChannelSinkAPI(ChannelSinkAPI* channelAPI) +{ + m_channelSinkAPIs.append(channelAPI); + renumerateChannels(); +} + +void DeviceAPI::removeChannelSinkAPI(ChannelSinkAPI* channelAPI) +{ + if (m_channelSinkAPIs.removeOne(channelAPI)) { + renumerateChannels(); + } + + channelAPI->setIndexInDeviceSet(-1); +} + +void DeviceAPI::addChannelSourceAPI(ChannelSourceAPI* channelAPI) +{ + m_channelSourceAPIs.append(channelAPI); + renumerateChannels(); +} + +void DeviceAPI::removeChannelSourceAPI(ChannelSourceAPI* channelAPI) +{ + if (m_channelSourceAPIs.removeOne(channelAPI)) { + renumerateChannels(); + } + + channelAPI->setIndexInDeviceSet(-1); +} + +void DeviceAPI::setSampleSource(DeviceSampleSource* source) +{ + if (m_deviceSourceEngine) { + m_deviceSourceEngine->setSource(source); + } +} + +void DeviceAPI::setSampleSink(DeviceSampleSink* sink) +{ + if (m_deviceSinkEngine) { + m_deviceSinkEngine->setSink(sink); + } +} + +DeviceSampleSource *DeviceAPI::getSampleSource() +{ + if (m_deviceSourceEngine) { + return m_deviceSourceEngine->getSource(); + } else { + return nullptr; + } +} + +DeviceSampleSink *DeviceAPI::getSampleSink() +{ + if (m_deviceSinkEngine) { + return m_deviceSinkEngine->getSink(); + } else { + return nullptr; + } +} + +bool DeviceAPI::initDeviceEngine() +{ + if (m_deviceSourceEngine) { + return m_deviceSourceEngine->initAcquisition(); + } else if (m_deviceSinkEngine) { + return m_deviceSinkEngine->initGeneration(); + } else { + return false; // TODO: not implemented + } +} + +bool DeviceAPI::startDeviceEngine() +{ + if (m_deviceSourceEngine) { + return m_deviceSourceEngine->startAcquisition(); + } else if (m_deviceSinkEngine) { + return m_deviceSinkEngine->startGeneration(); + } else { + return false; // TODO: not implemented + } +} + +void DeviceAPI::stopDeviceEngine() +{ + if (m_deviceSourceEngine) { + m_deviceSourceEngine->stopAcquistion(); + } else if (m_deviceSinkEngine) { + m_deviceSinkEngine->stopGeneration(); + } +} + +DeviceAPI::EngineState DeviceAPI::state() const +{ + if (m_deviceSourceEngine) { + return (DeviceAPI::EngineState) m_deviceSourceEngine->state(); + } else if (m_deviceSinkEngine) { + return (DeviceAPI::EngineState) m_deviceSinkEngine->state(); + } else { + return StError; // TODO: not implemented + } +} + +QString DeviceAPI::errorMessage() +{ + if (m_deviceSourceEngine) { + return m_deviceSourceEngine->errorMessage(); + } else if (m_deviceSinkEngine) { + return m_deviceSinkEngine->errorMessage(); + } else { + return "Not implemented"; // TODO: not implemented + } +} + +uint DeviceAPI::getDeviceUID() const +{ + if (m_deviceSourceEngine) { + return m_deviceSourceEngine->getUID(); + } else if (m_deviceSinkEngine) { + return m_deviceSinkEngine->getUID(); + } else { + return 0; // TODO: not implemented + } +} + +MessageQueue *DeviceAPI::getDeviceEngineInputMessageQueue() +{ + if (m_deviceSourceEngine) { + return m_deviceSourceEngine->getInputMessageQueue(); + } else if (m_deviceSinkEngine) { + return m_deviceSinkEngine->getInputMessageQueue(); + } else { + return nullptr; // TODO: not implemented + } +} + +MessageQueue *DeviceAPI::getSamplingDeviceInputMessageQueue() +{ + if (m_deviceSourceEngine) { + return m_deviceSourceEngine->getSource()->getInputMessageQueue(); + } else if (m_deviceSinkEngine) { + return m_deviceSinkEngine->getSink()->getInputMessageQueue(); + } else { + return nullptr; // TODO: not implemented + } +} + +MessageQueue *DeviceAPI::getSamplingDeviceGUIMessageQueue() +{ + if (m_deviceSourceEngine) { + return m_deviceSourceEngine->getSource()->getMessageQueueToGUI(); + } else if (m_deviceSinkEngine) { + return m_deviceSinkEngine->getSink()->getMessageQueueToGUI(); + } else { + return nullptr; // TODO: not implemented + } +} + +void DeviceAPI::configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection, int streamIndex) +{ + (void) streamIndex; + + if (m_deviceSourceEngine) { + m_deviceSourceEngine->configureCorrections(dcOffsetCorrection, iqImbalanceCorrection); + } +} + +void DeviceAPI::setHardwareId(const QString& id) +{ + m_hardwareId = id; +} + +void DeviceAPI::setSamplingDeviceId(const QString& id) +{ + if (m_streamType == StreamSingleRx) { + m_sampleSourceId = id; + } else if (m_streamType == StreamSingleTx) { + m_sampleSinkId = id; + } +} + +void DeviceAPI::resetSamplingDeviceId() +{ + if (m_streamType == StreamSingleRx) { + m_sampleSourceId.clear(); + } else if (m_streamType == StreamSingleTx) { + m_sampleSinkId.clear(); + } +} + +void DeviceAPI::setSamplingDeviceSerial(const QString& serial) +{ + if (m_streamType == StreamSingleRx) { + m_sampleSourceSerial = serial; + } else if (m_streamType == StreamSingleTx) { + m_sampleSinkSerial = serial; + } +} + +void DeviceAPI::setSamplingDeviceDisplayName(const QString& name) +{ + if (m_streamType == StreamSingleRx) { + m_sampleSourceDisplayName = name; + } else if (m_streamType == StreamSingleTx) { + m_sampleSinkDisplayName = name; + } +} + +void DeviceAPI::setSamplingDeviceSequence(int sequence) +{ + if (m_deviceSourceEngine) + { + m_sampleSourceSequence = sequence; + m_deviceSourceEngine->setSourceSequence(sequence); + } + else if (m_deviceSinkEngine) + { + m_sampleSinkSequence = sequence; + m_deviceSinkEngine->setSinkSequence(sequence); + } +} + +void DeviceAPI::setNbItems(uint32_t nbItems) +{ + m_nbItems = nbItems; +} + +void DeviceAPI::setItemIndex(uint32_t index) +{ + m_itemIndex = index; +} + +void DeviceAPI::setSamplingDevicePluginInterface(PluginInterface *iface) +{ + m_pluginInterface = iface; +} + +void DeviceAPI::setSamplingDevicePluginInstanceGUI(PluginInstanceGUI *gui) +{ + m_sampleSinkPluginInstanceUI = gui; +} + +void DeviceAPI::getDeviceEngineStateStr(QString& state) +{ + if (m_deviceSourceEngine) + { + switch(m_deviceSourceEngine->state()) + { + case DSPDeviceSourceEngine::StNotStarted: + state = "notStarted"; + break; + case DSPDeviceSourceEngine::StIdle: + state = "idle"; + break; + case DSPDeviceSourceEngine::StReady: + state = "ready"; + break; + case DSPDeviceSourceEngine::StRunning: + state = "running"; + break; + case DSPDeviceSourceEngine::StError: + state = "error"; + break; + default: + state = "notStarted"; + break; + } + } + else if (m_deviceSinkEngine) + { + switch(m_deviceSinkEngine->state()) + { + case DSPDeviceSinkEngine::StNotStarted: + state = "notStarted"; + break; + case DSPDeviceSinkEngine::StIdle: + state = "idle"; + break; + case DSPDeviceSinkEngine::StReady: + state = "ready"; + break; + case DSPDeviceSinkEngine::StRunning: + state = "running"; + break; + case DSPDeviceSinkEngine::StError: + state = "error"; + break; + default: + state = "notStarted"; + break; + } + } + else + { + state = "notStarted"; + } +} + +ChannelSinkAPI *DeviceAPI::getChanelSinkAPIAt(int index) +{ + if (m_streamType == StreamSingleRx) + { + if (index < m_channelSinkAPIs.size()) { + return m_channelSinkAPIs.at(index); + } else { + return nullptr; + } + } + else // TODO: not implemented + { + return nullptr; + } +} + +ChannelSourceAPI *DeviceAPI::getChanelSourceAPIAt(int index) +{ + if (m_streamType == StreamSingleTx) + { + if (index < m_channelSourceAPIs.size()) { + return m_channelSourceAPIs.at(index); + } else { + return nullptr; + } + } + else // TODO: not implemented + { + return nullptr; + } +} + +void DeviceAPI::loadSamplingDeviceSettings(const Preset* preset) +{ + if (m_deviceSourceEngine && (preset->isSourcePreset())) + { + qDebug("DeviceAPI::loadSamplingDeviceSettings: Loading preset [%s | %s]", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); + + const QByteArray* sourceConfig = preset->findBestDeviceConfig(m_sampleSourceId, m_sampleSourceSerial, m_sampleSourceSequence); + qint64 centerFrequency = preset->getCenterFrequency(); + qDebug("DeviceAPI::loadSamplingDeviceSettings: center frequency: %llu Hz", centerFrequency); + + if (sourceConfig != 0) + { + qDebug("DeviceAPI::loadSamplingDeviceSettings: deserializing source %s[%d]: %s", qPrintable(m_sampleSourceId), m_sampleSourceSequence, qPrintable(m_sampleSourceSerial)); + + if (m_sampleSourcePluginInstanceUI != 0) // GUI flavor + { + m_sampleSourcePluginInstanceUI->deserialize(*sourceConfig); + } + else if (m_deviceSourceEngine->getSource() != 0) // Server flavor + { + m_deviceSourceEngine->getSource()->deserialize(*sourceConfig); + } + else + { + qDebug("DeviceAPI::loadSamplingDeviceSettings: deserializing no source"); + } + } + else + { + qDebug("DeviceAPI::loadSamplingDeviceSettings: source %s[%d]: %s not found", qPrintable(m_sampleSourceId), m_sampleSourceSequence, qPrintable(m_sampleSourceSerial)); + } + + // set center frequency anyway + if (m_sampleSourcePluginInstanceUI != 0) // GUI flavor + { + m_sampleSourcePluginInstanceUI->setCenterFrequency(centerFrequency); + } + else if (m_deviceSourceEngine->getSource() != 0) // Server flavor + { + m_deviceSourceEngine->getSource()->setCenterFrequency(centerFrequency); + } + else + { + qDebug("DeviceAPI::loadSamplingDeviceSettings: no source"); + } + } + else if (m_deviceSinkEngine && (!preset->isSourcePreset())) // TODO: refine preset stream type + { + qDebug("DeviceAPI::loadSamplingDeviceSettings: Loading preset [%s | %s]", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); + + const QByteArray* sinkConfig = preset->findBestDeviceConfig(m_sampleSinkId, m_sampleSinkSerial, m_sampleSinkSequence); + qint64 centerFrequency = preset->getCenterFrequency(); + qDebug("DeviceAPI::loadSamplingDeviceSettings: center frequency: %llu Hz", centerFrequency); + + if (sinkConfig != 0) + { + qDebug("DeviceAPI::loadSamplingDeviceSettings: deserializing sink %s[%d]: %s", qPrintable(m_sampleSinkId), m_sampleSinkSequence, qPrintable(m_sampleSinkSerial)); + + if (m_sampleSinkPluginInstanceUI != 0) // GUI flavor + { + m_sampleSinkPluginInstanceUI->deserialize(*sinkConfig); + m_sampleSinkPluginInstanceUI->setCenterFrequency(centerFrequency); + } + else if (m_deviceSinkEngine->getSink() != 0) // Server flavor + { + m_deviceSinkEngine->getSink()->deserialize(*sinkConfig); + m_deviceSinkEngine->getSink()->setCenterFrequency(centerFrequency); + } + else + { + qDebug("DeviceAPI::loadSamplingDeviceSettings: no sink"); + } + } + else + { + qDebug("DeviceAPI::loadSamplingDeviceSettings: sink %s[%d]: %s not found", qPrintable(m_sampleSinkId), m_sampleSinkSequence, qPrintable(m_sampleSinkSerial)); + } + } + else + { + qDebug("DeviceAPI::loadSamplingDeviceSettings: Loading preset [%s | %s] is not a suitable preset", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); + } +} + +void DeviceAPI::saveSamplingDeviceSettings(Preset* preset) +{ + if (m_deviceSourceEngine && (preset->isSourcePreset())) + { + qDebug("DeviceAPI::saveSamplingDeviceSettings: serializing source %s[%d]: %s", qPrintable(m_sampleSourceId), m_sampleSourceSequence, qPrintable(m_sampleSourceSerial)); + + if (m_sampleSourcePluginInstanceUI != 0) + { + preset->addOrUpdateDeviceConfig(m_sampleSourceId, m_sampleSourceSerial, m_sampleSourceSequence, m_sampleSourcePluginInstanceUI->serialize()); + preset->setCenterFrequency(m_sampleSourcePluginInstanceUI->getCenterFrequency()); + } + else if (m_deviceSourceEngine->getSource() != 0) + { + preset->addOrUpdateDeviceConfig(m_sampleSourceId, m_sampleSourceSerial, m_sampleSourceSequence, m_deviceSourceEngine->getSource()->serialize()); + preset->setCenterFrequency(m_deviceSourceEngine->getSource()->getCenterFrequency()); + } + else + { + qDebug("DeviceAPI::saveSamplingDeviceSettings: no source"); + } + } + else if (m_deviceSinkEngine && (!preset->isSourcePreset())) // TODO: refine preset stream type + { + qDebug("DeviceAPI::saveSamplingDeviceSettings: serializing sink %s[%d]: %s", qPrintable(m_sampleSinkId), m_sampleSinkSequence, qPrintable(m_sampleSinkSerial)); + + if (m_sampleSinkPluginInstanceUI != 0) // GUI flavor + { + preset->addOrUpdateDeviceConfig(m_sampleSinkId, m_sampleSinkSerial, m_sampleSinkSequence, m_deviceSinkEngine->getSink()->serialize()); + preset->setCenterFrequency(m_deviceSinkEngine->getSink()->getCenterFrequency()); + } + else if (m_deviceSinkEngine->getSink() != 0) // Server flavor + { + preset->addOrUpdateDeviceConfig(m_sampleSinkId, m_sampleSinkSerial, m_sampleSinkSequence, m_deviceSinkEngine->getSink()->serialize()); + preset->setCenterFrequency(m_deviceSinkEngine->getSink()->getCenterFrequency()); + } + else + { + qDebug("DeviceAPI::saveSamplingDeviceSettings: no sink"); + } + } + else + { + qDebug("DeviceAPI::saveSamplingDeviceSettings: not a suitable preset"); + } +} + +void DeviceAPI::addSourceBuddy(DeviceAPI* buddy) +{ + if (buddy->m_streamType != StreamSingleRx) + { + qDebug("DeviceAPI::addSourceBuddy: buddy %s(%s) is not of single Rx type", + qPrintable(buddy->getHardwareId()), + qPrintable(buddy->getSamplingDeviceSerial())); + return; + } + + m_sourceBuddies.push_back(buddy); + + if (m_streamType == StreamSingleRx) { + buddy->m_sourceBuddies.push_back(this); // this is a source + } else if (m_streamType == StreamSingleTx) { + buddy->m_sinkBuddies.push_back(this); // this is a sink + } else { + qDebug("DeviceAPI::addSourceBuddy: not relevant if this is not a single Rx or Tx"); + return; + } + + qDebug("DeviceAPI::addSourceBuddy: added buddy %s(%s) [%llu] <-> [%llu]", + qPrintable(buddy->getHardwareId()), + qPrintable(buddy->getSamplingDeviceSerial()), + (quint64) buddy, + (quint64) this); +} + + +void DeviceAPI::addSinkBuddy(DeviceAPI* buddy) +{ + if (buddy->m_streamType != StreamSingleTx) + { + qDebug("DeviceAPI::addSinkBuddy: buddy %s(%s) is not of single Tx type", + qPrintable(buddy->getHardwareId()), + qPrintable(buddy->getSamplingDeviceSerial())); + return; + } + + m_sinkBuddies.push_back(buddy); + + if (m_streamType == StreamSingleRx) { + buddy->m_sourceBuddies.push_back(this); // this is a source + } else if (m_streamType == StreamSingleTx) { + buddy->m_sinkBuddies.push_back(this); // this is a sink + } else { + qDebug("DeviceAPI::addSinkBuddy: not relevant if this is not a single Rx or Tx"); + return; + } + + qDebug("DeviceAPI::addSinkBuddy: added buddy %s(%s) [%llu] <-> [%llu]", + qPrintable(buddy->getHardwareId()), + qPrintable(buddy->getSamplingDeviceSerial()), + (quint64) buddy, + (quint64) this); +} + +void DeviceAPI::removeSourceBuddy(DeviceAPI* buddy) +{ + if (buddy->m_streamType != StreamSingleRx) + { + qDebug("DeviceAPI::removeSourceBuddy: buddy %s(%s) is not of single Rx type", + qPrintable(buddy->getHardwareId()), + qPrintable(buddy->getSamplingDeviceSerial())); + return; + } + + std::vector::iterator it = m_sourceBuddies.begin(); + + for (;it != m_sourceBuddies.end(); ++it) + { + if (*it == buddy) + { + qDebug("DeviceAPI::removeSourceBuddy: buddy %s(%s) [%llu] removed from the list of [%llu]", + qPrintable(buddy->getHardwareId()), + qPrintable(buddy->getSamplingDeviceSerial()), + (quint64) (*it), + (quint64) this); + m_sourceBuddies.erase(it); + return; + } + } + + qDebug("DeviceAPI::removeSourceBuddy: buddy %s(%s) [%llu] not found in the list of [%llu]", + qPrintable(buddy->getHardwareId()), + qPrintable(buddy->getSamplingDeviceSerial()), + (quint64) buddy, + (quint64) this); +} + +void DeviceAPI::removeSinkBuddy(DeviceAPI* buddy) +{ + if (buddy->m_streamType != StreamSingleTx) + { + qDebug("DeviceAPI::removeSinkBuddy: buddy %s(%s) is not of single Tx type", + qPrintable(buddy->getHardwareId()), + qPrintable(buddy->getSamplingDeviceSerial())); + return; + } + + std::vector::iterator it = m_sinkBuddies.begin(); + + for (;it != m_sinkBuddies.end(); ++it) + { + if (*it == buddy) + { + qDebug("DeviceAPI::removeSinkBuddy: buddy %s(%s) [%llu] removed from the list of [%llu]", + qPrintable(buddy->getHardwareId()), + qPrintable(buddy->getSamplingDeviceSerial()), + (quint64) (*it), + (quint64) this); + m_sourceBuddies.erase(it); + return; + } + } + + qDebug("DeviceAPI::removeSourceBuddy: buddy %s(%s) [%llu] not found in the list of [%llu]", + qPrintable(buddy->getHardwareId()), + qPrintable(buddy->getSamplingDeviceSerial()), + (quint64) buddy, + (quint64) this); +} + +void DeviceAPI::clearBuddiesLists() +{ + std::vector::iterator itSource = m_sourceBuddies.begin(); + std::vector::iterator itSink = m_sinkBuddies.begin(); + bool leaderElected = false; + + for (;itSource != m_sourceBuddies.end(); ++itSource) + { + if (isBuddyLeader() && !leaderElected) + { + (*itSource)->setBuddyLeader(true); + leaderElected = true; + } + + (*itSource)->removeSinkBuddy(this); + } + + m_sourceBuddies.clear(); + + for (;itSink != m_sinkBuddies.end(); ++itSink) + { + if (isBuddyLeader() && !leaderElected) + { + (*itSink)->setBuddyLeader(true); + leaderElected = true; + } + + (*itSink)->removeSinkBuddy(this); + } + + m_sinkBuddies.clear(); +} + +void DeviceAPI::renumerateChannels() +{ + if (m_streamType == StreamSingleRx) + { + for (int i = 0; i < m_channelSinkAPIs.size(); ++i) + { + m_channelSinkAPIs.at(i)->setIndexInDeviceSet(i); + m_channelSinkAPIs.at(i)->setDeviceSetIndex(m_deviceTabIndex); + //m_channelSinkAPIs.at(i)->setDeviceSourceAPI(this); // FIXME: use generic DeviceAPI in ChannelSinkAPI + } + } + else if (m_streamType == StreamSingleTx) + { + for (int i = 0; i < m_channelSourceAPIs.size(); ++i) + { + m_channelSourceAPIs.at(i)->setIndexInDeviceSet(i); + m_channelSourceAPIs.at(i)->setDeviceSetIndex(m_deviceTabIndex); + //m_channelSourceAPIs.at(i)->setDeviceSinkAPI(this); // FIXME: use generic DeviceAPI in ChannelSourceAPI + } + } +} \ No newline at end of file diff --git a/sdrbase/device/deviceapi.h b/sdrbase/device/deviceapi.h new file mode 100644 index 000000000..0eb97cd38 --- /dev/null +++ b/sdrbase/device/deviceapi.h @@ -0,0 +1,233 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 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 // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_DEVICE_DEVICEAPI_H_ +#define SDRBASE_DEVICE_DEVICEAPI_H_ + +#include +#include +#include + +#include "export.h" + +class BasebandSampleSink; +class ThreadedBasebandSampleSink; +class ThreadedBasebandSampleSource; +class ChannelSinkAPI; +class ChannelSourceAPI; +class DeviceSampleSink; +class DeviceSampleSource; +class MessageQueue; +class PluginInterface; +class PluginInstanceGUI; +class DSPDeviceSourceEngine; +class DSPDeviceSinkEngine; +class DeviceSourceAPI; +class DeviceSinkAPI; +class Preset; + +class SDRBASE_API DeviceAPI : public QObject { + Q_OBJECT +public: + enum StreamType //!< This is the same enum as in PluginInterface + { + StreamSingleRx, //!< Exposes a single input stream that can be one of the streams of a physical device + StreamSingleTx, //!< Exposes a single output stream that can be one of the streams of a physical device + StreamAny //!< May expose any number of input and/or output streams + }; + + enum EngineState { + StNotStarted, //!< engine is before initialization + StIdle, //!< engine is idle + StReady, //!< engine is ready to run + StRunning, //!< engine is running + StError //!< engine is in error + }; + + DeviceAPI( + StreamType streamType, + int deviceTabIndex, + DSPDeviceSourceEngine *deviceSourceEngine, + DSPDeviceSinkEngine *deviceSinkEngine + ); + ~DeviceAPI(); + + void addAncillarySink(BasebandSampleSink* sink); //!< Adds a sink to receive full baseband and that is not a channel (e.g. spectrum) + void removeAncillarySink(BasebandSampleSink* sink); //!< Removes it + + void addChannelSink(ThreadedBasebandSampleSink* sink, int streamIndex = 0); //!< Add a channel sink (Rx) + void removeChannelSink(ThreadedBasebandSampleSink* sink, int streamIndex = 0); //!< Remove a channel sink (Rx) + void addChannelSource(ThreadedBasebandSampleSource* sink, int streamIndex = 0); //!< Add a channel source (Tx) + void removeChannelSource(ThreadedBasebandSampleSource* sink, int streamIndex = 0); //!< Remove a channel source (Tx) + + void addChannelSinkAPI(ChannelSinkAPI* channelAPI); + void removeChannelSinkAPI(ChannelSinkAPI* channelAPI); + void addChannelSourceAPI(ChannelSourceAPI* channelAPI); + void removeChannelSourceAPI(ChannelSourceAPI* channelAPI); + + void setSampleSource(DeviceSampleSource* source); //!< Set the device sample source (single Rx) + void setSampleSink(DeviceSampleSink* sink); //!< Set the device sample sink (single Tx) + DeviceSampleSource *getSampleSource(); //!< Return pointer to the device sample source (single Rx) or nullptr + DeviceSampleSink *getSampleSink(); //!< Return pointer to the device sample sink (single Tx) or nullptr + + bool initDeviceEngine(); //!< Init the device engine corresponding to the stream type + bool startDeviceEngine(); //!< Start the device engine corresponding to the stream type + void stopDeviceEngine(); //!< Stop the device engine corresponding to the stream type + EngineState state() const; //!< Return the state of the device engine corresponding to the stream type + QString errorMessage(); //!< Last error message from the device engine + uint getDeviceUID() const; //!< Return the current device engine unique ID + + MessageQueue *getDeviceEngineInputMessageQueue(); //!< Device engine message queue + MessageQueue *getSamplingDeviceInputMessageQueue(); //!< Sampling device (ex: single Rx) input message queue + // MessageQueue *getSampleSinkInputMessageQueue(); + // MessageQueue *getSampleSourceInputMessageQueue(); + MessageQueue *getSamplingDeviceGUIMessageQueue(); //!< Sampling device (ex: single Tx) GUI input message queue + // MessageQueue *getSampleSinkGUIMessageQueue(); + // MessageQueue *getSampleSourceGUIMessageQueue(); + + void configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection, int streamIndex = 0); //!< Configure current device engine DSP corrections (Rx) + + void setHardwareId(const QString& id); + void setSamplingDeviceId(const QString& id); + // void setSampleSourceId(const QString& id); + // void setSampleSinkId(const QString& id); + void resetSamplingDeviceId(); + // void resetSampleSourceId(); + // void resetSampleSinkId(); + void setSamplingDeviceSerial(const QString& serial); + // void setSampleSourceSerial(const QString& serial); + // void setSampleSinkSerial(const QString& serial); + void setSamplingDeviceDisplayName(const QString& name); + // void setSampleSourceDisplayName(const QString& serial); + // void setSampleSinkDisplayName(const QString& serial); + void setSamplingDeviceSequence(int sequence); + // void setSampleSourceSequence(int sequence); + // void setSampleSinkSequence(int sequence); + void setNbItems(uint32_t nbItems); + void setItemIndex(uint32_t index); + void setSamplingDevicePluginInterface(PluginInterface *iface); + // void setSampleSourcePluginInterface(PluginInterface *iface); + // void setSampleSinkPluginInterface(PluginInterface *iface); + void setSamplingDevicePluginInstanceGUI(PluginInstanceGUI *gui); + // void setSampleSourcePluginInstanceGUI(PluginInstanceGUI *gui); + // void setSampleSinkPluginInstanceUI(PluginInstanceGUI *gui); + + const QString& getHardwareId() const { return m_hardwareId; } + const QString& getSamplingDeviceId() const { return m_sampleSourceId; } + // const QString& getSampleSourceId() const { return m_sampleSourceId; } + // const QString& getSampleSinkId() const { return m_sampleSinkId; } + const QString& getSamplingDeviceSerial() const { return m_sampleSourceSerial; } + // const QString& getSampleSourceSerial() const { return m_sampleSourceSerial; } + // const QString& getSampleSinkSerial() const { return m_sampleSinkSerial; } + const QString& getSamplingDeviceDisplayName() const { return m_sampleSourceDisplayName; } + // const QString& getSampleSourceDisplayName() const { return m_sampleSourceDisplayName; } + // const QString& getSampleSinkDisplayName() const { return m_sampleSinkDisplayName; } + + uint32_t getSamplingDeviceSequence() const { return m_sampleSourceSequence; } + // uint32_t getSampleSourceSequence() const { return m_sampleSourceSequence; } + // uint32_t getSampleSinkSequence() const { return m_sampleSinkSequence; } + + uint32_t getNbItems() const { return m_nbItems; } + uint32_t getItemIndex() const { return m_itemIndex; } + int getDeviceSetIndex() const { return m_deviceTabIndex; } + PluginInterface *getPluginInterface() { return m_pluginInterface; } + + PluginInstanceGUI *getSamplingDevicePluginInstanceGUI() { return m_sampleSourcePluginInstanceUI; } + // PluginInstanceGUI *getSampleSourcePluginInstanceGUI() { return m_sampleSourcePluginInstanceUI; } + // PluginInstanceGUI *getSampleSinkPluginInstanceGUI() { return m_sampleSinkPluginInstanceUI; } + + void getDeviceEngineStateStr(QString& state); + + ChannelSinkAPI *getChanelSinkAPIAt(int index); + ChannelSourceAPI *getChanelSourceAPIAt(int index); + + int getNbSourceChannels() const { return m_channelSourceAPIs.size(); } + int getNbSinkChannels() const { return m_channelSinkAPIs.size(); } + + void loadSamplingDeviceSettings(const Preset* preset); + // void loadSourceSettings(const Preset* preset); + // void loadSinkSettings(const Preset* preset); + void saveSamplingDeviceSettings(Preset* preset); + // void saveSourceSettings(Preset* preset); + // void saveSinkSettings(Preset* preset); + + DSPDeviceSourceEngine *getDeviceSourceEngine() { return m_deviceSourceEngine; } + DSPDeviceSinkEngine *getDeviceSinkEngine() { return m_deviceSinkEngine; } + + void addSourceBuddy(DeviceAPI* buddy); + void addSinkBuddy(DeviceAPI* buddy); + void removeSourceBuddy(DeviceAPI* buddy); + void removeSinkBuddy(DeviceAPI* buddy); + void clearBuddiesLists(); + void *getBuddySharedPtr() const { return m_buddySharedPtr; } + void setBuddySharedPtr(void *ptr) { m_buddySharedPtr = ptr; } + bool isBuddyLeader() const { return m_isBuddyLeader; } + void setBuddyLeader(bool isBuddyLeader) { m_isBuddyLeader = isBuddyLeader; } + const std::vector& getSourceBuddies() const { return m_sourceBuddies; } + const std::vector& getSinkBuddies() const { return m_sinkBuddies; } + + const QTimer& getMasterTimer() const { return m_masterTimer; } //!< This is the DSPEngine master timer + +protected: + // common + + StreamType m_streamType; + int m_deviceTabIndex; //!< This is the tab index in the GUI and also the device set index + QString m_hardwareId; //!< The internal id that identifies the type of hardware (i.e. HackRF, BladeRF, ...) + uint32_t m_nbItems; //!< Number of items or streams in the device. Can be >1 for NxM devices (i.e. 2 for LimeSDR) + uint32_t m_itemIndex; //!< The Rx stream index. Can be >0 for NxM devices (i.e. 0 or 1 for LimeSDR) + PluginInterface* m_pluginInterface; + const QTimer& m_masterTimer; //!< This is the DSPEngine master timer + + // Buddies (single Rx or single Tx) + + std::vector m_sourceBuddies; //!< Device source APIs referencing the same physical device + std::vector m_sinkBuddies; //!< Device sink APIs referencing the same physical device + void *m_buddySharedPtr; + bool m_isBuddyLeader; + + // Single Rx (i.e. source) + + DSPDeviceSourceEngine *m_deviceSourceEngine; + QString m_sampleSourceId; //!< The internal plugin ID corresponding to the device (i.e. for HackRF input, for HackRF output ...) + QString m_sampleSourceSerial; //!< The device serial number defined by the vendor or a fake one (SDRplay) + QString m_sampleSourceDisplayName; //!< The human readable name identifying this instance + uint32_t m_sampleSourceSequence; //!< The device sequence. >0 when more than one device of the same type is connected + PluginInstanceGUI* m_sampleSourcePluginInstanceUI; // TODO: factorize + + QList m_channelSinkAPIs; + + // Single Tx (i.e. sink) + + DSPDeviceSinkEngine *m_deviceSinkEngine; + QString m_sampleSinkId; //!< The internal plugin ID corresponding to the device (i.e. for HackRF input, for HackRF output ...) + QString m_sampleSinkSerial; //!< The device serial number defined by the vendor + QString m_sampleSinkDisplayName; //!< The human readable name identifying this instance + uint32_t m_sampleSinkSequence; //!< The device sequence. >0 when more than one device of the same type is connected + PluginInstanceGUI* m_sampleSinkPluginInstanceUI; // TODO: factorize + + QList m_channelSourceAPIs; + + // Friends + + friend class DeviceSinkAPI; + friend class DeviceSourceAPI; + +private: + void renumerateChannels(); +}; +#endif // SDRBASE_DEVICE_DEVICEAPI_H_ \ No newline at end of file