diff --git a/plugins/samplesink/plutosdroutput/CMakeLists.txt b/plugins/samplesink/plutosdroutput/CMakeLists.txt
new file mode 100644
index 000000000..1f5b3556c
--- /dev/null
+++ b/plugins/samplesink/plutosdroutput/CMakeLists.txt
@@ -0,0 +1,72 @@
+project(plutosdroutput)
+
+set(plutosdroutput_SOURCES
+ plutosdroutputgui.cpp
+ plutosdroutput.cpp
+ plutosdroutputplugin.cpp
+ plutosdroutputsettings.cpp
+ plutosdroutputthread.cpp
+)
+
+set(plutosdroutput_HEADERS
+ plutosdroutputgui.h
+ plutosdroutput.h
+ plutosdroutputplugin.h
+ plutosdroutputsettings.h
+ plutosdroutputthread.h
+)
+
+set(plutosdroutput_FORMS
+ plutosdroutputgui.ui
+)
+
+if (BUILD_DEBIAN)
+include_directories(
+ .
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/devices
+ ${LIBIIOSRC}
+)
+else (BUILD_DEBIAN)
+include_directories(
+ .
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/devices
+ ${LIBIIO_INCLUDE_DIR}
+)
+endif (BUILD_DEBIAN)
+
+#include(${QT_USE_FILE})
+#add_definitions(${QT_DEFINITIONS})
+add_definitions("${QT_DEFINITIONS} -DLIBHACKRF_DYN_RATES")
+add_definitions(-DQT_PLUGIN)
+add_definitions(-DQT_SHARED)
+
+#qt4_wrap_cpp(plutosdroutput_HEADERS_MOC ${plutosdroutput_HEADERS})
+qt5_wrap_ui(plutosdroutput_FORMS_HEADERS ${plutosdroutput_FORMS})
+
+add_library(inputplutosdr SHARED
+ ${plutosdroutput_SOURCES}
+ ${plutosdroutput_HEADERS_MOC}
+ ${plutosdroutput_FORMS_HEADERS}
+)
+
+if (BUILD_DEBIAN)
+target_link_libraries(inputplutosdr
+ ${QT_LIBRARIES}
+ iio
+ sdrbase
+ plutosdrdevice
+)
+else (BUILD_DEBIAN)
+target_link_libraries(inputplutosdr
+ ${QT_LIBRARIES}
+ ${LIBIIO_LIBRARIES}
+ sdrbase
+ plutosdrdevice
+)
+endif (BUILD_DEBIAN)
+
+qt5_use_modules(inputplutosdr Core Widgets)
+
+install(TARGETS inputplutosdr DESTINATION lib/plugins/samplesource)
diff --git a/plugins/samplesink/plutosdroutput/plutosdroutput.cpp b/plugins/samplesink/plutosdroutput/plutosdroutput.cpp
new file mode 100644
index 000000000..9d5254522
--- /dev/null
+++ b/plugins/samplesink/plutosdroutput/plutosdroutput.cpp
@@ -0,0 +1,434 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 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 "dsp/dspcommands.h"
+#include "device/devicesourceapi.h"
+#include "device/devicesinkapi.h"
+#include "plutosdr/deviceplutosdrparams.h"
+#include "plutosdr/deviceplutosdrbox.h"
+
+#include "plutosdroutput.h"
+#include "plutosdroutputthread.h"
+
+#define PLUTOSDR_BLOCKSIZE_SAMPLES (32*1024) //complex samples per buffer (must be multiple of 64)
+
+MESSAGE_CLASS_DEFINITION(PlutoSDROutput::MsgConfigurePlutoSDR, Message)
+
+PlutoSDROutput::PlutoSDROutput(DeviceSinkAPI *deviceAPI) :
+ m_deviceAPI(deviceAPI),
+ m_settings(),
+ m_deviceDescription("PlutoSDROutput"),
+ m_running(false),
+ m_plutoTxBuffer(0),
+ m_plutoSDROutputThread(0)
+{
+ suspendBuddies();
+ openDevice();
+ resumeBuddies();
+}
+
+PlutoSDROutput::~PlutoSDROutput()
+{
+ suspendBuddies();
+ closeDevice();
+ resumeBuddies();
+}
+
+void PlutoSDROutput::destroy()
+{
+ delete this;
+}
+
+bool PlutoSDROutput::start()
+{
+ if (!m_deviceShared.m_deviceParams->getBox()) {
+ return false;
+ }
+
+ if (m_running) stop();
+
+ applySettings(m_settings, true);
+
+ // start / stop streaming is done in the thread.
+
+ if ((m_plutoSDROutputThread = new PlutoSDROutputThread(PLUTOSDR_BLOCKSIZE_SAMPLES, m_deviceShared.m_deviceParams->getBox(), &m_sampleSourceFifo)) == 0)
+ {
+ qFatal("PlutoSDROutput::start: cannot create thread");
+ stop();
+ return false;
+ }
+ else
+ {
+ qDebug("PlutoSDROutput::start: thread created");
+ }
+
+ m_plutoSDROutputThread->setLog2Interpolation(m_settings.m_log2Interp);
+ m_plutoSDROutputThread->startWork();
+
+ m_deviceShared.m_thread = m_plutoSDROutputThread;
+ m_running = true;
+
+ return true;
+}
+
+void PlutoSDROutput::stop()
+{
+ if (m_plutoSDROutputThread != 0)
+ {
+ m_plutoSDROutputThread->stopWork();
+ delete m_plutoSDROutputThread;
+ m_plutoSDROutputThread = 0;
+ }
+
+ m_deviceShared.m_thread = 0;
+ m_running = false;
+}
+
+const QString& PlutoSDROutput::getDeviceDescription() const
+{
+ return m_deviceDescription;
+}
+int PlutoSDROutput::getSampleRate() const
+{
+ return (m_settings.m_devSampleRate / (1<getSourceBuddies().size() > 0) // then sink
+ {
+ qDebug("PlutoSDROutput::openDevice: look at Rx buddy");
+
+ DeviceSourceAPI *sourceBuddy = m_deviceAPI->getSourceBuddies()[0];
+ m_deviceShared = *((DevicePlutoSDRShared *) sourceBuddy->getBuddySharedPtr()); // copy parameters
+
+ if (m_deviceShared.m_deviceParams == 0)
+ {
+ qCritical("PlutoSDROutput::openDevice: cannot get device parameters from Rx buddy");
+ return false; // the device params should have been created by the buddy
+ }
+ else
+ {
+ qDebug("PlutoSDROutput::openDevice: getting device parameters from Rx buddy");
+ }
+ }
+ // There is no buddy then create the first PlutoSDR common parameters
+ // open the device this will also populate common fields
+ else
+ {
+ qDebug("PlutoSDROutput::openDevice: open device here");
+
+ m_deviceShared.m_deviceParams = new DevicePlutoSDRParams();
+ char serial[256];
+ strcpy(serial, qPrintable(m_deviceAPI->getSampleSinkSerial()));
+ m_deviceShared.m_deviceParams->open(serial);
+ }
+
+ m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API
+
+ // acquire the channel
+ DevicePlutoSDRBox *plutoBox = m_deviceShared.m_deviceParams->getBox();
+ plutoBox->openTx();
+ m_plutoTxBuffer = plutoBox->createTxBuffer(PLUTOSDR_BLOCKSIZE_SAMPLES*2, false); // PlutoSDR buffer size is counted in number of I or Q samples not the combination
+
+ return true;
+}
+
+void PlutoSDROutput::closeDevice()
+{
+ if (m_deviceShared.m_deviceParams->getBox() == 0) { // was never open
+ return;
+ }
+
+ if (m_deviceAPI->getSourceBuddies().size() == 0)
+ {
+ m_deviceShared.m_deviceParams->close();
+ delete m_deviceShared.m_deviceParams;
+ m_deviceShared.m_deviceParams = 0;
+ }
+}
+
+void PlutoSDROutput::suspendBuddies()
+{
+ // suspend Rx buddy's thread
+
+ for (unsigned int i = 0; i < m_deviceAPI->getSourceBuddies().size(); i++)
+ {
+ DeviceSourceAPI *buddy = m_deviceAPI->getSourceBuddies()[i];
+ DevicePlutoSDRShared *buddyShared = (DevicePlutoSDRShared *) buddy->getBuddySharedPtr();
+
+ if (buddyShared->m_thread) {
+ buddyShared->m_thread->stopWork();
+ }
+ }
+}
+
+void PlutoSDROutput::resumeBuddies()
+{
+ // resume Rx buddy's thread
+
+ for (unsigned int i = 0; i < m_deviceAPI->getSourceBuddies().size(); i++)
+ {
+ DeviceSourceAPI *buddy = m_deviceAPI->getSourceBuddies()[i];
+ DevicePlutoSDRShared *buddyShared = (DevicePlutoSDRShared *) buddy->getBuddySharedPtr();
+
+ if (buddyShared->m_thread) {
+ buddyShared->m_thread->startWork();
+ }
+ }
+}
+
+bool PlutoSDROutput::applySettings(const PlutoSDROutputSettings& settings, bool force)
+{
+ bool forwardChangeOwnDSP = false;
+ bool forwardChangeOtherDSP = false;
+ bool suspendOwnThread = false;
+ bool ownThreadWasRunning = false;
+ bool suspendAllOtherThreads = false; // All others means Rx in fact
+ DevicePlutoSDRBox *plutoBox = m_deviceShared.m_deviceParams->getBox();
+
+ // determine if buddies threads or own thread need to be suspended
+
+ // changes affecting all buddies can occur if
+ // - device to host sample rate is changed
+ // - FIR filter is enabled or disabled
+ // - FIR filter is changed
+ // - LO correction is changed
+ if ((m_settings.m_devSampleRate != settings.m_devSampleRate) ||
+ (m_settings.m_lpfFIREnable != settings.m_lpfFIREnable) ||
+ (m_settings.m_lpfFIRlog2Interp != settings.m_lpfFIRlog2Interp) ||
+ (settings.m_lpfFIRBW != m_settings.m_lpfFIRBW) ||
+ (settings.m_lpfFIRGain != m_settings.m_lpfFIRGain) ||
+ (m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force)
+ {
+ suspendAllOtherThreads = true;
+ suspendOwnThread = true;
+ }
+ else
+ {
+ suspendOwnThread = true;
+ }
+
+ if (suspendAllOtherThreads)
+ {
+ const std::vector& sourceBuddies = m_deviceAPI->getSourceBuddies();
+ std::vector::const_iterator itSink = sourceBuddies.begin();
+
+ for (; itSink != sourceBuddies.end(); ++itSink)
+ {
+ DevicePlutoSDRShared *buddySharedPtr = (DevicePlutoSDRShared *) (*itSink)->getBuddySharedPtr();
+
+ if (buddySharedPtr->m_thread) {
+ buddySharedPtr->m_thread->stopWork();
+ buddySharedPtr->m_threadWasRunning = true;
+ }
+ else
+ {
+ buddySharedPtr->m_threadWasRunning = false;
+ }
+ }
+ }
+
+ if (suspendOwnThread)
+ {
+ if (m_plutoSDROutputThread && m_plutoSDROutputThread->isRunning())
+ {
+ m_plutoSDROutputThread->stopWork();
+ ownThreadWasRunning = true;
+ }
+ }
+
+ // apply settings
+
+ // Change affecting device sample rate chain and other buddies
+ if ((m_settings.m_devSampleRate != settings.m_devSampleRate) ||
+ (m_settings.m_lpfFIREnable != settings.m_lpfFIREnable) ||
+ (m_settings.m_lpfFIRlog2Interp != settings.m_lpfFIRlog2Interp) ||
+ (settings.m_lpfFIRBW != m_settings.m_lpfFIRBW) ||
+ (settings.m_lpfFIRGain != m_settings.m_lpfFIRGain) || force)
+ {
+ plutoBox->setFIR(settings.m_devSampleRate, settings.m_lpfFIRlog2Interp, DevicePlutoSDRBox::USE_TX, settings.m_lpfFIRBW, settings.m_lpfFIRGain);
+ plutoBox->setFIREnable(settings.m_lpfFIREnable); // eventually enable/disable FIR
+ plutoBox->setSampleRate(settings.m_devSampleRate); // and set end point sample rate
+
+ plutoBox->getTxSampleRates(m_deviceSampleRates); // pick up possible new rates
+ qDebug() << "PlutoSDRInput::applySettings: BBPLL(Hz): " << m_deviceSampleRates.m_bbRateHz
+ << " DAC: " << m_deviceSampleRates.m_addaConnvRate
+ << " <-HB3- " << m_deviceSampleRates.m_hb3Rate
+ << " <-HB2- " << m_deviceSampleRates.m_hb2Rate
+ << " <-HB1- " << m_deviceSampleRates.m_hb1Rate
+ << " <-FIR- " << m_deviceSampleRates.m_firRate;
+
+ forwardChangeOtherDSP = true;
+ forwardChangeOwnDSP = (m_settings.m_devSampleRate != settings.m_devSampleRate);
+ }
+
+ if ((m_settings.m_log2Interp != settings.m_log2Interp) || force)
+ {
+ if (m_plutoSDROutputThread != 0)
+ {
+ m_plutoSDROutputThread->setLog2Interpolation(settings.m_log2Interp);
+ qDebug() << "PlutoSDROutput::applySettings: set soft interpolation to " << (1<setLOPPMTenths(settings.m_LOppmTenths);
+ // TODO: forward change to Rx
+ }
+
+ // TODO: continue from here
+
+ std::vector params;
+ bool paramsToSet = false;
+
+ if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force)
+ {
+ params.push_back(QString(tr("out_altvoltage0_RX_LO_frequency=%1").arg(settings.m_centerFrequency)).toStdString());
+ paramsToSet = true;
+ forwardChangeOwnDSP = true;
+ }
+
+ if ((m_settings.m_lpfBW != settings.m_lpfBW) || force)
+ {
+ params.push_back(QString(tr("in_voltage_rf_bandwidth=%1").arg(settings.m_lpfBW)).toStdString());
+ paramsToSet = true;
+ }
+
+ if ((m_settings.m_antennaPath != settings.m_antennaPath) || force)
+ {
+ QString rfPortStr;
+ PlutoSDRInputSettings::translateRFPath(settings.m_antennaPath, rfPortStr);
+ params.push_back(QString(tr("in_voltage0_rf_port_select=%1").arg(rfPortStr)).toStdString());
+ paramsToSet = true;
+ }
+
+ if ((m_settings.m_gainMode != settings.m_gainMode) || force)
+ {
+ QString gainModeStr;
+ PlutoSDRInputSettings::translateGainMode(settings.m_gainMode, gainModeStr);
+ params.push_back(QString(tr("in_voltage0_gain_control_mode=%1").arg(gainModeStr)).toStdString());
+ paramsToSet = true;
+ }
+
+ if ((m_settings.m_gain != settings.m_gain) || force)
+ {
+ params.push_back(QString(tr("in_voltage0_hardwaregain=%1").arg(settings.m_gain)).toStdString());
+ paramsToSet = true;
+ }
+
+ if (paramsToSet)
+ {
+ plutoBox->set_params(DevicePlutoSDRBox::DEVICE_PHY, params);
+ }
+
+ m_settings = settings;
+
+ if (suspendAllOtherThreads)
+ {
+ const std::vector& sinkBuddies = m_deviceAPI->getSinkBuddies();
+ std::vector::const_iterator itSink = sinkBuddies.begin();
+
+ for (; itSink != sinkBuddies.end(); ++itSink)
+ {
+ DevicePlutoSDRShared *buddySharedPtr = (DevicePlutoSDRShared *) (*itSink)->getBuddySharedPtr();
+
+ if (buddySharedPtr->m_threadWasRunning) {
+ buddySharedPtr->m_thread->startWork();
+ }
+ }
+ }
+
+ if (suspendOwnThread)
+ {
+ if (ownThreadWasRunning) {
+ m_plutoSDRInputThread->startWork();
+ }
+ }
+
+ // TODO: forward changes to other (Tx) DSP
+ if (forwardChangeOtherDSP)
+ {
+ qDebug("PlutoSDRInput::applySettings: forwardChangeOtherDSP");
+ }
+
+ if (forwardChangeOwnDSP)
+ {
+ qDebug("PlutoSDRInput::applySettings: forward change to self");
+
+ int sampleRate = m_settings.m_devSampleRate/(1<handleMessage(*notif); // forward to file sink
+ m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
+ }
+
+ return true;
+}
+
+void PlutoSDRInput::getRSSI(std::string& rssiStr)
+{
+ DevicePlutoSDRBox *plutoBox = m_deviceShared.m_deviceParams->getBox();
+
+ if (!plutoBox->getRSSI(rssiStr, 0)) {
+ rssiStr = "xxx dB";
+ }
+}
+
+bool PlutoSDRInput::fetchTemperature()
+{
+ DevicePlutoSDRBox *plutoBox = m_deviceShared.m_deviceParams->getBox();
+ return plutoBox->fetchTemp();
+}
+
+float PlutoSDRInput::getTemperature()
+{
+ DevicePlutoSDRBox *plutoBox = m_deviceShared.m_deviceParams->getBox();
+ return plutoBox->getTemp();
+}
diff --git a/plugins/samplesink/plutosdroutput/plutosdroutput.h b/plugins/samplesink/plutosdroutput/plutosdroutput.h
new file mode 100644
index 000000000..746162306
--- /dev/null
+++ b/plugins/samplesink/plutosdroutput/plutosdroutput.h
@@ -0,0 +1,94 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 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_PLUTOSDROUTPUT_PLUTOSDRINPUT_H_
+#define PLUGINS_SAMPLESOURCE_PLUTOSDROUTPUT_PLUTOSDRINPUT_H_
+
+#include
+
+#include "iio.h"
+#include
+#include "util/message.h"
+#include "plutosdr/deviceplutosdrshared.h"
+#include "plutosdroutputsettings.h"
+
+class DeviceSinkAPI;
+class PlutoSDROutputThread;
+
+class PlutoSDROutput : public DeviceSampleSink {
+public:
+ class MsgConfigurePlutoSDR : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ const PlutoSDROutputSettings& getSettings() const { return m_settings; }
+ bool getForce() const { return m_force; }
+
+ static MsgConfigurePlutoSDR* create(const PlutoSDROutputSettings& settings, bool force)
+ {
+ return new MsgConfigurePlutoSDR(settings, force);
+ }
+
+ private:
+ PlutoSDROutputSettings m_settings;
+ bool m_force;
+
+ MsgConfigurePlutoSDR(const PlutoSDROutputSettings& settings, bool force) :
+ Message(),
+ m_settings(settings),
+ m_force(force)
+ { }
+ };
+
+ PlutoSDROutput(DeviceSinkAPI *deviceAPI);
+ ~PlutoSDROutput();
+ virtual void destroy();
+
+ virtual bool start();
+ virtual void stop();
+
+ virtual const QString& getDeviceDescription() const;
+ virtual int getSampleRate() const;
+ virtual quint64 getCenterFrequency() const;
+
+ virtual bool handleMessage(const Message& message);
+
+ uint32_t getDACSampleRate() const { return m_deviceSampleRates.m_addaConnvRate; }
+ uint32_t getFIRSampleRate() const { return m_deviceSampleRates.m_hb1Rate; }
+ void getRSSI(std::string& rssiStr);
+ bool fetchTemperature();
+ float getTemperature();
+
+ private:
+ DeviceSinkAPI *m_deviceAPI;
+ QString m_deviceDescription;
+ PlutoSDROutputSettings m_settings;
+ bool m_running;
+ DevicePlutoSDRShared m_deviceShared;
+ struct iio_buffer *m_plutoTxBuffer;
+ PlutoSDROutputThread *m_plutoSDROutputThread;
+ DevicePlutoSDRBox::SampleRates m_deviceSampleRates;
+ QMutex m_mutex;
+
+ bool openDevice();
+ void closeDevice();
+ void suspendBuddies();
+ void resumeBuddies();
+ bool applySettings(const PlutoSDROutputSettings& settings, bool force = false);
+};
+
+
+#endif /* PLUGINS_SAMPLESOURCE_PLUTOSDROUTPUT_PLUTOSDRINPUT_H_ */
diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp
new file mode 100644
index 000000000..43a5b3a5d
--- /dev/null
+++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp
@@ -0,0 +1,382 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 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 "dsp/dspengine.h"
+#include "dsp/dspcommands.h"
+#include "gui/glspectrum.h"
+#include "device/devicesinkapi.h"
+#include "plutosdr/deviceplutosdr.h"
+#include "plutosdroutput.h"
+#include "plutosdroutputgui.h"
+#include "ui_plutosdroutputgui.h"
+
+PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceSinkAPI *deviceAPI, QWidget* parent) :
+ QWidget(parent),
+ ui(new Ui::PlutoSDROutputGUI),
+ m_deviceAPI(deviceAPI),
+ m_settings(),
+ m_forceSettings(true),
+ m_sampleSink(0),
+ m_sampleRate(0),
+ m_deviceCenterFrequency(0),
+ m_lastEngineState((DSPDeviceSinkEngine::State)-1),
+ m_doApplySettings(true),
+ m_statusCounter(0)
+{
+ m_sampleSink = (PlutoSDROutput*) m_deviceAPI->getSampleSink();
+
+ ui->setupUi(this);
+ ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
+ ui->centerFrequency->setValueRange(7, DevicePlutoSDR::loLowLimitFreq/1000, DevicePlutoSDR::loHighLimitFreq/1000);
+
+ ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
+ ui->sampleRate->setValueRange(8, DevicePlutoSDR::srLowLimitFreq, DevicePlutoSDR::srHighLimitFreq);
+
+ ui->lpf->setColorMapper(ColorMapper(ColorMapper::GrayYellow));
+ ui->lpf->setValueRange(5, DevicePlutoSDR::bbLPTxLowLimitFreq/1000, DevicePlutoSDR::bbLPTxHighLimitFreq/1000);
+
+ ui->lpFIR->setColorMapper(ColorMapper(ColorMapper::GrayYellow));
+ ui->lpFIR->setValueRange(5, 1U, 56000U); // will be dynamically recalculated
+
+ ui->swInterpLabel->setText(QString::fromUtf8("S\u2193"));
+ ui->lpFIRInterpolationLabel->setText(QString::fromUtf8("\u2193"));
+
+ blockApplySettings(true);
+ displaySettings();
+ blockApplySettings(false);
+
+ connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
+ connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
+ m_statusTimer.start(500);
+
+ connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
+}
+
+PlutoSDROutputGUI::~PlutoSDROutputGUI()
+{
+ delete ui;
+}
+
+void PlutoSDROutputGUI::destroy()
+{
+ delete this;
+}
+
+void PlutoSDROutputGUI::setName(const QString& name)
+{
+ setObjectName(name);
+}
+
+QString PlutoSDROutputGUI::getName() const
+{
+ return objectName();
+}
+
+void PlutoSDROutputGUI::resetToDefaults()
+{
+
+}
+
+qint64 PlutoSDROutputGUI::getCenterFrequency() const
+{
+ return m_settings.m_centerFrequency;
+}
+
+void PlutoSDROutputGUI::setCenterFrequency(qint64 centerFrequency)
+{
+ m_settings.m_centerFrequency = centerFrequency;
+ displaySettings();
+ sendSettings();
+}
+
+QByteArray PlutoSDROutputGUI::serialize() const
+{
+ return m_settings.serialize();
+}
+
+bool PlutoSDROutputGUI::deserialize(const QByteArray& data)
+{
+ if(m_settings.deserialize(data))
+ {
+ blockApplySettings(true);
+ displaySettings();
+ blockApplySettings(false);
+ sendSettings(true);
+ return true;
+ }
+ else
+ {
+ resetToDefaults();
+ return false;
+ }
+}
+
+bool PlutoSDROutputGUI::handleMessage(const Message& message __attribute__((unused)))
+{
+ return false;
+}
+
+void PlutoSDROutputGUI::on_startStop_toggled(bool checked)
+{
+ if (checked)
+ {
+ if (m_deviceAPI->initGeneration())
+ {
+ m_deviceAPI->startGeneration();
+ DSPEngine::instance()->startAudioOutput();
+ }
+ }
+ else
+ {
+ m_deviceAPI->stopGeneration();
+ DSPEngine::instance()->stopAudioOutput();
+ }
+}
+
+void PlutoSDROutputGUI::on_centerFrequency_changed(quint64 value)
+{
+ m_settings.m_centerFrequency = value * 1000;
+ sendSettings();
+}
+
+void PlutoSDROutputGUI::on_loPPM_valueChanged(int value)
+{
+ ui->loPPMText->setText(QString("%1").arg(QString::number(value/10.0, 'f', 1)));
+ m_settings.m_LOppmTenths = value;
+ sendSettings();
+}
+
+void PlutoSDROutputGUI::on_swInterp_currentIndexChanged(int index)
+{
+ m_settings.m_log2Interp = index > 6 ? 6 : index;
+ sendSettings();
+}
+
+void PlutoSDROutputGUI::on_sampleRate_changed(quint64 value)
+{
+ m_settings.m_devSampleRate = value;
+ sendSettings();
+}
+
+void PlutoSDROutputGUI::on_lpf_changed(quint64 value)
+{
+ m_settings.m_lpfBW = value * 1000;
+ sendSettings();
+}
+
+void PlutoSDROutputGUI::on_lpFIREnable_toggled(bool checked)
+{
+ m_settings.m_lpfFIREnable = checked;
+ ui->lpFIRInterpolation->setEnabled(checked);
+ ui->lpFIRGain->setEnabled(checked);
+ sendSettings();
+}
+
+void PlutoSDROutputGUI::on_lpFIR_changed(quint64 value)
+{
+ m_settings.m_lpfFIRBW = value * 1000;
+ sendSettings();
+}
+
+void PlutoSDROutputGUI::on_lpFIRInterpolation_currentIndexChanged(int index)
+{
+ m_settings.m_lpfFIRlog2Interp = index > 2 ? 2 : index;
+ setSampleRateLimits();
+ sendSettings();
+}
+
+void PlutoSDROutputGUI::on_lpFIRGain_currentIndexChanged(int index)
+{
+ m_settings.m_lpfFIRGain = 6*(index > 1 ? 1 : index) - 6;
+ sendSettings();
+}
+
+void PlutoSDROutputGUI::on_att_valueChanged(int value)
+{
+ ui->attText->setText(QString("%1").arg(QString::number(value*0.25, 'f', 2)));
+ m_settings.m_att = value;
+ sendSettings();
+}
+
+void PlutoSDROutputGUI::on_antenna_currentIndexChanged(int index)
+{
+ m_settings.m_antennaPath = (PlutoSDROutputSettings::RFPath) (index < PlutoSDROutputSettings::RFPATH_END ? index : 0);
+ sendSettings();
+}
+
+void PlutoSDROutputGUI::displaySettings()
+{
+ ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000);
+ ui->sampleRate->setValue(m_settings.m_devSampleRate);
+
+ ui->loPPM->setValue(m_settings.m_LOppmTenths);
+ ui->loPPMText->setText(QString("%1").arg(QString::number(m_settings.m_LOppmTenths/10.0, 'f', 1)));
+
+ ui->swInterp->setCurrentIndex(m_settings.m_log2Interp);
+
+ ui->lpf->setValue(m_settings.m_lpfBW / 1000);
+
+ ui->lpFIREnable->setChecked(m_settings.m_lpfFIREnable);
+ ui->lpFIR->setValue(m_settings.m_lpfFIRBW / 1000);
+ ui->lpFIRInterpolation->setCurrentIndex(m_settings.m_lpfFIRlog2Interp);
+ ui->lpFIRGain->setCurrentIndex((m_settings.m_lpfFIRGain + 6)/6);
+ ui->lpFIRInterpolation->setEnabled(m_settings.m_lpfFIREnable);
+ ui->lpFIRGain->setEnabled(m_settings.m_lpfFIREnable);
+
+ ui->att->setValue(m_settings.m_att);
+ ui->attText->setText(QString("%1").arg(QString::number(m_settings.m_gain*0.25, 'f', 2)));
+
+ ui->antenna->setCurrentIndex((int) m_settings.m_antennaPath);
+
+ setFIRBWLimits();
+ setSampleRateLimits();
+}
+
+void PlutoSDROutputGUI::sendSettings(bool forceSettings)
+{
+ m_forceSettings = forceSettings;
+ if(!m_updateTimer.isActive()) { m_updateTimer.start(100); }
+}
+
+void PlutoSDROutputGUI::updateHardware()
+{
+ if (m_doApplySettings)
+ {
+ qDebug() << "PlutoSDROutputGUI::updateHardware";
+ PlutoSDROutput::MsgConfigurePlutoSDR* message = PlutoSDROutput::MsgConfigurePlutoSDR::create(m_settings, m_forceSettings);
+ m_sampleSink->getInputMessageQueue()->push(message);
+ m_forceSettings = false;
+ m_updateTimer.stop();
+ }
+}
+
+void PlutoSDROutputGUI::blockApplySettings(bool block)
+{
+ m_doApplySettings = !block;
+}
+
+void PlutoSDROutputGUI::updateStatus()
+{
+ int state = m_deviceAPI->state();
+
+ if(m_lastEngineState != state)
+ {
+ switch(state)
+ {
+ case DSPDeviceSourceEngine::StNotStarted:
+ ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
+ break;
+ case DSPDeviceSourceEngine::StIdle:
+ ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
+ break;
+ case DSPDeviceSourceEngine::StRunning:
+ ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
+ break;
+ case DSPDeviceSourceEngine::StError:
+ ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
+ QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage());
+ break;
+ default:
+ break;
+ }
+
+ m_lastEngineState = state;
+ }
+
+ if (m_statusCounter % 2 == 0) // 1s
+ {
+ uint32_t dacRate = ((PlutoSDROutput *) m_sampleSink)->getDACSampleRate();
+
+ if (dacRate < 100000000) {
+ ui->dacRateLabel->setText(tr("%1k").arg(QString::number(dacRate / 1000.0f, 'g', 5)));
+ } else {
+ ui->dacRateLabel->setText(tr("%1M").arg(QString::number(dacRate / 1000000.0f, 'g', 5)));
+ }
+ }
+
+ if (m_statusCounter % 4 == 0) // 2s
+ {
+ std::string rssiStr;
+ ((PlutoSDROutput *) m_sampleSink)->getRSSI(rssiStr);
+ ui->rssiText->setText(tr("-%1").arg(QString::fromStdString(rssiStr)));
+ }
+
+ if (m_statusCounter % 10 == 0) // 5s
+ {
+ if (m_deviceAPI->isBuddyLeader()) {
+ ((PlutoSDROutput *) m_sampleSink)->fetchTemperature();
+ }
+
+ ui->temperatureText->setText(tr("%1C").arg(QString::number(((PlutoSDROutput *) m_sampleSink)->getTemperature(), 'f', 0)));
+ }
+
+ m_statusCounter++;
+}
+
+void PlutoSDROutputGUI::setFIRBWLimits()
+{
+ float high = DevicePlutoSDR::firBWHighLimitFactor * ((PlutoSDROutput *) m_sampleSink)->getFIRSampleRate();
+ float low = DevicePlutoSDR::firBWLowLimitFactor * ((PlutoSDROutput *) m_sampleSink)->getFIRSampleRate();
+ ui->lpFIR->setValueRange(5, (int(low)/1000)+1, (int(high)/1000)+1);
+ ui->lpFIR->setValue(m_settings.m_lpfFIRBW/1000);
+}
+
+void PlutoSDROutputGUI::setSampleRateLimits()
+{
+ uint32_t low = ui->lpFIREnable->isChecked() ? DevicePlutoSDR::srLowLimitFreq / (1<lpFIRInterpolation->currentIndex()) : DevicePlutoSDR::srLowLimitFreq;
+ ui->sampleRate->setValueRange(8, low, DevicePlutoSDR::srHighLimitFreq);
+ ui->sampleRate->setValue(m_settings.m_devSampleRate);
+}
+
+void PlutoSDROutputGUI::handleInputMessages()
+{
+ Message* message;
+
+ while ((message = m_inputMessageQueue.pop()) != 0)
+ {
+ qDebug("PlutoSDROutputGUI::handleInputMessages: message: %s", message->getIdentifier());
+
+ if (DSPSignalNotification::match(*message))
+ {
+ DSPSignalNotification* notif = (DSPSignalNotification*) message;
+ m_sampleRate = notif->getSampleRate();
+ m_deviceCenterFrequency = notif->getCenterFrequency();
+ qDebug("PlutoSDROutputGUI::handleInputMessages: DSPSignalNotification: SampleRate: %d, CenterFrequency: %llu", notif->getSampleRate(), notif->getCenterFrequency());
+ updateSampleRateAndFrequency();
+ setFIRBWLimits();
+
+ delete message;
+ }
+ else
+ {
+ if (handleMessage(*message))
+ {
+ delete message;
+ }
+ }
+ }
+}
+
+void PlutoSDROutputGUI::updateSampleRateAndFrequency()
+{
+ m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate);
+ m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency);
+ ui->deviceRateLabel->setText(tr("%1k").arg(QString::number(m_sampleRate / 1000.0f, 'g', 5)));
+}
diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h
new file mode 100644
index 000000000..6adbcd552
--- /dev/null
+++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h
@@ -0,0 +1,94 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 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_PLUTOSDROUTPUT_PLUTOSDRINPUTGUI_H_
+#define PLUGINS_SAMPLESOURCE_PLUTOSDROUTPUT_PLUTOSDRINPUTGUI_H_
+
+#include
+#include
+#include
+
+#include "plugin/plugininstanceui.h"
+#include "util/messagequeue.h"
+
+#include "plutosdroutputsettings.h"
+
+class DeviceSinkAPI;
+class DeviceSampleSink;
+
+namespace Ui {
+ class PlutoSDROutputGUI;
+}
+
+class PlutoSDROutputGUI : public QWidget, public PluginInstanceUI {
+ Q_OBJECT
+
+public:
+ explicit PlutoSDROutputGUI(DeviceSinkAPI *deviceAPI, QWidget* parent = 0);
+ virtual ~PlutoSDROutputGUI();
+
+ virtual void destroy();
+ virtual void setName(const QString& name);
+ virtual QString getName() const;
+ virtual void resetToDefaults();
+ virtual qint64 getCenterFrequency() const;
+ virtual void setCenterFrequency(qint64 centerFrequency);
+ virtual QByteArray serialize() const;
+ virtual bool deserialize(const QByteArray& data);
+ virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
+ virtual bool handleMessage(const Message& message);
+
+private:
+ Ui::PlutoSDROutputGUI* ui;
+ DeviceSinkAPI* m_deviceAPI;
+ PlutoSDROutputSettings m_settings;
+ bool m_forceSettings;
+ QTimer m_updateTimer;
+ QTimer m_statusTimer;
+ DeviceSampleSink* m_sampleSink;
+ int m_sampleRate;
+ quint64 m_deviceCenterFrequency; //!< Center frequency in device
+ int m_lastEngineState;
+ bool m_doApplySettings;
+ uint32_t m_statusCounter;
+ MessageQueue m_inputMessageQueue;
+
+ void displaySettings();
+ void sendSettings(bool forceSettings = false);
+ void blockApplySettings(bool block);
+ void updateSampleRateAndFrequency();
+ void setFIRBWLimits();
+ void setSampleRateLimits();
+
+private slots:
+ void on_startStop_toggled(bool checked);
+ void on_centerFrequency_changed(quint64 value);
+ void on_loPPM_valueChanged(int value);
+ void on_swInterp_currentIndexChanged(int index);
+ void on_sampleRate_changed(quint64 value);
+ void on_lpf_changed(quint64 value);
+ void on_lpFIREnable_toggled(bool checked);
+ void on_lpFIR_changed(quint64 value);
+ void on_lpFIRInterpolation_currentIndexChanged(int index);
+ void on_lpFIRGain_currentIndexChanged(int index);
+ void on_att_valueChanged(int value);
+ void on_antenna_currentIndexChanged(int index);
+ void updateHardware();
+ void updateStatus();
+ void handleInputMessages();
+};
+
+#endif /* PLUGINS_SAMPLESOURCE_PLUTOSDROUTPUT_PLUTOSDRINPUTGUI_H_ */
diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui b/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui
new file mode 100644
index 000000000..73ec1ca82
--- /dev/null
+++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui
@@ -0,0 +1,779 @@
+
+
+ PlutoSDROutputGUI
+
+
+
+ 0
+ 0
+ 350
+ 260
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 350
+ 260
+
+
+
+
+ Sans Serif
+ 9
+
+
+
+ PlutoSDR output
+
+
+
+ 3
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+ -
+
+
+ 4
+
+
-
+
+
-
+
+
-
+
+
+ start/stop generation
+
+
+
+
+
+
+ :/play.png
+ :/stop.png:/play.png
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 54
+ 0
+
+
+
+ DAC rate after hardware upsampling (k or MS/s)
+
+
+ 00000k
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 16
+
+
+
+
+ DejaVu Sans Mono
+ 20
+ 50
+ false
+
+
+
+ PointingHandCursor
+
+
+ Qt::StrongFocus
+
+
+ Main center frequency in kHz
+
+
+
+ -
+
+
+ 6
+
+
+ 6
+
+
-
+
+
-
+
+
+ kHz
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 54
+ 0
+
+
+
+ Baseband I/Q sample rate kS/s
+
+
+ 00000k
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 2
+
+
-
+
+
+ LO ppm
+
+
+
+ -
+
+
+ Local oscillator correction (ppm)
+
+
+ -200
+
+
+ 200
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 34
+ 0
+
+
+
+ Local oscillator correction (ppm)
+
+
+ -00.0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 2
+
+
-
+
+
+ Sw
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ Software interpolation factor
+
+
+ 0
+
+
-
+
+ 1
+
+
+ -
+
+ 2
+
+
+ -
+
+ 4
+
+
+ -
+
+ 8
+
+
+ -
+
+ 16
+
+
+ -
+
+ 32
+
+
+ -
+
+ 64
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ :/antenna.png
+
+
+
+ -
+
+
+
+ 50
+ 0
+
+
+
+
+ 50
+ 16777215
+
+
+
+ Output path
+
+
-
+
+ A
+
+
+ -
+
+ B
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ SR
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 16
+
+
+
+
+ DejaVu Sans Mono
+ 12
+ 50
+ false
+
+
+
+ PointingHandCursor
+
+
+ Host to device sample rate
+
+
+
+ -
+
+
+ S/s
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ LP
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 16
+
+
+
+
+ DejaVu Sans Mono
+ 12
+ 50
+ false
+
+
+
+ PointingHandCursor
+
+
+ Analog lowpass filer bandwidth (kHz)
+
+
+
+ -
+
+
+ kHz
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 2
+
+
+ 2
+
+
-
+
+
+ Enable or disable TSP digital FIR lowpass filter
+
+
+ FIR
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 16
+
+
+
+
+ DejaVu Sans Mono
+ 12
+
+
+
+ PointingHandCursor
+
+
+ Digital FIR lowpass filers bandwidth (kHz) @ ~-6dB
+
+
+
+ -
+
+
+ kHz
+
+
+
+ -
+
+
+ I
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ FIR interpolation changes lower sample rate limit (see documentation)
+
+
-
+
+ 1
+
+
+ -
+
+ 2
+
+
+ -
+
+ 4
+
+
+
+
+ -
+
+
+ G
+
+
+
+ -
+
+
+ FIR gain (dB)
+
+
-
+
+ -6
+
+
+ -
+
+ 0
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ 2
+
+
+ 2
+
+
-
+
+
+
+ 24
+ 24
+
+
+
+ Reverse gain (attenuation) setting (dB)
+
+
+ -359
+
+
+ 0
+
+
+ 1
+
+
+ 1
+
+
+ -50
+
+
+
+ -
+
+
+
+ 40
+ 0
+
+
+
+ Gain value (dB)
+
+
+ -89.75
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 68
+ 0
+
+
+
+ Tx RSSI indication (dB) only in Tx monitor mode on the Rx
+
+
+ -100.00 dB
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 24
+ 0
+
+
+
+ Board temperature (degrees C)
+
+
+ 00C
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+ ValueDial
+ QWidget
+
+ 1
+
+
+ ButtonSwitch
+ QToolButton
+
+
+
+
+
+
+
+
diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp
new file mode 100644
index 000000000..2f3ecb11f
--- /dev/null
+++ b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp
@@ -0,0 +1,110 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 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 "plugin/pluginapi.h"
+#include "plutosdr/deviceplutosdr.h"
+
+#include "plutosdroutputgui.h"
+#include "plutosdroutput.h"
+#include "plutosdroutputplugin.h"
+
+class DeviceSourceAPI;
+
+const PluginDescriptor PlutoSDROutputPlugin::m_pluginDescriptor = {
+ QString("PlutoSDR Output"),
+ QString("3.7.1"),
+ QString("(c) Edouard Griffiths, F4EXB"),
+ QString("https://github.com/f4exb/sdrangel"),
+ true,
+ QString("https://github.com/f4exb/sdrangel")
+};
+
+const QString PlutoSDROutputPlugin::m_hardwareID = "PlutosDR";
+const QString PlutoSDROutputPlugin::m_deviceTypeID = PLUTOSDR_DEVICE_TYPE_ID;
+
+PlutoSDROutputPlugin::PlutoSDROutputPlugin(QObject* parent) :
+ QObject(parent)
+{
+}
+
+const PluginDescriptor& PlutoSDROutputPlugin::getPluginDescriptor() const
+{
+ return m_pluginDescriptor;
+}
+
+void PlutoSDROutputPlugin::initPlugin(PluginAPI* pluginAPI)
+{
+ pluginAPI->registerSampleSink(m_deviceTypeID, this);
+ DevicePlutoSDR::instance(); // create singleton
+}
+
+PluginInterface::SamplingDevices PlutoSDROutputPlugin::enumSampleSinks()
+{
+ DevicePlutoSDR::instance().scan();
+ std::vector serials;
+ DevicePlutoSDR::instance().getSerials(serials);
+
+ std::vector::const_iterator it = serials.begin();
+ int i;
+ SamplingDevices result;
+
+ for (i = 0; it != serials.end(); ++it, ++i)
+ {
+ QString serial_str = QString::fromLocal8Bit(it->c_str());
+ QString displayedName(QString("PlutoSDR[%1] %2").arg(i).arg(serial_str));
+
+ result.append(SamplingDevice(displayedName,
+ m_hardwareID,
+ m_deviceTypeID,
+ serial_str,
+ i));
+
+ qDebug("PlutoSDROutputPlugin::enumSampleSources: enumerated PlutoSDR device #%d", i);
+ }
+
+ return result;
+}
+
+PluginInstanceUI* PlutoSDROutputPlugin::createSampleSinkPluginInstanceGUI(const QString& sinkId, QWidget **widget, DeviceSinkAPI *deviceAPI)
+{
+ if(sinkId == m_deviceTypeID)
+ {
+ PlutoSDROutputGui* gui = new PlutoSDROutputGui(deviceAPI);
+ *widget = gui;
+ return gui;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+DeviceSampleSink *PlutoSDROutputPlugin::createSampleSourcePluginInstanceInput(const QString& sinkId, DeviceSinkAPI *deviceAPI)
+{
+ if (sinkId == m_deviceTypeID)
+ {
+ PlutoSDROutput* output = new PlutoSDROutput(deviceAPI);
+ return output;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h
new file mode 100644
index 000000000..2e0880fc8
--- /dev/null
+++ b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h
@@ -0,0 +1,49 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 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 INCLUDE_PLUTOSDROUTPUTPLUGIN_H
+#define INCLUDE_PLUTOSDROUTPUTPLUGIN_H
+
+#include
+#include "plugin/plugininterface.h"
+
+#define PLUTOSDR_DEVICE_TYPE_ID "sdrangel.samplesink.plutosdr"
+
+class PluginAPI;
+
+class PlutoSDROutputPlugin : public QObject, public PluginInterface {
+ Q_OBJECT
+ Q_INTERFACES(PluginInterface)
+ Q_PLUGIN_METADATA(IID PLUTOSDR_DEVICE_TYPE_ID)
+
+public:
+ explicit PlutoSDROutputPlugin(QObject* parent = NULL);
+
+ const PluginDescriptor& getPluginDescriptor() const;
+ void initPlugin(PluginAPI* pluginAPI);
+
+ virtual SamplingDevices enumSampleSinks();
+ virtual PluginInstanceUI* createSampleSinkPluginInstanceGUI(const QString& sinkId, QWidget **widget, DeviceSinkAPI *deviceAPI);
+ virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI);
+
+ static const QString m_hardwareID;
+ static const QString m_deviceTypeID;
+
+private:
+ static const PluginDescriptor m_pluginDescriptor;
+};
+
+#endif // INCLUDE_PLUTOSDROUTPUTPLUGIN_H
diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp
new file mode 100644
index 000000000..631da14d6
--- /dev/null
+++ b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp
@@ -0,0 +1,118 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 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 "plutosdroutputsettings.h"
+
+
+PlutoSDROutputSettings::PlutoSDROutputSettings()
+{
+ resetToDefaults();
+}
+
+void PlutoSDROutputSettings::resetToDefaults()
+{
+ m_centerFrequency = 435000 * 1000;
+ m_LOppmTenths = 0;
+ m_log2Interp = 0;
+ m_devSampleRate = 2500 * 1000;
+ m_lpfBW = 1500000;
+ m_lpfFIREnable = false;
+ m_lpfFIRBW = 500000U;
+ m_lpfFIRlog2Interp = 0;
+ m_att = -50;
+ m_antennaPath = RFPATH_A;
+}
+
+QByteArray PlutoSDROutputSettings::serialize() const
+{
+ SimpleSerializer s(1);
+
+ s.writeS32(1, m_LOppmTenths);
+ s.writeS32(2, m_lpfFIRGain);
+ s.writeU32(3, m_lpfFIRlog2Interp);
+ s.writeU32(4, m_log2Interp);
+ s.writeU32(9, m_lpfBW);
+ s.writeBool(10, m_lpfFIREnable);
+ s.writeU32(11, m_lpfFIRBW);
+ s.writeU64(12, m_devSampleRate);
+ s.writeS32(13, m_att);
+ s.writeS32(14, (int) m_antennaPath);
+
+ return s.final();
+}
+
+bool PlutoSDROutputSettings::deserialize(const QByteArray& data)
+{
+ SimpleDeserializer d(data);
+
+ if (!d.isValid())
+ {
+ resetToDefaults();
+ return false;
+ }
+
+ if (d.getVersion() == 1)
+ {
+ int intval;
+ uint32_t uintval;
+
+ d.readS32(1, &m_LOppmTenths, 0);
+ d.readS32(2, &m_lpfFIRGain, 0);
+ d.readU32(3, &uintval, 0);
+ if (uintval > 2) {
+ m_lpfFIRlog2Interp = 2;
+ } else {
+ m_lpfFIRlog2Interp = uintval;
+ }
+ d.readU32(4, &m_log2Interp, 0);
+ d.readU32(9, &m_lpfBW, 1500000);
+ d.readBool(10, &m_lpfFIREnable, false);
+ d.readU32(11, &m_lpfFIRBW, 500000U);
+ d.readU64(12, &m_devSampleRate, 1536000U);
+ d.readS32(13, &m_att, -50);
+ d.readS32(14, &intval, 0);
+ if ((intval >= 0) && (intval < (int) RFPATH_END)) {
+ m_antennaPath = (RFPath) intval;
+ } else {
+ m_antennaPath = RFPATH_A;
+ }
+
+ return true;
+ }
+ else
+ {
+ resetToDefaults();
+ return false;
+ }
+}
+
+void PlutoSDROutputSettings::translateRFPath(RFPath path, QString& s)
+{
+ switch(path)
+ {
+ case RFPATH_A:
+ s = "A";
+ break;
+ case RFPATH_B:
+ s = "B";
+ break;
+ default:
+ s = "A";
+ break;
+ }
+}
diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h
new file mode 100644
index 000000000..a3ad223b3
--- /dev/null
+++ b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h
@@ -0,0 +1,54 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 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 _PLUTOSDR_PLUTOSDROUTPUTSETTINGS_H_
+#define _PLUTOSDR_PLUTOSDROUTPUTSETTINGS_H_
+
+#include
+#include
+
+struct PlutoSDROutputSettings {
+ enum RFPath
+ {
+ RFPATH_A = 0,
+ RFPATH_B,
+ RFPATH_END
+ };
+
+ // global settings to be saved
+ quint64 m_centerFrequency;
+ // common device settings
+ quint64 m_devSampleRate; //!< Host interface sample rate
+ qint32 m_LOppmTenths; //!< XO correction
+ bool m_lpfFIREnable; //!< enable digital lowpass FIR filter
+ quint32 m_lpfFIRBW; //!< digital lowpass FIR filter bandwidth (Hz)
+ quint32 m_lpfFIRlog2Interp; //!< digital lowpass FIR filter log2 of interpolation factor (0..2)
+ int m_lpfFIRGain; //!< digital lowpass FIR filter gain (dB)
+ // individual channel settings
+ quint32 m_log2Interp;
+ quint32 m_lpfBW; //!< analog lowpass filter bandwidth (Hz)
+ qint32 m_att; //!< "hardware" attenuation in dB fourths
+ RFPath m_antennaPath;
+
+
+ PlutoSDROutputSettings();
+ void resetToDefaults();
+ QByteArray serialize() const;
+ bool deserialize(const QByteArray& data);
+ static void translateRFPath(RFPath path, QString& s);
+};
+
+#endif /* _PLUTOSDR_PLUTOSDROUTPUTSETTINGS_H_ */
diff --git a/plugins/samplesource/plutosdrinput/plutosdrinput.cpp b/plugins/samplesource/plutosdrinput/plutosdrinput.cpp
index 30877df0f..7302f252e 100644
--- a/plugins/samplesource/plutosdrinput/plutosdrinput.cpp
+++ b/plugins/samplesource/plutosdrinput/plutosdrinput.cpp
@@ -34,7 +34,7 @@ MESSAGE_CLASS_DEFINITION(PlutoSDRInput::MsgFileRecord, Message)
PlutoSDRInput::PlutoSDRInput(DeviceSourceAPI *deviceAPI) :
m_deviceAPI(deviceAPI),
m_fileSink(0),
- m_deviceDescription("PlutoSDR"),
+ m_deviceDescription("PlutoSDRInput"),
m_running(false),
m_plutoRxBuffer(0),
m_plutoSDRInputThread(0)