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 +
gui/valuedial.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+
+ + + + +
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)