1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-09-09 14:46:34 -04:00
sdrangel/plugins/samplesink/usrpoutput/usrpoutput.cpp
Jon Beniston 209be94947 USRP driver improvements.
Set bit size to be 16 for interpolators and decimators.
Match buffer size to UHD.
Don't destroy TX stream, as there appears to be a bug when recreating it.
Catch exception when checking for TX underflow.
Increase RX FIFO size, to reduce overflows at high sample rates.
Allow RX to continue streaming after receiving timeout.
2020-10-23 13:12:37 +01:00

1158 lines
42 KiB
C++

///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB //
// Copyright (C) 2020 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <cstddef>
#include <string.h>
#include <QMutexLocker>
#include <QDebug>
#include <QNetworkReply>
#include <QBuffer>
#include <uhd/usrp/multi_usrp.hpp>
#include "SWGDeviceSettings.h"
#include "SWGUSRPOutputSettings.h"
#include "SWGDeviceState.h"
#include "SWGDeviceReport.h"
#include "SWGUSRPOutputReport.h"
#include "device/deviceapi.h"
#include "dsp/dspcommands.h"
#include "dsp/dspengine.h"
#include "usrpoutputthread.h"
#include "usrp/deviceusrpparam.h"
#include "usrp/deviceusrp.h"
#include "usrpoutput.h"
MESSAGE_CLASS_DEFINITION(USRPOutput::MsgConfigureUSRP, Message)
MESSAGE_CLASS_DEFINITION(USRPOutput::MsgStartStop, Message)
MESSAGE_CLASS_DEFINITION(USRPOutput::MsgGetStreamInfo, Message)
MESSAGE_CLASS_DEFINITION(USRPOutput::MsgGetDeviceInfo, Message)
MESSAGE_CLASS_DEFINITION(USRPOutput::MsgReportStreamInfo, Message)
USRPOutput::USRPOutput(DeviceAPI *deviceAPI) :
m_deviceAPI(deviceAPI),
m_settings(),
m_usrpOutputThread(nullptr),
m_bufSamples(0),
m_deviceDescription("USRPOutput"),
m_running(false),
m_channelAcquired(false)
{
m_deviceAPI->setNbSinkStreams(1);
m_sampleSourceFifo.resize(SampleSourceFifo::getSizePolicy(m_settings.m_devSampleRate));
m_streamId = nullptr;
suspendRxBuddies();
suspendTxBuddies();
openDevice();
resumeTxBuddies();
resumeRxBuddies();
m_networkManager = new QNetworkAccessManager();
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
}
USRPOutput::~USRPOutput()
{
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
delete m_networkManager;
if (m_running) {
stop();
}
suspendRxBuddies();
suspendTxBuddies();
closeDevice();
resumeTxBuddies();
resumeRxBuddies();
}
void USRPOutput::destroy()
{
delete this;
}
bool USRPOutput::openDevice()
{
int requestedChannel = m_deviceAPI->getDeviceItemIndex();
// look for Tx buddies and get reference to common parameters
// if there is a channel left take the first available
if (m_deviceAPI->getSinkBuddies().size() > 0) // look sink sibling first
{
qDebug("USRPOutput::openDevice: look in Ix buddies");
DeviceAPI *sinkBuddy = m_deviceAPI->getSinkBuddies()[0];
//m_deviceShared = *((DeviceUSRPShared *) sinkBuddy->getBuddySharedPtr()); // copy shared data
DeviceUSRPShared *deviceUSRPShared = (DeviceUSRPShared*) sinkBuddy->getBuddySharedPtr();
m_deviceShared.m_deviceParams = deviceUSRPShared->m_deviceParams;
DeviceUSRPParams *deviceParams = m_deviceShared.m_deviceParams; // get device parameters
if (deviceParams == 0)
{
qCritical("USRPOutput::openDevice: cannot get device parameters from Tx buddy");
return false; // the device params should have been created by the buddy
}
else
{
qDebug("USRPOutput::openDevice: getting device parameters from Tx buddy");
}
if (m_deviceAPI->getSinkBuddies().size() == deviceParams->m_nbTxChannels)
{
qCritical("USRPOutput::openDevice: no more Tx channels available in device");
return false; // no more Tx channels available in device
}
else
{
qDebug("USRPOutput::openDevice: at least one more Tx channel is available in device");
}
// check if the requested channel is busy and abort if so (should not happen if device management is working correctly)
for (unsigned int i = 0; i < m_deviceAPI->getSinkBuddies().size(); i++)
{
DeviceAPI *buddy = m_deviceAPI->getSinkBuddies()[i];
DeviceUSRPShared *buddyShared = (DeviceUSRPShared *) buddy->getBuddySharedPtr();
if (buddyShared->m_channel == requestedChannel)
{
qCritical("USRPOutput::openDevice: cannot open busy channel %u", requestedChannel);
return false;
}
}
m_deviceShared.m_channel = requestedChannel; // acknowledge the requested channel
}
// look for Rx buddies and get reference to common parameters
// take the first Rx channel
else if (m_deviceAPI->getSourceBuddies().size() > 0) // then source
{
qDebug("USRPOutput::openDevice: look in Rx buddies");
DeviceAPI *sourceBuddy = m_deviceAPI->getSourceBuddies()[0];
//m_deviceShared = *((DeviceUSRPShared *) sourceBuddy->getBuddySharedPtr()); // copy parameters
DeviceUSRPShared *deviceUSRPShared = (DeviceUSRPShared*) sourceBuddy->getBuddySharedPtr();
m_deviceShared.m_deviceParams = deviceUSRPShared->m_deviceParams;
if (m_deviceShared.m_deviceParams == 0)
{
qCritical("USRPOutput::openDevice: cannot get device parameters from Rx buddy");
return false; // the device params should have been created by the buddy
}
else
{
qDebug("USRPOutput::openDevice: getting device parameters from Rx buddy");
}
m_deviceShared.m_channel = requestedChannel; // acknowledge the requested channel
}
// There are no buddies then create the first USRP common parameters
// open the device this will also populate common fields
// take the first Tx channel
else
{
qDebug("USRPOutput::openDevice: open device here");
m_deviceShared.m_deviceParams = new DeviceUSRPParams();
char serial[256];
strcpy(serial, qPrintable(m_deviceAPI->getSamplingDeviceSerial()));
m_deviceShared.m_deviceParams->open(serial);
m_deviceShared.m_channel = requestedChannel; // acknowledge the requested channel
}
m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API
return true;
}
void USRPOutput::suspendRxBuddies()
{
const std::vector<DeviceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
std::vector<DeviceAPI*>::const_iterator itSource = sourceBuddies.begin();
qDebug("USRPOutput::suspendRxBuddies (%lu)", sourceBuddies.size());
for (; itSource != sourceBuddies.end(); ++itSource)
{
DeviceUSRPShared *buddySharedPtr = (DeviceUSRPShared *) (*itSource)->getBuddySharedPtr();
if (buddySharedPtr->m_thread && buddySharedPtr->m_thread->isRunning())
{
buddySharedPtr->m_thread->stopWork();
buddySharedPtr->m_threadWasRunning = true;
}
else
{
buddySharedPtr->m_threadWasRunning = false;
}
}
}
void USRPOutput::suspendTxBuddies()
{
const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
std::vector<DeviceAPI*>::const_iterator itSink = sinkBuddies.begin();
qDebug("USRPOutput::suspendTxBuddies (%lu)", sinkBuddies.size());
for (; itSink != sinkBuddies.end(); ++itSink)
{
DeviceUSRPShared *buddySharedPtr = (DeviceUSRPShared *) (*itSink)->getBuddySharedPtr();
if (buddySharedPtr->m_thread && buddySharedPtr->m_thread->isRunning())
{
buddySharedPtr->m_thread->stopWork();
buddySharedPtr->m_threadWasRunning = true;
}
else
{
buddySharedPtr->m_threadWasRunning = false;
}
}
}
void USRPOutput::resumeRxBuddies()
{
const std::vector<DeviceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
std::vector<DeviceAPI*>::const_iterator itSource = sourceBuddies.begin();
qDebug("USRPOutput::resumeRxBuddies (%lu)", sourceBuddies.size());
for (; itSource != sourceBuddies.end(); ++itSource)
{
DeviceUSRPShared *buddySharedPtr = (DeviceUSRPShared *) (*itSource)->getBuddySharedPtr();
if (buddySharedPtr->m_threadWasRunning) {
buddySharedPtr->m_thread->startWork();
}
}
}
void USRPOutput::resumeTxBuddies()
{
const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
std::vector<DeviceAPI*>::const_iterator itSink = sinkBuddies.begin();
qDebug("USRPOutput::resumeTxBuddies (%lu)", sinkBuddies.size());
for (; itSink != sinkBuddies.end(); ++itSink)
{
DeviceUSRPShared *buddySharedPtr = (DeviceUSRPShared *) (*itSink)->getBuddySharedPtr();
if (buddySharedPtr->m_threadWasRunning) {
buddySharedPtr->m_thread->startWork();
}
}
}
void USRPOutput::closeDevice()
{
if (m_deviceShared.m_deviceParams->getDevice() == nullptr) { // was never open
return;
}
if (m_running) stop();
// No buddies so effectively close the device
if ((m_deviceAPI->getSourceBuddies().size() == 0) && (m_deviceAPI->getSinkBuddies().size() == 0))
{
m_deviceShared.m_deviceParams->close();
delete m_deviceShared.m_deviceParams;
m_deviceShared.m_deviceParams = 0;
}
m_deviceShared.m_channel = -1; // effectively release the channel for the possible buddies
}
bool USRPOutput::acquireChannel()
{
suspendRxBuddies();
suspendTxBuddies();
if (m_streamId == nullptr)
{
try
{
// set up the stream
std::string cpu_format("sc16");
std::string wire_format("sc16");
std::vector<size_t> channel_nums;
channel_nums.push_back(m_deviceShared.m_channel);
uhd::stream_args_t stream_args(cpu_format, wire_format);
stream_args.channels = channel_nums;
m_streamId = m_deviceShared.m_deviceParams->getDevice()->get_tx_stream(stream_args);
// Match our transmit buffer size to what UHD uses
m_bufSamples = m_streamId->get_max_num_samps();
}
catch (std::exception& e)
{
qDebug() << "USRPOutput::acquireChannel: exception: " << e.what();
}
}
resumeTxBuddies();
resumeRxBuddies();
m_channelAcquired = true;
return true;
}
void USRPOutput::releaseChannel()
{
suspendRxBuddies();
suspendTxBuddies();
// FIXME: Currently we do not try to destroy the stream, as there seems to be
// an issue when we re-acquire the stream, the output spectrum will not be correct
// The transmitter output will be disabled when we stop sending data to it anyway
if (false)
{
// destroy the stream
m_streamId = nullptr;
}
resumeTxBuddies();
resumeRxBuddies();
m_channelAcquired = false;
}
void USRPOutput::init()
{
applySettings(m_settings, true);
}
bool USRPOutput::start()
{
if (!m_deviceShared.m_deviceParams->getDevice()) {
return false;
}
if (m_running) { stop(); }
if (!acquireChannel())
{
return false;
}
m_usrpOutputThread = new USRPOutputThread(m_streamId, m_bufSamples, &m_sampleSourceFifo);
qDebug("USRPOutput::start: thread created");
applySettings(m_settings, true);
m_usrpOutputThread->setLog2Interpolation(m_settings.m_log2SoftInterp);
m_usrpOutputThread->startWork();
m_deviceShared.m_thread = m_usrpOutputThread;
m_running = true;
return true;
}
void USRPOutput::stop()
{
qDebug("USRPOutput::stop");
if (m_usrpOutputThread)
{
m_usrpOutputThread->stopWork();
delete m_usrpOutputThread;
m_usrpOutputThread = nullptr;
}
m_deviceShared.m_thread = 0;
m_running = false;
releaseChannel();
}
QByteArray USRPOutput::serialize() const
{
return m_settings.serialize();
}
bool USRPOutput::deserialize(const QByteArray& data)
{
bool success = true;
if (!m_settings.deserialize(data))
{
m_settings.resetToDefaults();
success = false;
}
MsgConfigureUSRP* message = MsgConfigureUSRP::create(m_settings, true);
m_inputMessageQueue.push(message);
if (m_guiMessageQueue)
{
MsgConfigureUSRP* messageToGUI = MsgConfigureUSRP::create(m_settings, true);
m_guiMessageQueue->push(messageToGUI);
}
return success;
}
const QString& USRPOutput::getDeviceDescription() const
{
return m_deviceDescription;
}
int USRPOutput::getSampleRate() const
{
int rate = m_settings.m_devSampleRate;
return (rate / (1<<m_settings.m_log2SoftInterp));
}
quint64 USRPOutput::getCenterFrequency() const
{
return m_settings.m_centerFrequency;
}
void USRPOutput::setCenterFrequency(qint64 centerFrequency)
{
USRPOutputSettings settings = m_settings;
settings.m_centerFrequency = centerFrequency;
MsgConfigureUSRP* message = MsgConfigureUSRP::create(settings, false);
m_inputMessageQueue.push(message);
if (m_guiMessageQueue)
{
MsgConfigureUSRP* messageToGUI = MsgConfigureUSRP::create(settings, false);
m_guiMessageQueue->push(messageToGUI);
}
}
std::size_t USRPOutput::getChannelIndex()
{
return m_deviceShared.m_channel;
}
void USRPOutput::getLORange(float& minF, float& maxF) const
{
minF = m_deviceShared.m_deviceParams->m_loRangeTx.start();
maxF = m_deviceShared.m_deviceParams->m_loRangeTx.stop();
}
void USRPOutput::getSRRange(float& minF, float& maxF) const
{
minF = m_deviceShared.m_deviceParams->m_srRangeTx.start();
maxF = m_deviceShared.m_deviceParams->m_srRangeTx.stop();
}
void USRPOutput::getLPRange(float& minF, float& maxF) const
{
minF = m_deviceShared.m_deviceParams->m_lpfRangeTx.start();
maxF = m_deviceShared.m_deviceParams->m_lpfRangeTx.stop();
}
void USRPOutput::getGainRange(float& minF, float& maxF) const
{
minF = m_deviceShared.m_deviceParams->m_gainRangeTx.start();
maxF = m_deviceShared.m_deviceParams->m_gainRangeTx.stop();
}
QStringList USRPOutput::getTxAntennas() const
{
return m_deviceShared.m_deviceParams->m_txAntennas;
}
QStringList USRPOutput::getClockSources() const
{
return m_deviceShared.m_deviceParams->m_clockSources;
}
bool USRPOutput::handleMessage(const Message& message)
{
if (MsgConfigureUSRP::match(message))
{
MsgConfigureUSRP& conf = (MsgConfigureUSRP&) message;
qDebug() << "USRPOutput::handleMessage: MsgConfigureUSRP";
if (!applySettings(conf.getSettings(), conf.getForce()))
{
qDebug("USRPOutput::handleMessage config error");
}
return true;
}
else if (MsgStartStop::match(message))
{
MsgStartStop& cmd = (MsgStartStop&) message;
qDebug() << "USRPOutput::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop");
if (cmd.getStartStop())
{
if (m_deviceAPI->initDeviceEngine())
{
m_deviceAPI->startDeviceEngine();
}
}
else
{
m_deviceAPI->stopDeviceEngine();
}
if (m_settings.m_useReverseAPI) {
webapiReverseSendStartStop(cmd.getStartStop());
}
return true;
}
else if (DeviceUSRPShared::MsgReportBuddyChange::match(message))
{
DeviceUSRPShared::MsgReportBuddyChange& report = (DeviceUSRPShared::MsgReportBuddyChange&) message;
if (report.getRxElseTx() && m_running)
{
double host_Hz;
host_Hz = m_deviceShared.m_deviceParams->getDevice()->get_tx_rate(m_deviceShared.m_channel);
m_settings.m_devSampleRate = roundf(host_Hz);
qDebug() << "USRPOutput::handleMessage: MsgReportBuddyChange:"
<< " m_devSampleRate: " << m_settings.m_devSampleRate;
}
else
{
m_settings.m_devSampleRate = report.getDevSampleRate();
m_settings.m_centerFrequency = report.getCenterFrequency();
}
DSPSignalNotification *notif = new DSPSignalNotification(
m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp),
m_settings.m_centerFrequency);
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
DeviceUSRPShared::MsgReportBuddyChange *reportToGUI = DeviceUSRPShared::MsgReportBuddyChange::create(
m_settings.m_devSampleRate, m_settings.m_centerFrequency, false);
getMessageQueueToGUI()->push(reportToGUI);
return true;
}
else if (DeviceUSRPShared::MsgReportClockSourceChange::match(message))
{
DeviceUSRPShared::MsgReportClockSourceChange& report = (DeviceUSRPShared::MsgReportClockSourceChange&) message;
m_settings.m_clockSource = report.getClockSource();
if (getMessageQueueToGUI())
{
DeviceUSRPShared::MsgReportClockSourceChange *reportToGUI = DeviceUSRPShared::MsgReportClockSourceChange::create(
m_settings.m_clockSource);
getMessageQueueToGUI()->push(reportToGUI);
}
return true;
}
else if (MsgGetStreamInfo::match(message))
{
if (m_deviceAPI->getSamplingDeviceGUIMessageQueue())
{
if ((m_streamId != nullptr) && m_channelAcquired)
{
bool active;
quint32 underflows;
quint32 droppedPackets;
m_usrpOutputThread->getStreamStatus(active, underflows, droppedPackets);
MsgReportStreamInfo *report = MsgReportStreamInfo::create(
true, // success
active,
underflows,
droppedPackets
);
m_deviceAPI->getSamplingDeviceGUIMessageQueue()->push(report);
}
else
{
MsgReportStreamInfo *report = MsgReportStreamInfo::create(false, false, 0, 0);
m_deviceAPI->getSamplingDeviceGUIMessageQueue()->push(report);
}
}
return true;
}
else
{
return false;
}
}
bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool force)
{
bool forwardChangeOwnDSP = false;
bool forwardChangeTxDSP = false;
bool forwardChangeAllDSP = false;
bool forwardClockSource = false;
bool ownThreadWasRunning = false;
QList<QString> reverseAPIKeys;
try
{
qint64 deviceCenterFrequency = settings.m_centerFrequency;
deviceCenterFrequency -= settings.m_transverterMode ? settings.m_transverterDeltaFrequency : 0;
deviceCenterFrequency = deviceCenterFrequency < 0 ? 0 : deviceCenterFrequency;
// apply settings
if ((m_settings.m_clockSource != settings.m_clockSource) || force)
{
reverseAPIKeys.append("clockSource");
if (m_deviceShared.m_deviceParams->getDevice())
{
try
{
m_deviceShared.m_deviceParams->getDevice()->set_clock_source(settings.m_clockSource.toStdString(), 0);
forwardClockSource = true;
qDebug() << "USRPOutput::applySettings: clock set to" << settings.m_clockSource;
}
catch (std::exception &e)
{
// An exception will be thrown if the clock is not detected
// however, get_clock_source called below will still say the clock has is set
qCritical() << "USRPOutput::applySettings: could not set clock " << settings.m_clockSource;
// So, default back to internal
m_deviceShared.m_deviceParams->getDevice()->set_clock_source("internal", 0);
// notify GUI that source couldn't be set
forwardClockSource = true;
}
}
else
{
qCritical() << "USRPOutput::applySettings: could not set clock to " << settings.m_clockSource;
}
}
if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force)
{
reverseAPIKeys.append("devSampleRate");
forwardChangeAllDSP = true;
if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
{
m_deviceShared.m_deviceParams->getDevice()->set_tx_rate(settings.m_devSampleRate, m_deviceShared.m_channel);
double actualSampleRate = m_deviceShared.m_deviceParams->getDevice()->get_tx_rate(m_deviceShared.m_channel);
qDebug("USRPOutput::applySettings: set sample rate set to %d - actual rate %f", settings.m_devSampleRate,
actualSampleRate);
m_deviceShared.m_deviceParams->m_sampleRate = m_settings.m_devSampleRate;
}
}
if ((m_settings.m_centerFrequency != settings.m_centerFrequency)
|| (m_settings.m_transverterMode != settings.m_transverterMode)
|| (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency)
|| force)
{
reverseAPIKeys.append("centerFrequency");
reverseAPIKeys.append("transverterMode");
reverseAPIKeys.append("transverterDeltaFrequency");
forwardChangeTxDSP = true;
if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
{
uhd::tune_request_t tune_request(deviceCenterFrequency);
m_deviceShared.m_deviceParams->getDevice()->set_tx_freq(tune_request, m_deviceShared.m_channel);
m_deviceShared.m_centerFrequency = deviceCenterFrequency; // for buddies
qDebug("USRPOutput::applySettings: frequency set to %lld", deviceCenterFrequency);
}
}
if ((m_settings.m_devSampleRate != settings.m_devSampleRate)
|| (m_settings.m_log2SoftInterp != settings.m_log2SoftInterp) || force)
{
reverseAPIKeys.append("devSampleRate");
reverseAPIKeys.append("log2SoftInterp");
#if defined(_MSC_VER)
unsigned int fifoRate = (unsigned int) settings.m_devSampleRate / (1<<settings.m_log2SoftInterp);
fifoRate = fifoRate < 48000U ? 48000U : fifoRate;
#else
unsigned int fifoRate = std::max(
(unsigned int) settings.m_devSampleRate / (1<<settings.m_log2SoftInterp),
DeviceUSRPShared::m_sampleFifoMinRate);
#endif
m_sampleSourceFifo.resize(SampleSourceFifo::getSizePolicy(fifoRate));
qDebug("USRPOutput::applySettings: resize FIFO: rate %u", fifoRate);
}
if ((m_settings.m_gain != settings.m_gain) || force)
{
reverseAPIKeys.append("gain");
if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
{
m_deviceShared.m_deviceParams->getDevice()->set_tx_gain(settings.m_gain, m_deviceShared.m_channel);
qDebug() << "USRPOutput::applySettings: Gain set to " << settings.m_gain;
}
}
if ((m_settings.m_lpfBW != settings.m_lpfBW) || force)
{
reverseAPIKeys.append("lpfBW");
if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
{
m_deviceShared.m_deviceParams->getDevice()->set_tx_bandwidth(settings.m_lpfBW, m_deviceShared.m_channel);
qDebug("USRPOutput::applySettings: LPF BW: %f for channel %d", settings.m_lpfBW, m_deviceShared.m_channel);
}
}
if ((m_settings.m_log2SoftInterp != settings.m_log2SoftInterp) || force)
{
reverseAPIKeys.append("log2SoftInterp");
forwardChangeOwnDSP = true;
m_deviceShared.m_log2Soft = settings.m_log2SoftInterp; // for buddies
if (m_usrpOutputThread)
{
m_usrpOutputThread->setLog2Interpolation(settings.m_log2SoftInterp);
qDebug() << "USRPOutput::applySettings: set soft interpolation to " << (1<<settings.m_log2SoftInterp);
}
}
if ((m_settings.m_antennaPath != settings.m_antennaPath) || force)
{
reverseAPIKeys.append("antennaPath");
if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
{
m_deviceShared.m_deviceParams->getDevice()->set_tx_antenna(settings.m_antennaPath.toStdString(), m_deviceShared.m_channel);
qDebug("USRPOutput::applySettings: set antenna path to %s on channel %d", qPrintable(settings.m_antennaPath), m_deviceShared.m_channel);
}
}
if (settings.m_useReverseAPI)
{
bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
(m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) ||
(m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) ||
(m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex);
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
}
m_settings = settings;
// forward changes to buddies or oneself
if (forwardChangeAllDSP)
{
qDebug("USRPOutput::applySettings: forward change to all buddies");
// send to self first
DSPSignalNotification *notif = new DSPSignalNotification(
m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp),
m_settings.m_centerFrequency);
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
// send to sink buddies
const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
std::vector<DeviceAPI*>::const_iterator itSink = sinkBuddies.begin();
for (; itSink != sinkBuddies.end(); ++itSink)
{
DeviceUSRPShared::MsgReportBuddyChange *report = DeviceUSRPShared::MsgReportBuddyChange::create(
m_settings.m_devSampleRate, m_settings.m_centerFrequency, false);
(*itSink)->getSamplingDeviceInputMessageQueue()->push(report);
}
// send to source buddies
const std::vector<DeviceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
std::vector<DeviceAPI*>::const_iterator itSource = sourceBuddies.begin();
for (; itSource != sourceBuddies.end(); ++itSource)
{
DeviceUSRPShared::MsgReportBuddyChange *report = DeviceUSRPShared::MsgReportBuddyChange::create(
m_settings.m_devSampleRate, m_settings.m_centerFrequency, false);
(*itSource)->getSamplingDeviceInputMessageQueue()->push(report);
}
}
else if (forwardChangeTxDSP)
{
qDebug("USRPOutput::applySettings: forward change to Tx buddies");
int sampleRate = m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp);
// send to self first
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, m_settings.m_centerFrequency);
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
// send to sink buddies
const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
std::vector<DeviceAPI*>::const_iterator itSink = sinkBuddies.begin();
for (; itSink != sinkBuddies.end(); ++itSink)
{
DeviceUSRPShared::MsgReportBuddyChange *report = DeviceUSRPShared::MsgReportBuddyChange::create(
m_settings.m_devSampleRate, m_settings.m_centerFrequency, false);
(*itSink)->getSamplingDeviceInputMessageQueue()->push(report);
}
}
else if (forwardChangeOwnDSP)
{
qDebug("USRPOutput::applySettings: forward change to self only");
int sampleRate = m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp);
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, m_settings.m_centerFrequency);
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
}
if (forwardClockSource)
{
// get what clock is actually set, in case requested clock couldn't be set
if (m_deviceShared.m_deviceParams->getDevice())
{
try
{
m_settings.m_clockSource = QString::fromStdString(m_deviceShared.m_deviceParams->getDevice()->get_clock_source(0));
qDebug() << "USRPOutput::applySettings: clock source is " << m_settings.m_clockSource;
}
catch (std::exception &e)
{
qDebug() << "USRPOutput::applySettings: could not get clock source";
}
}
// send to source buddies
const std::vector<DeviceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
std::vector<DeviceAPI*>::const_iterator itSource = sourceBuddies.begin();
for (; itSource != sourceBuddies.end(); ++itSource)
{
DeviceUSRPShared::MsgReportClockSourceChange *report = DeviceUSRPShared::MsgReportClockSourceChange::create(
m_settings.m_clockSource);
(*itSource)->getSamplingDeviceInputMessageQueue()->push(report);
}
// send to sink buddies
const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
std::vector<DeviceAPI*>::const_iterator itSink = sinkBuddies.begin();
for (; itSink != sinkBuddies.end(); ++itSink)
{
DeviceUSRPShared::MsgReportClockSourceChange *report = DeviceUSRPShared::MsgReportClockSourceChange::create(
m_settings.m_clockSource);
(*itSink)->getSamplingDeviceInputMessageQueue()->push(report);
}
}
QLocale loc;
qDebug().noquote() << "USRPOutput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz"
<< " m_transverterMode: " << m_settings.m_transverterMode
<< " m_transverterDeltaFrequency: " << m_settings.m_transverterDeltaFrequency
<< " deviceCenterFrequency: " << deviceCenterFrequency
<< " device stream sample rate: " << loc.toString(m_settings.m_devSampleRate) << "S/s"
<< " sample rate with soft interpolation: " << loc.toString( m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp)) << "S/s"
<< " m_log2SoftInterp: " << m_settings.m_log2SoftInterp
<< " m_gain: " << m_settings.m_gain
<< " m_lpfBW: " << loc.toString(static_cast<int>(m_settings.m_lpfBW))
<< " m_antennaPath: " << m_settings.m_antennaPath
<< " m_clockSource: " << m_settings.m_clockSource
<< " force: " << force;
return true;
}
catch (std::exception &e)
{
qDebug() << "USRPOutput::applySettings: exception: " << e.what();
return false;
}
}
int USRPOutput::webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setUsrpOutputSettings(new SWGSDRangel::SWGUSRPOutputSettings());
response.getUsrpOutputSettings()->init();
webapiFormatDeviceSettings(response, m_settings);
return 200;
}
int USRPOutput::webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage)
{
(void) errorMessage;
USRPOutputSettings settings = m_settings;
webapiUpdateDeviceSettings(settings, deviceSettingsKeys, response);
MsgConfigureUSRP *msg = MsgConfigureUSRP::create(settings, force);
m_inputMessageQueue.push(msg);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureUSRP *msgToGUI = MsgConfigureUSRP::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatDeviceSettings(response, settings);
return 200;
}
void USRPOutput::webapiUpdateDeviceSettings(
USRPOutputSettings& settings,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response)
{
if (deviceSettingsKeys.contains("antennaPath")) {
settings.m_antennaPath = *response.getUsrpOutputSettings()->getAntennaPath();
}
if (deviceSettingsKeys.contains("centerFrequency")) {
settings.m_centerFrequency = response.getUsrpOutputSettings()->getCenterFrequency();
}
if (deviceSettingsKeys.contains("devSampleRate")) {
settings.m_devSampleRate = response.getUsrpOutputSettings()->getDevSampleRate();
}
if (deviceSettingsKeys.contains("clockSource")) {
settings.m_clockSource = *response.getUsrpOutputSettings()->getClockSource();
}
if (deviceSettingsKeys.contains("gain")) {
settings.m_gain = response.getUsrpOutputSettings()->getGain();
}
if (deviceSettingsKeys.contains("log2SoftInterp")) {
settings.m_log2SoftInterp = response.getUsrpOutputSettings()->getLog2SoftInterp();
}
if (deviceSettingsKeys.contains("lpfBW")) {
settings.m_lpfBW = response.getUsrpOutputSettings()->getLpfBw();
}
if (deviceSettingsKeys.contains("transverterDeltaFrequency")) {
settings.m_transverterDeltaFrequency = response.getUsrpOutputSettings()->getTransverterDeltaFrequency();
}
if (deviceSettingsKeys.contains("transverterMode")) {
settings.m_transverterMode = response.getUsrpOutputSettings()->getTransverterMode() != 0;
}
if (deviceSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getUsrpOutputSettings()->getUseReverseApi() != 0;
}
if (deviceSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getUsrpOutputSettings()->getReverseApiAddress();
}
if (deviceSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getUsrpOutputSettings()->getReverseApiPort();
}
if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getUsrpOutputSettings()->getReverseApiDeviceIndex();
}
}
int USRPOutput::webapiReportGet(
SWGSDRangel::SWGDeviceReport& response,
QString& errorMessage)
{
(void) errorMessage;
response.setUsrpOutputReport(new SWGSDRangel::SWGUSRPOutputReport());
response.getUsrpOutputReport()->init();
webapiFormatDeviceReport(response);
return 200;
}
void USRPOutput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const USRPOutputSettings& settings)
{
response.getUsrpOutputSettings()->setAntennaPath(new QString(settings.m_antennaPath));
response.getUsrpOutputSettings()->setCenterFrequency(settings.m_centerFrequency);
response.getUsrpOutputSettings()->setDevSampleRate(settings.m_devSampleRate);
response.getUsrpOutputSettings()->setClockSource(new QString(settings.m_clockSource));
response.getUsrpOutputSettings()->setGain(settings.m_gain);
response.getUsrpOutputSettings()->setLog2SoftInterp(settings.m_log2SoftInterp);
response.getUsrpOutputSettings()->setLpfBw(settings.m_lpfBW);
response.getUsrpOutputSettings()->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency);
response.getUsrpOutputSettings()->setTransverterMode(settings.m_transverterMode ? 1 : 0);
response.getUsrpOutputSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getUsrpOutputSettings()->getReverseApiAddress()) {
*response.getUsrpOutputSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getUsrpOutputSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getUsrpOutputSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getUsrpOutputSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
}
int USRPOutput::webapiRunGet(
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage)
{
(void) errorMessage;
m_deviceAPI->getDeviceEngineStateStr(*response.getState());
return 200;
}
int USRPOutput::webapiRun(
bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage)
{
(void) errorMessage;
m_deviceAPI->getDeviceEngineStateStr(*response.getState());
MsgStartStop *message = MsgStartStop::create(run);
m_inputMessageQueue.push(message);
if (m_guiMessageQueue)
{
MsgStartStop *messagetoGui = MsgStartStop::create(run);
m_guiMessageQueue->push(messagetoGui);
}
return 200;
}
void USRPOutput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response)
{
bool success = false;
bool active = false;
quint32 underflows = 0;
quint32 droppedPackets = 0;
if ((m_streamId != nullptr) && m_channelAcquired)
{
m_usrpOutputThread->getStreamStatus(active, underflows, droppedPackets);
success = true;
}
response.getUsrpOutputReport()->setSuccess(success ? 1 : 0);
response.getUsrpOutputReport()->setStreamActive(active ? 1 : 0);
response.getUsrpOutputReport()->setUnderrunCount(underflows);
response.getUsrpOutputReport()->setDroppedPacketsCount(droppedPackets);
}
void USRPOutput::webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const USRPOutputSettings& settings, bool force)
{
SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings();
swgDeviceSettings->setDirection(1); // single Tx
swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
swgDeviceSettings->setDeviceHwType(new QString("USRP"));
swgDeviceSettings->setUsrpOutputSettings(new SWGSDRangel::SWGUSRPOutputSettings());
SWGSDRangel::SWGUSRPOutputSettings *swgUsrpOutputSettings = swgDeviceSettings->getUsrpOutputSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (deviceSettingsKeys.contains("antennaPath") || force) {
swgUsrpOutputSettings->setAntennaPath(new QString(settings.m_antennaPath));
}
if (deviceSettingsKeys.contains("centerFrequency") || force) {
swgUsrpOutputSettings->setCenterFrequency(settings.m_centerFrequency);
}
if (deviceSettingsKeys.contains("devSampleRate") || force) {
swgUsrpOutputSettings->setDevSampleRate(settings.m_devSampleRate);
}
if (deviceSettingsKeys.contains("clockSource") || force) {
swgUsrpOutputSettings->setClockSource(new QString(settings.m_clockSource));
}
if (deviceSettingsKeys.contains("gain") || force) {
swgUsrpOutputSettings->setGain(settings.m_gain);
}
if (deviceSettingsKeys.contains("log2SoftInterp") || force) {
swgUsrpOutputSettings->setLog2SoftInterp(settings.m_log2SoftInterp);
}
if (deviceSettingsKeys.contains("lpfBW") || force) {
swgUsrpOutputSettings->setLpfBw(settings.m_lpfBW);
}
if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) {
swgUsrpOutputSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency);
}
if (deviceSettingsKeys.contains("transverterMode") || force) {
swgUsrpOutputSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0);
}
QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIDeviceIndex);
m_networkRequest.setUrl(QUrl(deviceSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgDeviceSettings->asJson().toUtf8());
buffer->seek(0);
// Always use PATCH to avoid passing reverse API settings
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
buffer->setParent(reply);
delete swgDeviceSettings;
}
void USRPOutput::webapiReverseSendStartStop(bool start)
{
SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings();
swgDeviceSettings->setDirection(1); // single Tx
swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
swgDeviceSettings->setDeviceHwType(new QString("USRP"));
QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run")
.arg(m_settings.m_reverseAPIAddress)
.arg(m_settings.m_reverseAPIPort)
.arg(m_settings.m_reverseAPIDeviceIndex);
m_networkRequest.setUrl(QUrl(deviceSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgDeviceSettings->asJson().toUtf8());
buffer->seek(0);
QNetworkReply *reply;
if (start) {
reply = m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer);
} else {
reply = m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer);
}
buffer->setParent(reply);
delete swgDeviceSettings;
}
void USRPOutput::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "USRPOutput::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("USRPOutput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}