From 209be94947015a97054588ea2f6dab599040f2ed Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Fri, 23 Oct 2020 13:12:37 +0100 Subject: [PATCH] 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. --- devices/usrp/deviceusrp.h | 2 - plugins/samplesink/usrpoutput/usrpoutput.cpp | 53 +++++++++------ plugins/samplesink/usrpoutput/usrpoutput.h | 1 + .../usrpoutput/usrpoutputthread.cpp | 27 +++++--- .../samplesink/usrpoutput/usrpoutputthread.h | 5 +- plugins/samplesource/usrpinput/usrpinput.cpp | 10 ++- plugins/samplesource/usrpinput/usrpinput.h | 1 + .../usrpinput/usrpinputthread.cpp | 67 +++++++++++++------ .../samplesource/usrpinput/usrpinputthread.h | 8 ++- 9 files changed, 115 insertions(+), 59 deletions(-) diff --git a/devices/usrp/deviceusrp.h b/devices/usrp/deviceusrp.h index c447a422f..497129e72 100644 --- a/devices/usrp/deviceusrp.h +++ b/devices/usrp/deviceusrp.h @@ -33,8 +33,6 @@ public: /** Enumeration of USRP hardware devices */ static void enumOriginDevices(const QString& hardwareId, PluginInterface::OriginDevices& originDevices); - /** Block size used for transferring IQ samples to and from the device. This perhaps needs tuning. */ - static const unsigned int blockSize = (1<<15); }; #endif /* DEVICES_USRP_DEVICEUSRP_H_ */ diff --git a/plugins/samplesink/usrpoutput/usrpoutput.cpp b/plugins/samplesink/usrpoutput/usrpoutput.cpp index 5cbbbc047..0b2b5d33e 100644 --- a/plugins/samplesink/usrpoutput/usrpoutput.cpp +++ b/plugins/samplesink/usrpoutput/usrpoutput.cpp @@ -51,6 +51,7 @@ USRPOutput::USRPOutput(DeviceAPI *deviceAPI) : m_deviceAPI(deviceAPI), m_settings(), m_usrpOutputThread(nullptr), + m_bufSamples(0), m_deviceDescription("USRPOutput"), m_running(false), m_channelAcquired(false) @@ -288,22 +289,28 @@ bool USRPOutput::acquireChannel() suspendRxBuddies(); suspendTxBuddies(); - try + if (m_streamId == nullptr) { - // set up the stream - std::string cpu_format("sc16"); - std::string wire_format("sc16"); - std::vector channel_nums; - channel_nums.push_back(m_deviceShared.m_channel); + try + { + // set up the stream + std::string cpu_format("sc16"); + std::string wire_format("sc16"); + std::vector 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; + 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); - } - catch (std::exception& e) - { - qDebug() << "USRPOutput::acquireChannel: exception: " << e.what(); + 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(); @@ -319,8 +326,14 @@ void USRPOutput::releaseChannel() suspendRxBuddies(); suspendTxBuddies(); - // destroy the stream - FIXME: Better way to do this? - m_streamId = nullptr; + // 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(); @@ -346,9 +359,7 @@ bool USRPOutput::start() return false; } - // start / stop streaming is done in the thread. - - m_usrpOutputThread = new USRPOutputThread(m_streamId, &m_sampleSourceFifo); + m_usrpOutputThread = new USRPOutputThread(m_streamId, m_bufSamples, &m_sampleSourceFifo); qDebug("USRPOutput::start: thread created"); applySettings(m_settings, true); @@ -563,7 +574,7 @@ bool USRPOutput::handleMessage(const Message& message) { if (m_deviceAPI->getSamplingDeviceGUIMessageQueue()) { - if (m_streamId != nullptr) + if ((m_streamId != nullptr) && m_channelAcquired) { bool active; quint32 underflows; @@ -1023,8 +1034,8 @@ void USRPOutput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response bool active = false; quint32 underflows = 0; quint32 droppedPackets = 0; - - if (m_streamId != nullptr) + + if ((m_streamId != nullptr) && m_channelAcquired) { m_usrpOutputThread->getStreamStatus(active, underflows, droppedPackets); success = true; diff --git a/plugins/samplesink/usrpoutput/usrpoutput.h b/plugins/samplesink/usrpoutput/usrpoutput.h index 999b0adde..7ebec32d0 100644 --- a/plugins/samplesink/usrpoutput/usrpoutput.h +++ b/plugins/samplesink/usrpoutput/usrpoutput.h @@ -226,6 +226,7 @@ private: DeviceUSRPShared m_deviceShared; bool m_channelAcquired; uhd::tx_streamer::sptr m_streamId; + size_t m_bufSamples; QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; diff --git a/plugins/samplesink/usrpoutput/usrpoutputthread.cpp b/plugins/samplesink/usrpoutput/usrpoutputthread.cpp index 9537cab0d..2b6edd6cf 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputthread.cpp +++ b/plugins/samplesink/usrpoutput/usrpoutputthread.cpp @@ -26,19 +26,23 @@ #include "usrpoutputthread.h" #include "usrpoutputsettings.h" -USRPOutputThread::USRPOutputThread(uhd::tx_streamer::sptr stream, SampleSourceFifo* sampleFifo, QObject* parent) : +USRPOutputThread::USRPOutputThread(uhd::tx_streamer::sptr stream, size_t bufSamples, SampleSourceFifo* sampleFifo, QObject* parent) : QThread(parent), m_running(false), m_stream(stream), + m_bufSamples(bufSamples), m_sampleFifo(sampleFifo), m_log2Interp(0) { - std::fill(m_buf, m_buf + 2*DeviceUSRP::blockSize, 0); + // *2 as samples are I+Q + m_buf = new qint16[2*bufSamples]; + std::fill(m_buf, m_buf + 2*bufSamples, 0); } USRPOutputThread::~USRPOutputThread() { stopWork(); + delete m_buf; } void USRPOutputThread::startWork() @@ -66,8 +70,15 @@ void USRPOutputThread::stopWork() m_running = false; wait(); - // Get message indicating underflow, so it doesn't appear if we restart - m_stream->recv_async_msg(md); + try + { + // Get message indicating underflow, so it doesn't appear if we restart + m_stream->recv_async_msg(md); + } + catch (std::exception& e) + { + qDebug() << "USRPOutputThread::stopWork: exception: " << e.what(); + } qDebug("USRPOutputThread::stopWork: stream stopped"); } @@ -90,15 +101,15 @@ void USRPOutputThread::run() while (m_running) { - callback(m_buf, DeviceUSRP::blockSize); + callback(m_buf, m_bufSamples); try { - const size_t samples_sent = m_stream->send(m_buf, DeviceUSRP::blockSize, md); + const size_t samples_sent = m_stream->send(m_buf, m_bufSamples, md); m_packets++; - if (samples_sent != DeviceUSRP::blockSize) + if (samples_sent != m_bufSamples) { - qDebug("USRPOutputThread::run written %ld/%d samples", samples_sent, DeviceUSRP::blockSize); + qDebug("USRPOutputThread::run written %ld/%d samples", samples_sent, m_bufSamples); } } catch (std::exception& e) diff --git a/plugins/samplesink/usrpoutput/usrpoutputthread.h b/plugins/samplesink/usrpoutput/usrpoutputthread.h index fcd59a5ad..c1a8f658b 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputthread.h +++ b/plugins/samplesink/usrpoutput/usrpoutputthread.h @@ -37,7 +37,7 @@ class USRPOutputThread : public QThread, public DeviceUSRPShared::ThreadInterfac Q_OBJECT public: - USRPOutputThread(uhd::tx_streamer::sptr stream, SampleSourceFifo* sampleFifo, QObject* parent = 0); + USRPOutputThread(uhd::tx_streamer::sptr stream, size_t bufSamples, SampleSourceFifo* sampleFifo, QObject* parent = 0); ~USRPOutputThread(); virtual void startWork(); @@ -57,7 +57,8 @@ private: quint32 m_droppedPackets; uhd::tx_streamer::sptr m_stream; - qint16 m_buf[2*DeviceUSRP::blockSize]; //must hold I+Q values of each sample hence 2xcomplex size + qint16 *m_buf; + size_t m_bufSamples; SampleSourceFifo* m_sampleFifo; unsigned int m_log2Interp; // soft decimation diff --git a/plugins/samplesource/usrpinput/usrpinput.cpp b/plugins/samplesource/usrpinput/usrpinput.cpp index 05ac35aae..d7598aa02 100644 --- a/plugins/samplesource/usrpinput/usrpinput.cpp +++ b/plugins/samplesource/usrpinput/usrpinput.cpp @@ -51,6 +51,7 @@ USRPInput::USRPInput(DeviceAPI *deviceAPI) : m_deviceAPI(deviceAPI), m_settings(), m_usrpInputThread(nullptr), + m_bufSamples(0), m_deviceDescription("USRPInput"), m_running(false), m_channelAcquired(false) @@ -91,7 +92,9 @@ void USRPInput::destroy() bool USRPInput::openDevice() { - if (!m_sampleFifo.setSize(96000 * 4)) + // B210 supports up to 50MSa/s, so a fairly large FIFO is probably a good idea + // Should it be bigger still? + if (!m_sampleFifo.setSize(2000000)) { qCritical("USRPInput::openDevice: could not allocate SampleFifo"); return false; @@ -325,6 +328,9 @@ bool USRPInput::acquireChannel() stream_args.channels = channel_nums; m_streamId = m_deviceShared.m_deviceParams->getDevice()->get_rx_stream(stream_args); + + // Match our receive buffer size to what UHD uses + m_bufSamples = m_streamId->get_max_num_samps(); } catch (std::exception& e) { @@ -375,7 +381,7 @@ bool USRPInput::start() // start / stop streaming is done in the thread. - m_usrpInputThread = new USRPInputThread(m_streamId, &m_sampleFifo); + m_usrpInputThread = new USRPInputThread(m_streamId, m_bufSamples, &m_sampleFifo); qDebug("USRPInput::start: thread created"); applySettings(m_settings, true); diff --git a/plugins/samplesource/usrpinput/usrpinput.h b/plugins/samplesource/usrpinput/usrpinput.h index b84ec2028..82cbefa85 100644 --- a/plugins/samplesource/usrpinput/usrpinput.h +++ b/plugins/samplesource/usrpinput/usrpinput.h @@ -227,6 +227,7 @@ private: DeviceUSRPShared m_deviceShared; bool m_channelAcquired; uhd::rx_streamer::sptr m_streamId; + size_t m_bufSamples; QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; diff --git a/plugins/samplesource/usrpinput/usrpinputthread.cpp b/plugins/samplesource/usrpinput/usrpinputthread.cpp index 54b81aab5..4935c8576 100644 --- a/plugins/samplesource/usrpinput/usrpinputthread.cpp +++ b/plugins/samplesource/usrpinput/usrpinputthread.cpp @@ -26,20 +26,35 @@ #include "usrpinputsettings.h" #include "usrpinputthread.h" -USRPInputThread::USRPInputThread(uhd::rx_streamer::sptr stream, SampleSinkFifo* sampleFifo, QObject* parent) : +USRPInputThread::USRPInputThread(uhd::rx_streamer::sptr stream, size_t bufSamples, SampleSinkFifo* sampleFifo, QObject* parent) : QThread(parent), m_running(false), m_stream(stream), - m_convertBuffer(DeviceUSRP::blockSize), + m_bufSamples(bufSamples), + m_convertBuffer(bufSamples), m_sampleFifo(sampleFifo), m_log2Decim(0) { - std::fill(m_buf, m_buf + 2*DeviceUSRP::blockSize, 0); + // *2 as samples are I+Q + m_buf = new qint16[2*bufSamples]; + std::fill(m_buf, m_buf + 2*bufSamples, 0); } USRPInputThread::~USRPInputThread() { stopWork(); + delete m_buf; +} + +void USRPInputThread::issueStreamCmd(bool start) +{ + uhd::stream_cmd_t stream_cmd(start ? uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS : uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + stream_cmd.num_samps = size_t(0); + stream_cmd.stream_now = true; + stream_cmd.time_spec = uhd::time_spec_t(); + + m_stream->issue_stream_cmd(stream_cmd); + qDebug() << "USRPInputThread::issueStreamCmd " << (start ? "start" : "stop"); } void USRPInputThread::startWork() @@ -48,12 +63,8 @@ void USRPInputThread::startWork() try { - uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - stream_cmd.num_samps = size_t(0); - stream_cmd.stream_now = true; - stream_cmd.time_spec = uhd::time_spec_t(); - - m_stream->issue_stream_cmd(stream_cmd); + // Start streaming + issueStreamCmd(true); // Reset stats m_packets = 0; @@ -84,18 +95,20 @@ void USRPInputThread::stopWork() try { uhd::rx_metadata_t md; - uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); - stream_cmd.stream_now = true; - m_stream->issue_stream_cmd(stream_cmd); + + // Stop streaming + issueStreamCmd(false); // Clear out any data left in the stream, otherwise we'll get an // exception 'recv buffer smaller than vrt packet offset' when restarting - while (!md.end_of_burst) + md.end_of_burst = false; + md.error_code = uhd::rx_metadata_t::ERROR_CODE_NONE; + while (!md.end_of_burst && (md.error_code != uhd::rx_metadata_t::ERROR_CODE_TIMEOUT)) { try { - //qDebug() << "USRPInputThread::stopWork: recing until end of burst"; - m_stream->recv(m_buf, DeviceUSRP::blockSize, md); + md.reset(); + m_stream->recv(m_buf, m_bufSamples, md); } catch (std::exception& e) { @@ -127,27 +140,39 @@ void USRPInputThread::run() { while (m_running) { - const size_t samples_received = m_stream->recv(m_buf, DeviceUSRP::blockSize, md); + md.reset(); + const size_t samples_received = m_stream->recv(m_buf, m_bufSamples, md); m_packets++; - if (samples_received != DeviceUSRP::blockSize) + if (samples_received != m_bufSamples) { - qDebug("USRPInputThread::run - received %ld/%d samples", samples_received, DeviceUSRP::blockSize); + qDebug("USRPInputThread::run - received %ld/%d samples", samples_received, m_bufSamples); } if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) { qDebug("USRPInputThread::run - timeout - ending thread"); m_timeouts++; - // It seems we can't recover after a timeout, so stop thread - m_running = false; + // Restart streaming + issueStreamCmd(false); + issueStreamCmd(true); + qDebug("USRPInputThread::run - timeout - restarting"); } else if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) { qDebug("USRPInputThread::run - overflow"); m_overflows++; } + else if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND) + qDebug("USRPInputThread::run - late command error"); + else if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN) + qDebug("USRPInputThread::run - broken chain error"); + else if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT) + qDebug("USRPInputThread::run - alignment error"); + else if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET) + qDebug("USRPInputThread::run - bad packet error"); - callbackIQ(m_buf, 2 * samples_received); + if (samples_received > 0) + callbackIQ(m_buf, 2 * samples_received); } } catch (std::exception& e) diff --git a/plugins/samplesource/usrpinput/usrpinputthread.h b/plugins/samplesource/usrpinput/usrpinputthread.h index f6d049844..9894f3141 100644 --- a/plugins/samplesource/usrpinput/usrpinputthread.h +++ b/plugins/samplesource/usrpinput/usrpinputthread.h @@ -36,7 +36,7 @@ class USRPInputThread : public QThread, public DeviceUSRPShared::ThreadInterface Q_OBJECT public: - USRPInputThread(uhd::rx_streamer::sptr stream, SampleSinkFifo* sampleFifo, QObject* parent = 0); + USRPInputThread(uhd::rx_streamer::sptr stream, size_t bufSamples, SampleSinkFifo* sampleFifo, QObject* parent = 0); ~USRPInputThread(); virtual void startWork(); @@ -45,6 +45,7 @@ public: virtual bool isRunning() { return m_running; } void setLog2Decimation(unsigned int log2_decim); void getStreamStatus(bool& active, quint32& overflows, quint32& m_timeouts); + void issueStreamCmd(bool start); private: QMutex m_startWaitMutex; @@ -56,13 +57,14 @@ private: quint32 m_timeouts; uhd::rx_streamer::sptr m_stream; - qint16 m_buf[2*DeviceUSRP::blockSize]; //must hold I+Q values of each sample hence 2xcomplex size + qint16 *m_buf; + size_t m_bufSamples; SampleVector m_convertBuffer; SampleSinkFifo* m_sampleFifo; unsigned int m_log2Decim; // soft decimation - Decimators m_decimatorsIQ; + Decimators m_decimatorsIQ; void run(); void callbackIQ(const qint16* buf, qint32 len);