diff --git a/devices/soapysdr/devicesoapysdr.h b/devices/soapysdr/devicesoapysdr.h index be697024f..c8be84557 100644 --- a/devices/soapysdr/devicesoapysdr.h +++ b/devices/soapysdr/devicesoapysdr.h @@ -33,6 +33,8 @@ public: uint32_t getNbDevices() const { return m_scanner.getNbDevices(); } const std::vector& getDevicesEnumeration() const { return m_scanner.getDevicesEnumeration(); } + static const unsigned int blockSize = (1<<14); + protected: DeviceSoapySDR(); DeviceSoapySDR(const DeviceSoapySDR&) {} diff --git a/devices/soapysdr/devicesoapysdrshared.cpp b/devices/soapysdr/devicesoapysdrshared.cpp new file mode 100644 index 000000000..16a2ef64e --- /dev/null +++ b/devices/soapysdr/devicesoapysdrshared.cpp @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "devicesoapysdrshared.h" + +DeviceSoapySDRShared::DeviceSoapySDRShared() : + m_device(0), + m_channel(-1), + m_source(0), + m_sink(0) +{} + +DeviceSoapySDRShared::~DeviceSoapySDRShared() +{} diff --git a/devices/soapysdr/devicesoapysdrshared.h b/devices/soapysdr/devicesoapysdrshared.h new file mode 100644 index 000000000..4fce21a2b --- /dev/null +++ b/devices/soapysdr/devicesoapysdrshared.h @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef DEVICES_SOAPYSDR_DEVICESOAPYSDRSHARED_H_ +#define DEVICES_SOAPYSDR_DEVICESOAPYSDRSHARED_H_ + +#include + +#include "export.h" + +class SoapySDRInput; +class SoapySDROutput; + +/** + * Structure shared by a buddy with other buddies + */ +class DEVICES_API DeviceSoapySDRShared +{ +public: + DeviceSoapySDRShared(); + ~DeviceSoapySDRShared(); + + SoapySDR::Device *m_device; + int m_channel; //!< allocated channel (-1 if none) + SoapySDRInput *m_source; + SoapySDROutput *m_sink; +}; + + +#endif /* DEVICES_SOAPYSDR_DEVICESOAPYSDRSHARED_H_ */ diff --git a/plugins/samplesink/bladerf2output/bladerf2outputsettings.h b/plugins/samplesink/bladerf2output/bladerf2outputsettings.h index b1385be41..b83cc380e 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputsettings.h +++ b/plugins/samplesink/bladerf2output/bladerf2outputsettings.h @@ -18,7 +18,6 @@ #define PLUGINS_SAMPLESINK_BLADERF2OUTPUT_BLADERF2OUTPUTSETTINGS_H_ #include -#include struct BladeRF2OutputSettings { quint64 m_centerFrequency; diff --git a/plugins/samplesink/soapysdroutput/CMakeLists.txt b/plugins/samplesink/soapysdroutput/CMakeLists.txt index e52e60f49..0015a6feb 100644 --- a/plugins/samplesink/soapysdroutput/CMakeLists.txt +++ b/plugins/samplesink/soapysdroutput/CMakeLists.txt @@ -6,7 +6,7 @@ set(soapysdroutput_SOURCES soapysdroutputgui.cpp soapysdroutput.cpp soapysdroutputplugin.cpp -# soapysdroutputsettings.cpp + soapysdroutputsettings.cpp # soapysdroutputthread.cpp ) @@ -14,7 +14,7 @@ set(soapysdroutput_HEADERS soapysdroutputgui.h soapysdroutput.h soapysdroutputplugin.h -# soapysdroutputsettings.h + soapysdroutputsettings.h # soapysdroutputthread.h ) diff --git a/plugins/samplesink/soapysdroutput/soapysdroutput.cpp b/plugins/samplesink/soapysdroutput/soapysdroutput.cpp index 2cbcc484a..d1bd40485 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutput.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutput.cpp @@ -41,7 +41,7 @@ void SoapySDROutput::destroy() bool SoapySDROutput::openDevice() { - m_sampleSourceFifo.resize(96000 * 4); + m_sampleSourceFifo.resize(m_settings.m_devSampleRate/(1<<(m_settings.m_log2Interp <= 4 ? m_settings.m_log2Interp : 4))); // look for Tx buddies and get reference to the device object if (m_deviceAPI->getSinkBuddies().size() > 0) // look sink sibling first diff --git a/plugins/samplesink/soapysdroutput/soapysdroutput.h b/plugins/samplesink/soapysdroutput/soapysdroutput.h index f69336414..d78265eae 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutput.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutput.h @@ -22,6 +22,8 @@ #include "dsp/devicesamplesink.h" #include "soapysdr/devicesoapysdrshared.h" +#include "soapysdroutputsettings.h" + class DeviceSinkAPI; class SoapySDROutput : public DeviceSampleSink { @@ -47,6 +49,7 @@ public: private: DeviceSinkAPI *m_deviceAPI; + SoapySDROutputSettings m_settings; QString m_deviceDescription; DeviceSoapySDRShared m_deviceShared; bool m_running; diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp new file mode 100644 index 000000000..140212219 --- /dev/null +++ b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include "util/simpleserializer.h" + +#include "soapysdroutputsettings.h" + +SoapySDROutputSettings::SoapySDROutputSettings() +{ + resetToDefaults(); +} + +void SoapySDROutputSettings::resetToDefaults() +{ + m_centerFrequency = 435000*1000; + m_LOppmTenths = 0; + m_devSampleRate = 1024000; + m_log2Interp = 0; + m_transverterMode = false; + m_transverterDeltaFrequency = 0; +} + +QByteArray SoapySDROutputSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeS32(1, m_devSampleRate); + s.writeS32(2, m_LOppmTenths); + s.writeU32(3, m_log2Interp); + s.writeBool(4, m_transverterMode); + s.writeS64(5, m_transverterDeltaFrequency); + + return s.final(); +} + +bool SoapySDROutputSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + d.readS32(1, &m_devSampleRate); + d.readS32(2, &m_LOppmTenths); + d.readU32(3, &m_log2Interp); + d.readBool(4, &m_transverterMode, false); + d.readS64(5, &m_transverterDeltaFrequency, 0); + + return true; + } + else + { + resetToDefaults(); + return false; + } +} diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h new file mode 100644 index 000000000..1e6f190e2 --- /dev/null +++ b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESINK_SOAPYSDROUTPUT_SOAPYSDROUTPUTSETTINGS_H_ +#define PLUGINS_SAMPLESINK_SOAPYSDROUTPUT_SOAPYSDROUTPUTSETTINGS_H_ + +#include + +struct SoapySDROutputSettings { + quint64 m_centerFrequency; + int m_LOppmTenths; + qint32 m_devSampleRate; + quint32 m_log2Interp; + bool m_transverterMode; + qint64 m_transverterDeltaFrequency; + + SoapySDROutputSettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + +#endif /* PLUGINS_SAMPLESINK_SOAPYSDROUTPUT_SOAPYSDROUTPUTSETTINGS_H_ */ diff --git a/plugins/samplesource/bladerf2input/bladerf2inputsettings.h b/plugins/samplesource/bladerf2input/bladerf2inputsettings.h index 32af13ab4..722be9e6e 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputsettings.h +++ b/plugins/samplesource/bladerf2input/bladerf2inputsettings.h @@ -19,7 +19,6 @@ #include #include -#include struct BladeRF2InputSettings { typedef enum { diff --git a/plugins/samplesource/soapysdrinput/CMakeLists.txt b/plugins/samplesource/soapysdrinput/CMakeLists.txt index 9e461fb3c..a6606a303 100644 --- a/plugins/samplesource/soapysdrinput/CMakeLists.txt +++ b/plugins/samplesource/soapysdrinput/CMakeLists.txt @@ -6,16 +6,16 @@ set(soapysdrinput_SOURCES soapysdrinputgui.cpp soapysdrinput.cpp soapysdrinputplugin.cpp -# soapysdrinputsettings.cpp -# soapysdrinputthread.cpp + soapysdrinputsettings.cpp + soapysdrinputthread.cpp ) set(soapysdrinput_HEADERS soapysdrinputgui.h soapysdrinput.h soapysdrinputplugin.h -# soapysdrinputsettings.h -# soapysdrinputthread.h + soapysdrinputsettings.h + soapysdrinputthread.h ) set(soapysdrinput_FORMS diff --git a/plugins/samplesource/soapysdrinput/soapysdrinput.cpp b/plugins/samplesource/soapysdrinput/soapysdrinput.cpp index 9bea83d1a..6c0575e0a 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinput.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinput.cpp @@ -23,10 +23,12 @@ #include "dsp/dspengine.h" #include "soapysdr/devicesoapysdr.h" +#include "soapysdrinputthread.h" #include "soapysdrinput.h" SoapySDRInput::SoapySDRInput(DeviceSourceAPI *deviceAPI) : m_deviceAPI(deviceAPI), + m_thread(0), m_deviceDescription("SoapySDRInput"), m_running(false) { @@ -131,9 +133,9 @@ void SoapySDRInput::closeDevice() stop(); } -// if (m_thread) { // stills own the thread => transfer to a buddy -// moveThreadToBuddy(); -// } + if (m_thread) { // stills own the thread => transfer to a buddy + moveThreadToBuddy(); + } m_deviceShared.m_channel = -1; // publicly release channel m_deviceShared.m_source = 0; @@ -152,6 +154,23 @@ void SoapySDRInput::init() { } +void SoapySDRInput::moveThreadToBuddy() +{ + const std::vector& sourceBuddies = m_deviceAPI->getSourceBuddies(); + std::vector::const_iterator it = sourceBuddies.begin(); + + for (; it != sourceBuddies.end(); ++it) + { + SoapySDRInput *buddySource = ((DeviceSoapySDRShared*) (*it)->getBuddySharedPtr())->m_source; + + if (buddySource) + { + buddySource->setThread(m_thread); + m_thread = 0; // zero for others + } + } +} + bool SoapySDRInput::start() { return false; diff --git a/plugins/samplesource/soapysdrinput/soapysdrinput.h b/plugins/samplesource/soapysdrinput/soapysdrinput.h index 2c03ea257..830aa0ebd 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinput.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinput.h @@ -25,6 +25,7 @@ #include "dsp/devicesamplesource.h" class DeviceSourceAPI; +class SoapySDRInputThread; class SoapySDRInput : public DeviceSampleSource { @@ -36,6 +37,8 @@ public: virtual void init(); virtual bool start(); virtual void stop(); + SoapySDRInputThread *getThread() { return m_thread; } + void setThread(SoapySDRInputThread *thread) { m_thread = thread; } virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); @@ -51,11 +54,13 @@ public: private: DeviceSourceAPI *m_deviceAPI; DeviceSoapySDRShared m_deviceShared; + SoapySDRInputThread *m_thread; QString m_deviceDescription; bool m_running; bool openDevice(); void closeDevice(); + void moveThreadToBuddy(); }; diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp new file mode 100644 index 000000000..ba8e75e2a --- /dev/null +++ b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "util/simpleserializer.h" + +#include "soapysdrinputsettings.h" + +SoapySDRInputSettings::SoapySDRInputSettings() +{ + resetToDefaults(); +} + +void SoapySDRInputSettings::resetToDefaults() +{ + m_centerFrequency = 435000*1000; + m_LOppmTenths = 0; + m_devSampleRate = 1024000; + m_log2Decim = 0; + m_fcPos = FC_POS_CENTER; + m_dcBlock = false; + m_iqCorrection = false; + m_transverterMode = false; + m_transverterDeltaFrequency = 0; + m_fileRecordName = ""; +} + +QByteArray SoapySDRInputSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeS32(1, m_devSampleRate); + s.writeU32(2, m_log2Decim); + s.writeS32(3, (int) m_fcPos); + s.writeBool(4, m_dcBlock); + s.writeBool(5, m_iqCorrection); + s.writeS32(6, m_LOppmTenths); + s.writeBool(7, m_transverterMode); + s.writeS64(8, m_transverterDeltaFrequency); + + return s.final(); +} + +bool SoapySDRInputSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + int intval; + + d.readS32(1, &m_devSampleRate, 1024000); + d.readU32(2, &m_log2Decim); + d.readS32(3, &intval, (int) FC_POS_CENTER); + m_fcPos = (fcPos_t) intval; + d.readBool(4, &m_dcBlock); + d.readBool(5, &m_iqCorrection); + d.readS32(6, &m_LOppmTenths); + d.readBool(7, &m_transverterMode, false); + d.readS64(8, &m_transverterDeltaFrequency, 0); + + return true; + } + else + { + resetToDefaults(); + return false; + } +} diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h new file mode 100644 index 000000000..53c967b03 --- /dev/null +++ b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESOURCE_SOAPYSDRINPUT_SOAPYSDRINPUTSETTINGS_H_ +#define PLUGINS_SAMPLESOURCE_SOAPYSDRINPUT_SOAPYSDRINPUTSETTINGS_H_ + +#include +#include + +struct SoapySDRInputSettings { + typedef enum { + FC_POS_INFRA = 0, + FC_POS_SUPRA, + FC_POS_CENTER + } fcPos_t; + + quint64 m_centerFrequency; + qint32 m_LOppmTenths; + qint32 m_devSampleRate; + quint32 m_log2Decim; + fcPos_t m_fcPos; + bool m_dcBlock; + bool m_iqCorrection; + bool m_transverterMode; + qint64 m_transverterDeltaFrequency; + QString m_fileRecordName; + + SoapySDRInputSettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + +#endif /* PLUGINS_SAMPLESOURCE_SOAPYSDRINPUT_SOAPYSDRINPUTSETTINGS_H_ */ diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputthread.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputthread.cpp new file mode 100644 index 000000000..96748ec87 --- /dev/null +++ b/plugins/samplesource/soapysdrinput/soapysdrinputthread.cpp @@ -0,0 +1,532 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "soapysdr/devicesoapysdr.h" + +#include "soapysdrinputthread.h" + +SoapySDRInputThread::SoapySDRInputThread(SoapySDR::Device* dev, unsigned int nbRxChannels, QObject* parent) : + QThread(parent), + m_running(false), + m_dev(dev), + m_sampleRate(0), + m_nbChannels(nbRxChannels), + m_decimatorType(DecimatorFloat) +{ + qDebug("SoapySDRInputThread::SoapySDRInputThread"); + m_channels = new Channel[nbRxChannels]; + + for (unsigned int i = 0; i < nbRxChannels; i++) { + m_channels[i].m_convertBuffer.resize(DeviceSoapySDR::blockSize, Sample{0,0}); + } + + m_buf = new qint16[2*DeviceSoapySDR::blockSize*nbRxChannels]; +} + +SoapySDRInputThread::~SoapySDRInputThread() +{ + qDebug("SoapySDRInputThread::~SoapySDRInputThread"); + + if (m_running) { + stopWork(); + } + + delete[] m_buf; + delete[] m_channels; +} + +void SoapySDRInputThread::startWork() +{ + m_startWaitMutex.lock(); + start(); + + while(!m_running) { + m_startWaiter.wait(&m_startWaitMutex, 100); + } + + m_startWaitMutex.unlock(); +} + +void SoapySDRInputThread::stopWork() +{ + m_running = false; + wait(); +} + +void SoapySDRInputThread::run() +{ + m_running = true; + m_startWaiter.wakeAll(); + + unsigned int nbFifos = getNbFifos(); + + if ((m_nbChannels > 0) && (nbFifos > 0)) + { + // build channels list + std::vector channels(m_nbChannels); + std::iota(channels.begin(), channels.end(), 0); // Fill with 0, 1, ..., m_nbChannels-1. + + //initialize the sample rate for all channels + for (const auto &it : channels) { + m_dev->setSampleRate(SOAPY_SDR_RX, it, m_sampleRate); + } + + // Determine sample format to be used + double fullScale(0.0); + std::string format = m_dev->getNativeStreamFormat(SOAPY_SDR_RX, channels.front(), fullScale); + + if ((format == "CF8") && (fullScale == 128.0)) { // 8 bit signed - native + m_decimatorType = Decimator8; + } else if ((format == "CF16") && (fullScale == 2048.0)) { // 12 bit signed - native + m_decimatorType = Decimator12; + } else if ((format == "CF16") && (fullScale == 32768.0)) { // 16 bit signed - native + m_decimatorType = Decimator16; + } else { // for other types make a conversion to float + m_decimatorType = DecimatorFloat; + format = "CF32"; + } + + unsigned int elemSize = SoapySDR::formatToSize(format); // sample (I+Q) size in bytes + SoapySDR::Stream *stream = m_dev->setupStream(SOAPY_SDR_RX, format, channels); + + //allocate buffers for the stream read/write + const unsigned int numElems = m_dev->getStreamMTU(stream); // number of samples (I+Q) + std::vector> buffMem(m_nbChannels, std::vector(elemSize*numElems)); + std::vector buffs(m_nbChannels); + + for (std::size_t i = 0; i < m_nbChannels; i++) { + buffs[i] = buffMem[i].data(); + } + + qDebug("SoapySDRInputThread::run: start running loop"); + m_dev->activateStream(stream); + int flags(0); + long long timeNs(0); + + while (m_running) + { + int ret = m_dev->readStream(stream, buffs.data(), numElems, flags, timeNs); + + if (ret < 0) + { + qCritical("SoapySDRInputThread::run: Unexpected read stream error: %s", SoapySDR::errToStr(ret)); + break; + } + + if (m_nbChannels > 1) + { + callbackMI(buffs, (elemSize/2)*numElems); + } + else + { + switch (m_decimatorType) + { + case Decimator8: + callbackSI8((const qint8*) buffs[0], (elemSize/2)*numElems); + break; + case Decimator12: + callbackSI12((const qint16*) buffs[0], (elemSize/2)*numElems); + break; + case Decimator16: + callbackSI16((const qint16*) buffs[0], (elemSize/2)*numElems); + break; + case DecimatorFloat: + default: + callbackSIF((const float*) buffs[0], (elemSize/2)*numElems); + } + } + } + + qDebug("SoapySDRInputThread::run: stop running loop"); + m_dev->deactivateStream(stream); + } + else + { + qWarning("SoapySDRInputThread::run: no channels or FIFO allocated. Aborting"); + } + + m_running = false; +} + +unsigned int SoapySDRInputThread::getNbFifos() +{ + unsigned int fifoCount = 0; + + for (unsigned int i = 0; i < m_nbChannels; i++) + { + if (m_channels[i].m_sampleFifo) { + fifoCount++; + } + } + + return fifoCount; +} + +void SoapySDRInputThread::setLog2Decimation(unsigned int channel, unsigned int log2_decim) +{ + if (channel < m_nbChannels) { + m_channels[channel].m_log2Decim = log2_decim; + } +} + +unsigned int SoapySDRInputThread::getLog2Decimation(unsigned int channel) const +{ + if (channel < m_nbChannels) { + return m_channels[channel].m_log2Decim; + } else { + return 0; + } +} + +void SoapySDRInputThread::setFcPos(unsigned int channel, int fcPos) +{ + if (channel < m_nbChannels) { + m_channels[channel].m_fcPos = fcPos; + } +} + +int SoapySDRInputThread::getFcPos(unsigned int channel) const +{ + if (channel < m_nbChannels) { + return m_channels[channel].m_fcPos; + } else { + return 0; + } +} + +void SoapySDRInputThread::setFifo(unsigned int channel, SampleSinkFifo *sampleFifo) +{ + if (channel < m_nbChannels) { + m_channels[channel].m_sampleFifo = sampleFifo; + } +} + +SampleSinkFifo *SoapySDRInputThread::getFifo(unsigned int channel) +{ + if (channel < m_nbChannels) { + return m_channels[channel].m_sampleFifo; + } else { + return 0; + } +} + +void SoapySDRInputThread::callbackMI(std::vector& buffs, qint32 samplesPerChannel) +{ + for(unsigned int ichan = 0; ichan < m_nbChannels; ichan++) + { + switch (m_decimatorType) + { + case Decimator8: + callbackSI8((const qint8*) buffs[ichan], samplesPerChannel, ichan); + break; + case Decimator12: + callbackSI12((const qint16*) buffs[ichan], samplesPerChannel, ichan); + break; + case Decimator16: + callbackSI16((const qint16*) buffs[ichan], samplesPerChannel, ichan); + break; + case DecimatorFloat: + default: + callbackSIF((const float*) buffs[ichan], samplesPerChannel, ichan); + } + } +} + +void SoapySDRInputThread::callbackSI8(const qint8* buf, qint32 len, unsigned int channel) +{ + SampleVector::iterator it = m_channels[channel].m_convertBuffer.begin(); + + if (m_channels[channel].m_log2Decim == 0) + { + m_channels[channel].m_decimators8.decimate1(&it, buf, len); + } + else + { + if (m_channels[channel].m_fcPos == 0) // Infra + { + switch (m_channels[channel].m_log2Decim) + { + case 1: + m_channels[channel].m_decimators8.decimate2_inf(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimators8.decimate4_inf(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimators8.decimate8_inf(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimators8.decimate16_inf(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimators8.decimate32_inf(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimators8.decimate64_inf(&it, buf, len); + break; + default: + break; + } + } + else if (m_channels[channel].m_fcPos == 1) // Supra + { + switch (m_channels[channel].m_log2Decim) + { + case 1: + m_channels[channel].m_decimators8.decimate2_sup(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimators8.decimate4_sup(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimators8.decimate8_sup(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimators8.decimate16_sup(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimators8.decimate32_sup(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimators8.decimate64_sup(&it, buf, len); + break; + default: + break; + } + } + else if (m_channels[channel].m_fcPos == 2) // Center + { + switch (m_channels[channel].m_log2Decim) + { + case 1: + m_channels[channel].m_decimators8.decimate2_cen(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimators8.decimate4_cen(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimators8.decimate8_cen(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimators8.decimate16_cen(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimators8.decimate32_cen(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimators8.decimate64_cen(&it, buf, len); + break; + default: + break; + } + } + } + + m_channels[channel].m_sampleFifo->write(m_channels[channel].m_convertBuffer.begin(), it); +} + +void SoapySDRInputThread::callbackSI12(const qint16* buf, qint32 len, unsigned int channel) +{ + SampleVector::iterator it = m_channels[channel].m_convertBuffer.begin(); + + if (m_channels[channel].m_log2Decim == 0) + { + m_channels[channel].m_decimators12.decimate1(&it, buf, len); + } + else + { + if (m_channels[channel].m_fcPos == 0) // Infra + { + switch (m_channels[channel].m_log2Decim) + { + case 1: + m_channels[channel].m_decimators12.decimate2_inf(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimators12.decimate4_inf(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimators12.decimate8_inf(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimators12.decimate16_inf(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimators12.decimate32_inf(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimators12.decimate64_inf(&it, buf, len); + break; + default: + break; + } + } + else if (m_channels[channel].m_fcPos == 1) // Supra + { + switch (m_channels[channel].m_log2Decim) + { + case 1: + m_channels[channel].m_decimators12.decimate2_sup(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimators12.decimate4_sup(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimators12.decimate8_sup(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimators12.decimate16_sup(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimators12.decimate32_sup(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimators12.decimate64_sup(&it, buf, len); + break; + default: + break; + } + } + else if (m_channels[channel].m_fcPos == 2) // Center + { + switch (m_channels[channel].m_log2Decim) + { + case 1: + m_channels[channel].m_decimators12.decimate2_cen(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimators12.decimate4_cen(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimators12.decimate8_cen(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimators12.decimate16_cen(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimators12.decimate32_cen(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimators12.decimate64_cen(&it, buf, len); + break; + default: + break; + } + } + } + + m_channels[channel].m_sampleFifo->write(m_channels[channel].m_convertBuffer.begin(), it); +} + +void SoapySDRInputThread::callbackSI16(const qint16* buf, qint32 len, unsigned int channel) +{ + SampleVector::iterator it = m_channels[channel].m_convertBuffer.begin(); + + if (m_channels[channel].m_log2Decim == 0) + { + m_channels[channel].m_decimators16.decimate1(&it, buf, len); + } + else + { + if (m_channels[channel].m_fcPos == 0) // Infra + { + switch (m_channels[channel].m_log2Decim) + { + case 1: + m_channels[channel].m_decimators16.decimate2_inf(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimators16.decimate4_inf(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimators16.decimate8_inf(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimators16.decimate16_inf(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimators16.decimate32_inf(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimators16.decimate64_inf(&it, buf, len); + break; + default: + break; + } + } + else if (m_channels[channel].m_fcPos == 1) // Supra + { + switch (m_channels[channel].m_log2Decim) + { + case 1: + m_channels[channel].m_decimators16.decimate2_sup(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimators16.decimate4_sup(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimators16.decimate8_sup(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimators16.decimate16_sup(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimators16.decimate32_sup(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimators16.decimate64_sup(&it, buf, len); + break; + default: + break; + } + } + else if (m_channels[channel].m_fcPos == 2) // Center + { + switch (m_channels[channel].m_log2Decim) + { + case 1: + m_channels[channel].m_decimators16.decimate2_cen(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimators16.decimate4_cen(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimators16.decimate8_cen(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimators16.decimate16_cen(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimators16.decimate32_cen(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimators16.decimate64_cen(&it, buf, len); + break; + default: + break; + } + } + } + + m_channels[channel].m_sampleFifo->write(m_channels[channel].m_convertBuffer.begin(), it); +} diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputthread.h b/plugins/samplesource/soapysdrinput/soapysdrinputthread.h new file mode 100644 index 000000000..6118926f7 --- /dev/null +++ b/plugins/samplesource/soapysdrinput/soapysdrinputthread.h @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESOURCE_SOAPYSDRINPUT_SOAPYSDRINPUTTHREAD_H_ +#define PLUGINS_SAMPLESOURCE_SOAPYSDRINPUT_SOAPYSDRINPUTTHREAD_H_ + +// SoapySDR is a device wrapper with a single stream supporting one or many Rx +// Therefore only one thread can be allocated for the Rx side +// All FIFOs must be registered before calling startWork() + +#include +#include +#include + +#include + +#include "soapysdr/devicesoapysdrshared.h" +#include "dsp/decimators.h" +#include "dsp/decimatorsfi.h" + +class SampleSinkFifo; + +class SoapySDRInputThread : public QThread { + Q_OBJECT + +public: + SoapySDRInputThread(SoapySDR::Device* dev, unsigned int nbRxChannels, QObject* parent = NULL); + ~SoapySDRInputThread(); + + void startWork(); + void stopWork(); + bool isRunning() const { return m_running; } + unsigned int getNbChannels() const { return m_nbChannels; } + void setLog2Decimation(unsigned int channel, unsigned int log2_decim); + unsigned int getLog2Decimation(unsigned int channel) const; + void setSampleRate(unsigned int sampleRate) { m_sampleRate = sampleRate; } + unsigned int getSampleRate() const { return m_sampleRate; } + void setFcPos(unsigned int channel, int fcPos); + int getFcPos(unsigned int channel) const; + void setFifo(unsigned int channel, SampleSinkFifo *sampleFifo); + SampleSinkFifo *getFifo(unsigned int channel); + +private: + struct Channel + { + SampleVector m_convertBuffer; + SampleSinkFifo* m_sampleFifo; + unsigned int m_log2Decim; + int m_fcPos; + Decimators m_decimators8; + Decimators m_decimators12; + Decimators m_decimators16; + DecimatorsFI m_decimatorsFloat; + + Channel() : + m_sampleFifo(0), + m_log2Decim(0), + m_fcPos(0) + {} + + ~Channel() + {} + }; + + enum DecimatorType + { + Decimator8, + Decimator12, + Decimator16, + DecimatorFloat + }; + + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + bool m_running; + SoapySDR::Device* m_dev; + + Channel *m_channels; //!< Array of channels dynamically allocated for the given number of Rx channels + unsigned int m_sampleRate; + qint16 *m_buf; //!< Full buffer for SISO or MIMO operation + unsigned int m_nbChannels; + DecimatorType m_decimatorType; + + void run(); + unsigned int getNbFifos(); + void callbackSI8(const qint8* buf, qint32 len, unsigned int channel = 0); + void callbackSI12(const qint16* buf, qint32 len, unsigned int channel = 0); + void callbackSI16(const qint16* buf, qint32 len, unsigned int channel = 0); + void callbackSIF(const float* buf, qint32 len, unsigned int channel = 0); + void callbackMI(std::vector& buffs, qint32 samplesPerChannel); +}; + + + +#endif /* PLUGINS_SAMPLESOURCE_SOAPYSDRINPUT_SOAPYSDRINPUTTHREAD_H_ */