mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-03-21 11:48:54 -04:00
LimeSDR output (1)
This commit is contained in:
parent
4ae889c06a
commit
f447c9f9bd
55
plugins/samplesink/limesdroutput/CMakeLists.txt
Normal file
55
plugins/samplesink/limesdroutput/CMakeLists.txt
Normal file
@ -0,0 +1,55 @@
|
||||
project(limesdroutput)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
|
||||
set(limesdroutput_SOURCES
|
||||
limesdroutputgui.cpp
|
||||
limesdroutput.cpp
|
||||
limesdroutputplugin.cpp
|
||||
limesdroutputsettings.cpp
|
||||
limesdroutputthread.cpp
|
||||
)
|
||||
|
||||
set(limesdroutput_HEADERS
|
||||
limesdroutputgui.h
|
||||
limesdroutput.h
|
||||
limesdroutputplugin.h
|
||||
limesdroutputsettings.h
|
||||
limesdroutputthread.h
|
||||
)
|
||||
|
||||
set(limesdroutput_FORMS
|
||||
limesdroutputgui.ui
|
||||
)
|
||||
|
||||
include_directories(
|
||||
.
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/devices
|
||||
${LIMESUITE_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
#include(${QT_USE_FILE})
|
||||
add_definitions(${QT_DEFINITIONS})
|
||||
add_definitions(-DQT_PLUGIN)
|
||||
add_definitions(-DQT_SHARED)
|
||||
|
||||
#qt4_wrap_cpp(limesdroutput_HEADERS_MOC ${limesdroutput_HEADERS})
|
||||
qt5_wrap_ui(limesdroutput_FORMS_HEADERS ${limesdroutput_FORMS})
|
||||
|
||||
add_library(outputlimesdr SHARED
|
||||
${limesdroutput_SOURCES}
|
||||
${limesdroutput_HEADERS_MOC}
|
||||
${limesdroutput_FORMS_HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries(outputlimesdr
|
||||
${QT_LIBRARIES}
|
||||
${LIMESUITE_LIBRARY}
|
||||
sdrbase
|
||||
limesdrdevice
|
||||
)
|
||||
|
||||
qt5_use_modules(inputlimesdr Core Widgets)
|
||||
|
||||
install(TARGETS inputlimesdr DESTINATION lib/plugins/samplesource)
|
845
plugins/samplesink/limesdroutput/limesdroutput.cpp
Normal file
845
plugins/samplesink/limesdroutput/limesdroutput.cpp
Normal file
@ -0,0 +1,845 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QMutexLocker>
|
||||
#include <QDebug>
|
||||
#include <cstddef>
|
||||
#include <string.h>
|
||||
#include "lime/LimeSuite.h"
|
||||
|
||||
#include "device/devicesourceapi.h"
|
||||
#include "device/devicesinkapi.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "limesdrinputthread.h"
|
||||
#include "limesdr/devicelimesdrparam.h"
|
||||
#include "limesdr/devicelimesdr.h"
|
||||
#include "limesdroutput.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(LimeSDROutput::MsgConfigureLimeSDR, Message)
|
||||
MESSAGE_CLASS_DEFINITION(LimeSDROutput::MsgGetStreamInfo, Message)
|
||||
MESSAGE_CLASS_DEFINITION(LimeSDROutput::MsgSetReferenceConfig, Message)
|
||||
MESSAGE_CLASS_DEFINITION(LimeSDROutput::MsgReportLimeSDRToGUI, Message)
|
||||
MESSAGE_CLASS_DEFINITION(LimeSDROutput::MsgReportStreamInfo, Message)
|
||||
|
||||
|
||||
LimeSDROutput::LimeSDROutput(DeviceSinkAPI *deviceAPI) :
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_settings(),
|
||||
m_limeSDROutputThread(0),
|
||||
m_deviceDescription(),
|
||||
m_running(false),
|
||||
m_firstConfig(true)
|
||||
{
|
||||
m_streamId.handle = 0;
|
||||
openDevice();
|
||||
}
|
||||
|
||||
LimeSDROutput::~LimeSDROutput()
|
||||
{
|
||||
if (m_running) stop();
|
||||
closeDevice();
|
||||
}
|
||||
|
||||
bool LimeSDROutput::openDevice()
|
||||
{
|
||||
// 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("LimeSDROutput::openDevice: look in Ix buddies");
|
||||
|
||||
DeviceSinkAPI *sinkBuddy = m_deviceAPI->getSinkBuddies()[0];
|
||||
m_deviceShared = *((DeviceLimeSDRShared *) sinkBuddy->getBuddySharedPtr()); // copy shared data
|
||||
DeviceLimeSDRParams *deviceParams = m_deviceShared.m_deviceParams; // get device parameters
|
||||
|
||||
if (deviceParams == 0)
|
||||
{
|
||||
qCritical("LimeSDROutput::openDevice: cannot get device parameters from Tx buddy");
|
||||
return false; // the device params should have been created by the buddy
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("LimeSDROutput::openDevice: getting device parameters from Tx buddy");
|
||||
}
|
||||
|
||||
if (m_deviceAPI->getSinkBuddies().size() == deviceParams->m_nbTxChannels)
|
||||
{
|
||||
qCritical("LimeSDROutput::openDevice: no more Tx channels available in device");
|
||||
return false; // no more Tx channels available in device
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("LimeSDROutput::openDevice: at least one more Tx channel is available in device");
|
||||
}
|
||||
|
||||
// look for unused channel number
|
||||
char *busyChannels = new char[deviceParams->m_nbTxChannels];
|
||||
memset(busyChannels, 0, deviceParams->m_nbTxChannels);
|
||||
|
||||
for (int i = 0; i < m_deviceAPI->getSinkBuddies().size(); i++)
|
||||
{
|
||||
DeviceSinkAPI *buddy = m_deviceAPI->getSinkBuddies()[i];
|
||||
DeviceLimeSDRShared *buddyShared = (DeviceLimeSDRShared *) buddy->getBuddySharedPtr();
|
||||
busyChannels[buddyShared->m_channel] = 1;
|
||||
|
||||
if (buddyShared->m_thread) { // suspend Tx buddy's thread for proper stream allocation later
|
||||
((LimeSDROutputThread *) buddyShared->m_thread)->stopWork();
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t ch = 0;
|
||||
|
||||
for (;ch < deviceParams->m_nbTxChannels; ch++)
|
||||
{
|
||||
if (busyChannels[ch] == 0) {
|
||||
break; // first available is the good one
|
||||
}
|
||||
}
|
||||
|
||||
m_deviceShared.m_channel = ch;
|
||||
delete[] busyChannels;
|
||||
}
|
||||
// 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("LimeSDROutput::openDevice: look in Rx buddies");
|
||||
|
||||
DeviceSourceAPI *sourceBuddy = m_deviceAPI->getSourceBuddies()[0];
|
||||
m_deviceShared = *((DeviceLimeSDRShared *) sourceBuddy->getBuddySharedPtr()); // copy parameters
|
||||
|
||||
if (m_deviceShared.m_deviceParams == 0)
|
||||
{
|
||||
qCritical("LimeSDROutput::openDevice: cannot get device parameters from Rx buddy");
|
||||
return false; // the device params should have been created by the buddy
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("LimeSDROutput::openDevice: getting device parameters from Rx buddy");
|
||||
}
|
||||
|
||||
m_deviceShared.m_channel = 0; // take first channel
|
||||
}
|
||||
// There are no buddies then create the first LimeSDR common parameters
|
||||
// open the device this will also populate common fields
|
||||
// take the first Tx channel
|
||||
else
|
||||
{
|
||||
qDebug("LimeSDROutput::openDevice: open device here");
|
||||
|
||||
m_deviceShared.m_deviceParams = new DeviceLimeSDRParams();
|
||||
char serial[256];
|
||||
strcpy(serial, qPrintable(m_deviceAPI->getSampleSinkSerial()));
|
||||
m_deviceShared.m_deviceParams->open(serial);
|
||||
m_deviceShared.m_channel = 0; // take first channel
|
||||
}
|
||||
|
||||
m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API
|
||||
|
||||
// acquire the channel
|
||||
|
||||
if (LMS_EnableChannel(m_deviceShared.m_deviceParams->getDevice(), LMS_CH_TX, m_deviceShared.m_channel, true) != 0)
|
||||
{
|
||||
qCritical("LimeSDROutput::openDevice: cannot enable Tx channel %lu", m_deviceShared.m_channel);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("LimeSDROutput::openDevice: Tx channel %lu enabled", m_deviceShared.m_channel);
|
||||
}
|
||||
|
||||
// set up the stream
|
||||
|
||||
m_streamId.channel = m_deviceShared.m_channel; // channel number
|
||||
m_streamId.fifoSize = 1024 * 128; // fifo size in samples
|
||||
m_streamId.throughputVsLatency = 1.0; // optimize for max throughput
|
||||
m_streamId.isTx = true; // TX channel
|
||||
m_streamId.dataFmt = lms_stream_t::LMS_FMT_I12; // 12-bit integers
|
||||
|
||||
if (LMS_SetupStream(m_deviceShared.m_deviceParams->getDevice(), &m_streamId) != 0)
|
||||
{
|
||||
qCritical("LimeSDROutput::start: cannot setup the stream on Tx channel %lu", m_deviceShared.m_channel);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("LimeSDROutput::start: stream set up on Tx channel %lu", m_deviceShared.m_channel);
|
||||
}
|
||||
|
||||
// resume Tx buddy's threads
|
||||
|
||||
for (int i = 0; i < m_deviceAPI->getSinkBuddies().size(); i++)
|
||||
{
|
||||
DeviceSinkAPI *buddy = m_deviceAPI->getSinkBuddies()[i];
|
||||
DeviceLimeSDRShared *buddyShared = (DeviceLimeSDRShared *) buddy->getBuddySharedPtr();
|
||||
|
||||
if (buddyShared->m_thread) {
|
||||
((LimeSDROutputThread *) buddyShared->m_thread)->startWork();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LimeSDROutput::closeDevice()
|
||||
{
|
||||
if (m_deviceShared.m_deviceParams->getDevice() == 0) { // was never open
|
||||
return;
|
||||
}
|
||||
|
||||
// suspend Tx buddy's threads
|
||||
|
||||
for (int i = 0; i < m_deviceAPI->getSinkBuddies().size(); i++)
|
||||
{
|
||||
DeviceSinkAPI *buddy = m_deviceAPI->getSinkBuddies()[i];
|
||||
DeviceLimeSDRShared *buddyShared = (DeviceLimeSDRShared *) buddy->getBuddySharedPtr();
|
||||
|
||||
if (buddyShared->m_thread) {
|
||||
((LimeSDROutputThread *) buddyShared->m_thread)->stopWork();
|
||||
}
|
||||
}
|
||||
|
||||
// destroy the stream
|
||||
LMS_DestroyStream(m_deviceShared.m_deviceParams->getDevice(), &m_streamId);
|
||||
m_streamId.handle = 0;
|
||||
|
||||
// release the channel
|
||||
|
||||
if (LMS_EnableChannel(m_deviceShared.m_deviceParams->getDevice(), LMS_CH_TX, m_deviceShared.m_channel, false) != 0)
|
||||
{
|
||||
qWarning("LimeSDROutput::closeDevice: cannot disable Tx channel %lu", m_deviceShared.m_channel);
|
||||
}
|
||||
|
||||
m_deviceShared.m_channel = -1;
|
||||
|
||||
// resume Tx buddy's threads
|
||||
|
||||
for (int i = 0; i < m_deviceAPI->getSinkBuddies().size(); i++)
|
||||
{
|
||||
DeviceSourceAPI *buddy = m_deviceAPI->getSinkBuddies()[i];
|
||||
DeviceLimeSDRShared *buddyShared = (DeviceLimeSDRShared *) buddy->getBuddySharedPtr();
|
||||
|
||||
if (buddyShared->m_thread) {
|
||||
((LimeSDROutputThread *) buddyShared->m_thread)->startWork();
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
bool LimeSDROutput::start()
|
||||
{
|
||||
if (!m_deviceShared.m_deviceParams->getDevice()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_running) stop();
|
||||
|
||||
// start / stop streaming is done in the thread.
|
||||
|
||||
if ((m_limeSDROutputThread = new LimeSDROutputThread(&m_streamId, &m_sampleSourceFifo)) == 0)
|
||||
{
|
||||
qFatal("LimeSDROutput::start: cannot create thread");
|
||||
stop();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("LimeSDROutput::start: thread created");
|
||||
}
|
||||
|
||||
m_limeSDROutputThread->setLog2Interpolation(m_settings.m_log2SoftInterp);
|
||||
|
||||
m_limeSDROutputThread->startWork();
|
||||
|
||||
m_deviceShared.m_thread = (void *) m_limeSDROutputThread;
|
||||
m_running = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LimeSDROutput::stop()
|
||||
{
|
||||
if (m_limeSDROutputThread != 0)
|
||||
{
|
||||
m_limeSDROutputThread->stopWork();
|
||||
delete m_limeSDROutputThread;
|
||||
m_limeSDROutputThread = 0;
|
||||
}
|
||||
|
||||
m_deviceShared.m_thread = 0;
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
const QString& LimeSDROutput::getDeviceDescription() const
|
||||
{
|
||||
return m_deviceDescription;
|
||||
}
|
||||
|
||||
int LimeSDROutput::getSampleRate() const
|
||||
{
|
||||
int rate = m_settings.m_devSampleRate;
|
||||
return (rate / (1<<m_settings.m_log2SoftInterp));
|
||||
}
|
||||
|
||||
quint64 LimeSDROutput::getCenterFrequency() const
|
||||
{
|
||||
return m_settings.m_centerFrequency;
|
||||
}
|
||||
|
||||
std::size_t LimeSDROutput::getChannelIndex()
|
||||
{
|
||||
return m_deviceShared.m_channel;
|
||||
}
|
||||
|
||||
void LimeSDROutput::getLORange(float& minF, float& maxF, float& stepF) const
|
||||
{
|
||||
lms_range_t range = m_deviceShared.m_deviceParams->m_loRangeTx;
|
||||
minF = range.min;
|
||||
maxF = range.max;
|
||||
stepF = range.step;
|
||||
qDebug("LimeSDROutput::getLORange: min: %f max: %f step: %f", range.min, range.max, range.step);
|
||||
}
|
||||
|
||||
void LimeSDROutput::getSRRange(float& minF, float& maxF, float& stepF) const
|
||||
{
|
||||
lms_range_t range = m_deviceShared.m_deviceParams->m_srRangeTx;
|
||||
minF = range.min;
|
||||
maxF = range.max;
|
||||
stepF = range.step;
|
||||
qDebug("LimeSDROutput::getSRRange: min: %f max: %f step: %f", range.min, range.max, range.step);
|
||||
}
|
||||
|
||||
void LimeSDROutput::getLPRange(float& minF, float& maxF, float& stepF) const
|
||||
{
|
||||
lms_range_t range = m_deviceShared.m_deviceParams->m_lpfRangeTx;
|
||||
minF = range.min;
|
||||
maxF = range.max;
|
||||
stepF = range.step;
|
||||
qDebug("LimeSDROutput::getLPRange: min: %f max: %f step: %f", range.min, range.max, range.step);
|
||||
}
|
||||
|
||||
uint32_t LimeSDROutput::getHWLog2Interp() const
|
||||
{
|
||||
return m_deviceShared.m_deviceParams->m_log2OvSRTx;
|
||||
}
|
||||
|
||||
bool LimeSDROutput::handleMessage(const Message& message)
|
||||
{
|
||||
if (MsgConfigureLimeSDR::match(message))
|
||||
{
|
||||
MsgConfigureLimeSDR& conf = (MsgConfigureLimeSDR&) message;
|
||||
qDebug() << "LimeSDROutput::handleMessage: MsgConfigureLimeSDR";
|
||||
|
||||
if (!applySettings(conf.getSettings(), m_firstConfig))
|
||||
{
|
||||
qDebug("LimeSDROutput::handleMessage config error");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_firstConfig = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgSetReferenceConfig::match(message))
|
||||
{
|
||||
MsgSetReferenceConfig& conf = (MsgSetReferenceConfig&) message;
|
||||
qDebug() << "LimeSDROutput::handleMessage: MsgSetReferenceConfig";
|
||||
m_settings = conf.getSettings();
|
||||
m_deviceShared.m_ncoFrequency = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0; // for buddies
|
||||
m_deviceShared.m_centerFrequency = m_settings.m_centerFrequency; // for buddies
|
||||
return true;
|
||||
}
|
||||
else if (MsgGetStreamInfo::match(message))
|
||||
{
|
||||
// qDebug() << "LimeSDROutput::handleMessage: MsgGetStreamInfo";
|
||||
lms_stream_status_t status;
|
||||
|
||||
if (m_streamId.handle && (LMS_GetStreamStatus(&m_streamId, &status) == 0))
|
||||
{
|
||||
MsgReportStreamInfo *report = MsgReportStreamInfo::create(
|
||||
true, // Success
|
||||
status.active,
|
||||
status.fifoFilledCount,
|
||||
status.fifoSize,
|
||||
status.underrun,
|
||||
status.overrun,
|
||||
status.droppedPackets,
|
||||
status.sampleRate,
|
||||
status.linkRate,
|
||||
status.timestamp);
|
||||
m_deviceAPI->getDeviceOutputMessageQueue()->push(report);
|
||||
}
|
||||
else
|
||||
{
|
||||
MsgReportStreamInfo *report = MsgReportStreamInfo::create(
|
||||
false, // Success
|
||||
false, // status.active,
|
||||
0, // status.fifoFilledCount,
|
||||
16384, // status.fifoSize,
|
||||
0, // status.underrun,
|
||||
0, // status.overrun,
|
||||
0, // status.droppedPackets,
|
||||
0, // status.sampleRate,
|
||||
0, // status.linkRate,
|
||||
0); // status.timestamp);
|
||||
m_deviceAPI->getDeviceOutputMessageQueue()->push(report);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool force)
|
||||
{
|
||||
bool forwardChangeOwnDSP = false;
|
||||
bool forwardChangeTxDSP = false;
|
||||
bool forwardChangeAllDSP = false;
|
||||
bool suspendOwnThread = false;
|
||||
bool suspendTxThread = false;
|
||||
bool suspendAllThread = false;
|
||||
bool doCalibration = false;
|
||||
// QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
// determine if buddies threads or own thread need to be suspended
|
||||
|
||||
if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force)
|
||||
{
|
||||
suspendAllThread = true;
|
||||
}
|
||||
|
||||
if ((m_settings.m_log2HardInterp != settings.m_log2HardInterp) ||
|
||||
(m_settings.m_centerFrequency != settings.m_centerFrequency) || force)
|
||||
{
|
||||
suspendTxThread = true;
|
||||
}
|
||||
|
||||
if ((m_settings.m_gain != settings.m_gain) ||
|
||||
(m_settings.m_lpfBW != settings.m_lpfBW) ||
|
||||
(m_settings.m_lpfFIRBW != settings.m_lpfFIRBW) ||
|
||||
(m_settings.m_lpfFIREnable != settings.m_lpfFIREnable) ||
|
||||
(m_settings.m_ncoEnable != settings.m_ncoEnable) ||
|
||||
(m_settings.m_ncoFrequency != settings.m_ncoFrequency) || force)
|
||||
{
|
||||
suspendOwnThread = true;
|
||||
}
|
||||
|
||||
// suspend buddies threads or own thread
|
||||
|
||||
if (suspendAllThread)
|
||||
{
|
||||
const std::vector<DeviceSourceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
|
||||
std::vector<DeviceSourceAPI*>::const_iterator itSource = sourceBuddies.begin();
|
||||
|
||||
for (; itSource != sourceBuddies.end(); ++itSource)
|
||||
{
|
||||
DeviceLimeSDRShared *buddySharedPtr = (DeviceLimeSDRShared *) (*itSource)->getBuddySharedPtr();
|
||||
if (buddySharedPtr->m_thread)
|
||||
{
|
||||
((LimeSDRInputThread *) buddySharedPtr->m_thread)->stopWork();
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<DeviceSinkAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
|
||||
std::vector<DeviceSinkAPI*>::const_iterator itSink = sinkBuddies.begin();
|
||||
|
||||
for (; itSink != sinkBuddies.end(); ++itSink)
|
||||
{
|
||||
DeviceLimeSDRShared *buddySharedPtr = (DeviceLimeSDRShared *) (*itSink)->getBuddySharedPtr();
|
||||
if (buddySharedPtr->m_thread)
|
||||
{
|
||||
((LimeSDROutputThread *) buddySharedPtr->m_thread)->stopWork();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_limeSDROutputThread) {
|
||||
m_limeSDROutputThread->stopWork();
|
||||
}
|
||||
}
|
||||
else if (suspendTxThread)
|
||||
{
|
||||
const std::vector<DeviceSinkAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
|
||||
std::vector<DeviceSinkAPI*>::const_iterator itSink = sinkBuddies.begin();
|
||||
|
||||
for (; itSink != sinkBuddies.end(); ++itSink)
|
||||
{
|
||||
DeviceLimeSDRShared *buddySharedPtr = (DeviceLimeSDRShared *) (*itSink)->getBuddySharedPtr();
|
||||
if (buddySharedPtr->m_thread)
|
||||
{
|
||||
((LimeSDROutputThread *) buddySharedPtr->m_thread)->stopWork();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_limeSDROutputThread) {
|
||||
m_limeSDROutputThread->stopWork();
|
||||
}
|
||||
}
|
||||
else if (suspendOwnThread)
|
||||
{
|
||||
if (m_limeSDROutputThread) {
|
||||
m_limeSDROutputThread->stopWork();
|
||||
}
|
||||
}
|
||||
|
||||
// apply settings
|
||||
|
||||
if ((m_settings.m_gain != settings.m_gain) || force)
|
||||
{
|
||||
m_settings.m_gain = settings.m_gain;
|
||||
|
||||
if (m_deviceShared.m_deviceParams->getDevice() != 0)
|
||||
{
|
||||
if (LMS_SetGaindB(m_deviceShared.m_deviceParams->getDevice(),
|
||||
LMS_CH_TX,
|
||||
m_deviceShared.m_channel,
|
||||
m_settings.m_gain) < 0)
|
||||
{
|
||||
qDebug("LimeSDROutput::applySettings: LMS_SetGaindB() failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
doCalibration = true;
|
||||
qDebug() << "LimeSDROutput::applySettings: Gain set to " << m_settings.m_gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_devSampleRate != settings.m_devSampleRate)
|
||||
|| (m_settings.m_log2HardInterp != settings.m_log2HardInterp) || force)
|
||||
{
|
||||
forwardChangeTxDSP = m_settings.m_log2HardInterp != settings.m_log2HardInterp;
|
||||
forwardChangeAllDSP = m_settings.m_devSampleRate != settings.m_devSampleRate;
|
||||
|
||||
m_settings.m_devSampleRate = settings.m_devSampleRate;
|
||||
m_settings.m_log2HardInterp = settings.m_log2HardInterp;
|
||||
|
||||
if (m_deviceShared.m_deviceParams->getDevice() != 0)
|
||||
{
|
||||
if (LMS_SetSampleRateDir(m_deviceShared.m_deviceParams->getDevice(),
|
||||
LMS_CH_TX,
|
||||
m_settings.m_devSampleRate,
|
||||
1<<m_settings.m_log2HardInterp) < 0)
|
||||
{
|
||||
qCritical("LimeSDROutput::applySettings: could not set sample rate to %d with oversampling of %d",
|
||||
m_settings.m_devSampleRate,
|
||||
1<<m_settings.m_log2HardInterp);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_deviceShared.m_deviceParams->m_log2OvSRTx = m_settings.m_log2HardInterp;
|
||||
m_deviceShared.m_deviceParams->m_sampleRate = m_settings.m_devSampleRate;
|
||||
doCalibration = true;
|
||||
qDebug("LimeSDROutput::applySettings: set sample rate set to %d with oversampling of %d",
|
||||
m_settings.m_devSampleRate,
|
||||
1<<m_settings.m_log2HardInterp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_lpfBW != settings.m_lpfBW) || force)
|
||||
{
|
||||
m_settings.m_lpfBW = settings.m_lpfBW;
|
||||
|
||||
if (m_deviceShared.m_deviceParams->getDevice() != 0)
|
||||
{
|
||||
if (LMS_SetLPFBW(m_deviceShared.m_deviceParams->getDevice(),
|
||||
LMS_CH_TX,
|
||||
m_deviceShared.m_channel,
|
||||
m_settings.m_lpfBW) < 0)
|
||||
{
|
||||
qCritical("LimeSDROutput::applySettings: could not set LPF to %f Hz", m_settings.m_lpfBW);
|
||||
}
|
||||
else
|
||||
{
|
||||
doCalibration = true;
|
||||
qDebug("LimeSDROutput::applySettings: LPF set to %f Hz", m_settings.m_lpfBW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_lpfFIRBW != settings.m_lpfFIRBW) ||
|
||||
(m_settings.m_lpfFIREnable != settings.m_lpfFIREnable) || force)
|
||||
{
|
||||
m_settings.m_lpfFIRBW = settings.m_lpfFIRBW;
|
||||
m_settings.m_lpfFIREnable = settings.m_lpfFIREnable;
|
||||
|
||||
if (m_deviceShared.m_deviceParams->getDevice() != 0)
|
||||
{
|
||||
if (LMS_SetGFIRLPF(m_deviceShared.m_deviceParams->getDevice(),
|
||||
LMS_CH_TX,
|
||||
m_deviceShared.m_channel,
|
||||
m_settings.m_lpfFIREnable,
|
||||
m_settings.m_lpfFIRBW) < 0)
|
||||
{
|
||||
qCritical("LimeSDROutput::applySettings: could %s and set LPF FIR to %f Hz",
|
||||
m_settings.m_lpfFIREnable ? "enable" : "disable",
|
||||
m_settings.m_lpfFIRBW);
|
||||
}
|
||||
else
|
||||
{
|
||||
doCalibration = true;
|
||||
qDebug("LimeSDROutput::applySettings: %sd and set LPF FIR to %f Hz",
|
||||
m_settings.m_lpfFIREnable ? "enable" : "disable",
|
||||
m_settings.m_lpfFIRBW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_ncoFrequency != settings.m_ncoFrequency) ||
|
||||
(m_settings.m_ncoEnable != settings.m_ncoEnable) || force)
|
||||
{
|
||||
m_settings.m_ncoFrequency = settings.m_ncoFrequency;
|
||||
m_settings.m_ncoEnable = settings.m_ncoEnable;
|
||||
|
||||
if (m_deviceShared.m_deviceParams->getDevice() != 0)
|
||||
{
|
||||
if (DeviceLimeSDR::setNCOFrequency(m_deviceShared.m_deviceParams->getDevice(),
|
||||
LMS_CH_TX,
|
||||
m_deviceShared.m_channel,
|
||||
m_settings.m_ncoEnable,
|
||||
m_settings.m_ncoFrequency))
|
||||
{
|
||||
doCalibration = true;
|
||||
forwardChangeOwnDSP = true;
|
||||
m_deviceShared.m_ncoFrequency = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0; // for buddies
|
||||
qDebug("LimeSDROutput::applySettings: %sd and set NCO to %d Hz",
|
||||
m_settings.m_ncoEnable ? "enable" : "disable",
|
||||
m_settings.m_ncoFrequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical("LimeSDROutput::applySettings: could not %s and set NCO to %d Hz",
|
||||
m_settings.m_ncoEnable ? "enable" : "disable",
|
||||
m_settings.m_ncoFrequency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_log2SoftInterp != settings.m_log2SoftInterp) || force)
|
||||
{
|
||||
m_settings.m_log2SoftInterp = settings.m_log2SoftInterp;
|
||||
forwardChangeOwnDSP = true;
|
||||
|
||||
if (m_limeSDROutputThread != 0)
|
||||
{
|
||||
m_limeSDROutputThread->setLog2Interpolation(m_settings.m_log2SoftInterp);
|
||||
qDebug() << "LimeSDROutput::applySettings: set soft decimation to " << (1<<m_settings.m_log2SoftInterp);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force)
|
||||
{
|
||||
m_settings.m_centerFrequency = settings.m_centerFrequency;
|
||||
forwardChangeTxDSP = true;
|
||||
|
||||
if (m_deviceShared.m_deviceParams->getDevice() != 0)
|
||||
{
|
||||
if (LMS_SetLOFrequency(m_deviceShared.m_deviceParams->getDevice(),
|
||||
LMS_CH_TX,
|
||||
m_deviceShared.m_channel, // same for both channels anyway but switches antenna port automatically
|
||||
m_settings.m_centerFrequency) < 0)
|
||||
{
|
||||
qCritical("LimeSDROutput::applySettings: could not set frequency to %lu", m_settings.m_centerFrequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
doCalibration = true;
|
||||
m_deviceShared.m_centerFrequency = m_settings.m_centerFrequency; // for buddies
|
||||
qDebug("LimeSDROutput::applySettings: frequency set to %lu", m_settings.m_centerFrequency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (doCalibration)
|
||||
{
|
||||
if (LMS_Calibrate(m_deviceShared.m_deviceParams->getDevice(),
|
||||
LMS_CH_TX,
|
||||
m_deviceShared.m_channel,
|
||||
m_settings.m_lpfBW,
|
||||
0) < 0)
|
||||
{
|
||||
qCritical("LimeSDROutput::applySettings: calibration failed on Rx channel %lu", m_deviceShared.m_channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("LimeSDROutput::applySettings: calibration successful on Rx channel %lu", m_deviceShared.m_channel);
|
||||
}
|
||||
}
|
||||
|
||||
// resume buddies threads or own thread
|
||||
|
||||
if (suspendAllThread)
|
||||
{
|
||||
const std::vector<DeviceSourceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
|
||||
std::vector<DeviceSourceAPI*>::const_iterator itSource = sourceBuddies.begin();
|
||||
|
||||
for (; itSource != sourceBuddies.end(); ++itSource)
|
||||
{
|
||||
DeviceLimeSDRShared *buddySharedPtr = (DeviceLimeSDRShared *) (*itSource)->getBuddySharedPtr();
|
||||
if (buddySharedPtr->m_thread)
|
||||
{
|
||||
((LimeSDRInputThread *) buddySharedPtr->m_thread)->startWork();
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<DeviceSinkAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
|
||||
std::vector<DeviceSinkAPI*>::const_iterator itSink = sinkBuddies.begin();
|
||||
|
||||
for (; itSink != sinkBuddies.end(); ++itSink)
|
||||
{
|
||||
DeviceLimeSDRShared *buddySharedPtr = (DeviceLimeSDRShared *) (*itSink)->getBuddySharedPtr();
|
||||
if (buddySharedPtr->m_thread)
|
||||
{
|
||||
((LimeSDROutputThread *) buddySharedPtr->m_thread)->startWork();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_limeSDROutputThread) {
|
||||
m_limeSDROutputThread->startWork();
|
||||
}
|
||||
}
|
||||
else if (suspendTxThread)
|
||||
{
|
||||
const std::vector<DeviceSinkAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
|
||||
std::vector<DeviceSinkAPI*>::const_iterator itSink = sinkBuddies.begin();
|
||||
|
||||
for (; itSink != sinkBuddies.end(); ++itSink)
|
||||
{
|
||||
DeviceLimeSDRShared *buddySharedPtr = (DeviceLimeSDRShared *) (*itSink)->getBuddySharedPtr();
|
||||
if (buddySharedPtr->m_thread)
|
||||
{
|
||||
((LimeSDROutputThread *) buddySharedPtr->m_thread)->startWork();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_limeSDROutputThread) {
|
||||
m_limeSDROutputThread->startWork();
|
||||
}
|
||||
}
|
||||
else if (suspendOwnThread)
|
||||
{
|
||||
if (m_limeSDROutputThread) {
|
||||
m_limeSDROutputThread->startWork();
|
||||
}
|
||||
}
|
||||
|
||||
// forward changes to buddies or oneself
|
||||
|
||||
if (forwardChangeAllDSP)
|
||||
{
|
||||
qDebug("LimeSDROutput::applySettings: forward change to all buddies");
|
||||
|
||||
int sampleRate = m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp);
|
||||
int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
|
||||
|
||||
// send to self first
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, m_settings.m_centerFrequency + ncoShift);
|
||||
m_deviceAPI->getDeviceInputMessageQueue()->push(notif);
|
||||
|
||||
// send to sink buddies
|
||||
const std::vector<DeviceSinkAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
|
||||
std::vector<DeviceSinkAPI*>::const_iterator itSink = sinkBuddies.begin();
|
||||
|
||||
for (; itSink != sinkBuddies.end(); ++itSink)
|
||||
{
|
||||
DeviceLimeSDRShared *buddySharedPtr = (DeviceLimeSDRShared *) (*itSink)->getBuddySharedPtr();
|
||||
uint64_t buddyCenterFreq = buddySharedPtr->m_centerFrequency;
|
||||
int buddyNCOFreq = buddySharedPtr->m_ncoFrequency;
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, buddyCenterFreq + buddyNCOFreq); // do not change center frequency
|
||||
(*itSink)->getDeviceInputMessageQueue()->push(notif);
|
||||
MsgReportLimeSDRToGUI *report = MsgReportLimeSDRToGUI::create(
|
||||
m_settings.m_centerFrequency,
|
||||
m_settings.m_devSampleRate,
|
||||
m_settings.m_log2HardInterp);
|
||||
(*itSink)->getDeviceOutputMessageQueue()->push(report);
|
||||
}
|
||||
|
||||
// send to source buddies
|
||||
const std::vector<DeviceSourceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
|
||||
std::vector<DeviceSourceAPI*>::const_iterator itSource = sourceBuddies.begin();
|
||||
|
||||
for (; itSource != sourceBuddies.end(); ++itSource)
|
||||
{
|
||||
DeviceLimeSDRShared *buddySharedPtr = (DeviceLimeSDRShared *) (*itSource)->getBuddySharedPtr();
|
||||
int buddyNCOFreq = buddySharedPtr->m_ncoFrequency;
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, m_settings.m_centerFrequency + buddyNCOFreq);
|
||||
(*itSource)->getDeviceInputMessageQueue()->push(notif);
|
||||
MsgReportLimeSDRToGUI *report = MsgReportLimeSDRToGUI::create(
|
||||
m_settings.m_centerFrequency,
|
||||
m_settings.m_devSampleRate,
|
||||
m_settings.m_log2HardInterp);
|
||||
(*itSource)->getDeviceOutputMessageQueue()->push(report);
|
||||
}
|
||||
}
|
||||
else if (forwardChangeTxDSP)
|
||||
{
|
||||
qDebug("LimeSDROutput::applySettings: forward change to Tx buddies");
|
||||
|
||||
int sampleRate = m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp);
|
||||
int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
|
||||
|
||||
// send to self first
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, m_settings.m_centerFrequency + ncoShift);
|
||||
m_deviceAPI->getDeviceInputMessageQueue()->push(notif);
|
||||
|
||||
// send to sink buddies
|
||||
const std::vector<DeviceSinkAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
|
||||
std::vector<DeviceSinkAPI*>::const_iterator itSink = sinkBuddies.begin();
|
||||
|
||||
for (; itSink != sinkBuddies.end(); ++itSink)
|
||||
{
|
||||
DeviceLimeSDRShared *buddySharedPtr = (DeviceLimeSDRShared *) (*itSink)->getBuddySharedPtr();
|
||||
uint64_t buddyCenterFreq = buddySharedPtr->m_centerFrequency;
|
||||
int buddyNCOFreq = buddySharedPtr->m_ncoFrequency;
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, buddyCenterFreq + buddyNCOFreq); // do not change center frequency
|
||||
(*itSink)->getDeviceInputMessageQueue()->push(notif);
|
||||
MsgReportLimeSDRToGUI *report = MsgReportLimeSDRToGUI::create(
|
||||
m_settings.m_centerFrequency,
|
||||
m_settings.m_devSampleRate,
|
||||
m_settings.m_log2HardInterp);
|
||||
(*itSink)->getDeviceOutputMessageQueue()->push(report);
|
||||
}
|
||||
}
|
||||
else if (forwardChangeOwnDSP)
|
||||
{
|
||||
qDebug("LimeSDROutput::applySettings: forward change to self only");
|
||||
|
||||
int sampleRate = m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp);
|
||||
int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, m_settings.m_centerFrequency + ncoShift);
|
||||
m_deviceAPI->getDeviceInputMessageQueue()->push(notif);
|
||||
}
|
||||
|
||||
qDebug() << "LimeSDROutput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz"
|
||||
<< " device stream sample rate: " << m_settings.m_devSampleRate << "S/s"
|
||||
<< " sample rate with soft decimation: " << m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp) << "S/s";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
231
plugins/samplesink/limesdroutput/limesdroutput.h
Normal file
231
plugins/samplesink/limesdroutput/limesdroutput.h
Normal file
@ -0,0 +1,231 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUT_H_
|
||||
#define PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUT_H_
|
||||
|
||||
#include <QString>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "dsp/devicesamplesink.h"
|
||||
#include "limesdr/devicelimesdrshared.h"
|
||||
#include "limesdroutputsettings.h"
|
||||
|
||||
class DeviceSinkAPI;
|
||||
class LimeSDROutputThread;
|
||||
struct DeviceLimeSDRParams;
|
||||
|
||||
class LimeSDROutput : public DeviceSampleSink
|
||||
{
|
||||
public:
|
||||
class MsgConfigureLimeSDR : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const LimeSDROutputSettings& getSettings() const { return m_settings; }
|
||||
|
||||
static MsgConfigureLimeSDR* create(const LimeSDROutputSettings& settings)
|
||||
{
|
||||
return new MsgConfigureLimeSDR(settings);
|
||||
}
|
||||
|
||||
private:
|
||||
LimeSDROutputSettings m_settings;
|
||||
|
||||
MsgConfigureLimeSDR(const LimeSDROutputSettings& settings) :
|
||||
Message(),
|
||||
m_settings(settings)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgSetReferenceConfig : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const LimeSDROutputSettings& getSettings() const { return m_settings; }
|
||||
|
||||
static MsgSetReferenceConfig* create(const LimeSDROutputSettings& settings)
|
||||
{
|
||||
return new MsgSetReferenceConfig(settings);
|
||||
}
|
||||
|
||||
private:
|
||||
LimeSDROutputSettings m_settings;
|
||||
|
||||
MsgSetReferenceConfig(const LimeSDROutputSettings& settings) :
|
||||
Message(),
|
||||
m_settings(settings)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgGetStreamInfo : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
static MsgGetStreamInfo* create()
|
||||
{
|
||||
return new MsgGetStreamInfo();
|
||||
}
|
||||
|
||||
private:
|
||||
MsgGetStreamInfo() :
|
||||
Message()
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportLimeSDRToGUI : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
float getCenterFrequency() const { return m_centerFrequency; }
|
||||
int getSampleRate() const { return m_sampleRate; }
|
||||
uint32_t getLog2HardInterp() const { return m_log2HardInterp; }
|
||||
|
||||
static MsgReportLimeSDRToGUI* create(float centerFrequency, int sampleRate, uint32_t log2HardInterp)
|
||||
{
|
||||
return new MsgReportLimeSDRToGUI(centerFrequency, sampleRate, log2HardInterp);
|
||||
}
|
||||
|
||||
private:
|
||||
float m_centerFrequency;
|
||||
int m_sampleRate;
|
||||
uint32_t m_log2HardInterp;
|
||||
|
||||
MsgReportLimeSDRToGUI(float centerFrequency, int sampleRate, uint32_t log2HardInterp) :
|
||||
Message(),
|
||||
m_centerFrequency(centerFrequency),
|
||||
m_sampleRate(sampleRate),
|
||||
m_log2HardInterp(log2HardInterp)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportStreamInfo : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
bool getSuccess() const { return m_success; }
|
||||
bool getActive() const { return m_active; }
|
||||
uint32_t getFifoFilledCount() const { return m_fifoFilledCount; }
|
||||
uint32_t getFifoSize() const { return m_fifoSize; }
|
||||
uint32_t getUnderrun() const { return m_underrun; }
|
||||
uint32_t getOverrun() const { return m_overrun; }
|
||||
uint32_t getDroppedPackets() const { return m_droppedPackets; }
|
||||
float getSampleRate() const { return m_sampleRate; }
|
||||
float getLinkRate() const { return m_linkRate; }
|
||||
uint64_t getTimestamp() const { return m_timestamp; }
|
||||
|
||||
static MsgReportStreamInfo* create(
|
||||
bool success,
|
||||
bool active,
|
||||
uint32_t fifoFilledCount,
|
||||
uint32_t fifoSize,
|
||||
uint32_t underrun,
|
||||
uint32_t overrun,
|
||||
uint32_t droppedPackets,
|
||||
float sampleRate,
|
||||
float linkRate,
|
||||
uint64_t timestamp
|
||||
)
|
||||
{
|
||||
return new MsgReportStreamInfo(
|
||||
success,
|
||||
active,
|
||||
fifoFilledCount,
|
||||
fifoSize,
|
||||
underrun,
|
||||
overrun,
|
||||
droppedPackets,
|
||||
sampleRate,
|
||||
linkRate,
|
||||
timestamp
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_success;
|
||||
// everything from lms_stream_status_t
|
||||
bool m_active; //!< Indicates whether the stream is currently active
|
||||
uint32_t m_fifoFilledCount; //!< Number of samples in FIFO buffer
|
||||
uint32_t m_fifoSize; //!< Size of FIFO buffer
|
||||
uint32_t m_underrun; //!< FIFO underrun count
|
||||
uint32_t m_overrun; //!< FIFO overrun count
|
||||
uint32_t m_droppedPackets; //!< Number of dropped packets by HW
|
||||
float m_sampleRate; //!< Sampling rate of the stream
|
||||
float m_linkRate; //!< Combined data rate of all stream of the same direction (TX or RX)
|
||||
uint64_t m_timestamp; //!< Current HW timestamp
|
||||
|
||||
MsgReportStreamInfo(
|
||||
bool success,
|
||||
bool active,
|
||||
uint32_t fifoFilledCount,
|
||||
uint32_t fifoSize,
|
||||
uint32_t underrun,
|
||||
uint32_t overrun,
|
||||
uint32_t droppedPackets,
|
||||
float sampleRate,
|
||||
float linkRate,
|
||||
uint64_t timestamp
|
||||
) :
|
||||
Message(),
|
||||
m_success(success),
|
||||
m_active(active),
|
||||
m_fifoFilledCount(fifoFilledCount),
|
||||
m_fifoSize(fifoSize),
|
||||
m_underrun(underrun),
|
||||
m_overrun(overrun),
|
||||
m_droppedPackets(droppedPackets),
|
||||
m_sampleRate(sampleRate),
|
||||
m_linkRate(linkRate),
|
||||
m_timestamp(timestamp)
|
||||
{ }
|
||||
};
|
||||
|
||||
LimeSDROutput(DeviceSinkAPI *deviceAPI);
|
||||
virtual ~LimeSDROutput();
|
||||
|
||||
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);
|
||||
|
||||
std::size_t getChannelIndex();
|
||||
void getLORange(float& minF, float& maxF, float& stepF) const;
|
||||
void getSRRange(float& minF, float& maxF, float& stepF) const;
|
||||
void getLPRange(float& minF, float& maxF, float& stepF) const;
|
||||
uint32_t getHWLog2Interp() const;
|
||||
|
||||
private:
|
||||
DeviceSinkAPI *m_deviceAPI;
|
||||
QMutex m_mutex;
|
||||
LimeSDROutputSettings m_settings;
|
||||
LimeSDROutputThread* m_limeSDROutputThread;
|
||||
QString m_deviceDescription;
|
||||
bool m_running;
|
||||
DeviceLimeSDRShared m_deviceShared;
|
||||
bool m_firstConfig;
|
||||
|
||||
lms_stream_t m_streamId;
|
||||
|
||||
bool openDevice();
|
||||
void closeDevice();
|
||||
bool applySettings(const LimeSDROutputSettings& settings, bool force);
|
||||
};
|
||||
|
||||
#endif /* PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUT_H_ */
|
825
plugins/samplesink/limesdroutput/limesdroutputgui.ui
Normal file
825
plugins/samplesink/limesdroutput/limesdroutputgui.ui
Normal file
@ -0,0 +1,825 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LimeSDROutputGUI</class>
|
||||
<widget class="QWidget" name="LimeSDROutputGUI">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>350</width>
|
||||
<height>290</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>290</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Sans Serif</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>LimeSDR Input</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_freq">
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="deviceUILayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="deviceButtonsLayout">
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="startStop">
|
||||
<property name="toolTip">
|
||||
<string>start/stop acquisition</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrbase/resources/res.qrc">
|
||||
<normaloff>:/play.png</normaloff>
|
||||
<normalon>:/stop.png</normalon>:/play.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="deviceRateLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="deviceRateLabel">
|
||||
<property name="toolTip">
|
||||
<string>I/Q sample rate kS/s</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>00000k</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="freqLeftSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ValueDial" name="centerFrequency" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>SizeVerCursor</cursorShape>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Main center frequency in kHz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="freqUnits">
|
||||
<property name="text">
|
||||
<string> kHz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="freqRightlSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="channelNumberText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Channel number</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>#0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="ncoEnable">
|
||||
<property name="toolTip">
|
||||
<string>Enable the TSP NCO</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>NCO</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ncoReset">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Reset the NCO to zero frequency</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>R</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ValueDial" name="ncoFrequency" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Center frequency with NCO engaged (kHz)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="ncoUnits">
|
||||
<property name="text">
|
||||
<string>kHz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_lna">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="ncoSampleRateLayout">
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="hwInterpLabel">
|
||||
<property name="text">
|
||||
<string>Hw</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="hwInterp">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>TSP hardware interpolation factor</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>8</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>16</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>32</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="swInterpLabel">
|
||||
<property name="text">
|
||||
<string>Sw</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="swInterp">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Software interpolation factor</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>8</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>16</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>32</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="samplerateLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>SR</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ValueDial" name="sampleRate" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Device to host sample rate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="samplerateUnit">
|
||||
<property name="text">
|
||||
<string>S/s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_freq">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="lpfLayout">
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="lpfLabel">
|
||||
<property name="text">
|
||||
<string>LP</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ValueDial" name="lpf" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Analog lowpass filer bandwidth (kHz)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lpfUnits">
|
||||
<property name="text">
|
||||
<string>kHz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="lpFIREnable">
|
||||
<property name="toolTip">
|
||||
<string>Enable or disable TSP digital FIR lowpass filters</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>FIR</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ValueDial" name="lpFIR" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Digital FIR lowpass filers bandwidth (kHz)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lpFIRUnits">
|
||||
<property name="text">
|
||||
<string>kHz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="gainLayout">
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="gainLabel">
|
||||
<property name="text">
|
||||
<string>Gain</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="gain">
|
||||
<property name="toolTip">
|
||||
<string>Global gain setting (dB)</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>70</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="gainText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Global gain (dB)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>20dB</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="statusLayout">
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="streamStatusLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Green when stream is reporting data</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../../sdrbase/resources/res.qrc">:/stream.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="underrunLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>12</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Red if underruns</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background:rgb(79,79,79);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>U</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="overrunLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>12</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Red if overruns</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background:rgb(79,79,79);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>O</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="droppedLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>12</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Red if dropped packets</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background:rgb(79,79,79);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>D</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="streamLinkRateText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>90</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Stream link rate (MB/s)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>000.000 MB/s</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="fifoBar">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>8</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>FIFO fill status</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QProgressBar{border: 2px solid rgb(79, 79, 79); text-align: center;}
|
||||
QToolTip{background-color: white; color: black;}</string>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_vga2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="padLayout">
|
||||
<item>
|
||||
<spacer name="verticalPadSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ValueDial</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/valuedial.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ButtonSwitch</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>gui/buttonswitch.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
130
plugins/samplesink/limesdroutput/limesdroutputplugin.cpp
Normal file
130
plugins/samplesink/limesdroutput/limesdroutputplugin.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QtPlugin>
|
||||
#include <QAction>
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "lime/LimeSuite.h"
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "device/devicesinkapi.h"
|
||||
|
||||
#include "limesdroutputgui.h"
|
||||
#include "limesdroutputplugin.h"
|
||||
|
||||
const PluginDescriptor LimeSDROutputPlugin::m_pluginDescriptor = {
|
||||
QString("LimeSDR Output"),
|
||||
QString("3.4.0"),
|
||||
QString("(c) Edouard Griffiths, F4EXB"),
|
||||
QString("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
QString("https://github.com/f4exb/sdrangel")
|
||||
};
|
||||
|
||||
const QString LimeSDROutputPlugin::m_hardwareID = "LimeSDR";
|
||||
const QString LimeSDROutputPlugin::m_deviceTypeID = LIMESDROUTPUT_DEVICE_TYPE_ID;
|
||||
|
||||
LimeSDROutputPlugin::LimeSDROutputPlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& LimeSDROutputPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void LimeSDROutputPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
pluginAPI->registerSampleSource(m_deviceTypeID, this);
|
||||
}
|
||||
|
||||
PluginInterface::SamplingDevices LimeSDROutputPlugin::enumSampleSinks()
|
||||
{
|
||||
lms_info_str_t* deviceList;
|
||||
int nbDevices;
|
||||
SamplingDevices result;
|
||||
|
||||
if ((nbDevices = LMS_GetDeviceList(0)) <= 0)
|
||||
{
|
||||
qDebug("LimeSDROutputPlugin::enumSampleSources: Could not find any LimeSDR device");
|
||||
return result; // empty result
|
||||
}
|
||||
|
||||
deviceList = new lms_info_str_t[nbDevices];
|
||||
|
||||
if (LMS_GetDeviceList(deviceList) < 0)
|
||||
{
|
||||
qDebug("LimeSDROutputPlugin::enumSampleSources: Could not obtain LimeSDR devices information");
|
||||
delete[] deviceList;
|
||||
return result; // empty result
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < nbDevices; i++)
|
||||
{
|
||||
std::string serial("N/D");
|
||||
findSerial((const char *) deviceList[i], serial);
|
||||
|
||||
qDebug("LimeSDROutputPlugin::enumSampleSources: device #%d: %s", i, (char *) deviceList[i]);
|
||||
QString displayedName(QString("LimeSDR[%1] %2").arg(i).arg(serial.c_str()));
|
||||
result.append(SamplingDevice(displayedName,
|
||||
m_hardwareID,
|
||||
m_deviceTypeID,
|
||||
QString(deviceList[i]),
|
||||
i));
|
||||
}
|
||||
}
|
||||
|
||||
delete[] deviceList;
|
||||
return result;
|
||||
}
|
||||
|
||||
PluginGUI* LimeSDROutputPlugin::createSampleSinkPluginGUI(const QString& sinkId,QWidget **widget, DeviceSinkAPI *deviceAPI)
|
||||
{
|
||||
if(sinkId == m_deviceTypeID)
|
||||
{
|
||||
LimeSDROutputGUI* gui = new LimeSDROutputGUI(deviceAPI);
|
||||
*widget = gui;
|
||||
return gui;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool LimeSDROutputPlugin::findSerial(const char *lmsInfoStr, std::string& serial)
|
||||
{
|
||||
std::regex serial_reg("serial=([0-9,A-F]+)");
|
||||
std::string input(lmsInfoStr);
|
||||
std::smatch result;
|
||||
std::regex_search(input, result, serial_reg);
|
||||
|
||||
if (result[1].str().length()>0)
|
||||
{
|
||||
serial = result[1].str();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
50
plugins/samplesink/limesdroutput/limesdroutputplugin.h
Normal file
50
plugins/samplesink/limesdroutput/limesdroutputplugin.h
Normal file
@ -0,0 +1,50 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUTPLUGIN_H_
|
||||
#define PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUTPLUGIN_H_
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class PluginAPI;
|
||||
|
||||
#define LIMESDROUTPUT_DEVICE_TYPE_ID "sdrangel.samplesink.limesdr"
|
||||
|
||||
class LimeSDROutputPlugin : public QObject, public PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID LIMESDROUTPUT_DEVICE_TYPE_ID)
|
||||
|
||||
public:
|
||||
explicit LimeSDROutputPlugin(QObject* parent = 0);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
virtual SamplingDevices enumSampleSinks();
|
||||
virtual PluginGUI* createSampleSinkPluginGUI(const QString& sinkId, QWidget **widget, DeviceSinkAPI *deviceAPI);
|
||||
|
||||
static const QString m_hardwareID;
|
||||
static const QString m_deviceTypeID;
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
static bool findSerial(const char *lmsInfoStr, std::string& serial);
|
||||
};
|
||||
|
||||
|
||||
#endif /* PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUTPLUGIN_H_ */
|
91
plugins/samplesink/limesdroutput/limesdroutputsettings.cpp
Normal file
91
plugins/samplesink/limesdroutput/limesdroutputsettings.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "util/simpleserializer.h"
|
||||
|
||||
#include "limesdroutputsettings.h"
|
||||
|
||||
LimeSDROutputSettings::LimeSDROutputSettings()
|
||||
{
|
||||
resetToDefaults();
|
||||
}
|
||||
|
||||
void LimeSDROutputSettings::resetToDefaults()
|
||||
{
|
||||
m_centerFrequency = 435000*1000;
|
||||
m_devSampleRate = 5000000;
|
||||
m_log2HardInterp = 3;
|
||||
m_log2SoftInterp = 0;
|
||||
m_lpfBW = 4.5e6f;
|
||||
m_lpfFIREnable = false;
|
||||
m_lpfFIRBW = 2.5e6f;
|
||||
m_gain = 30;
|
||||
m_ncoEnable = false;
|
||||
m_ncoFrequency = 0;
|
||||
}
|
||||
|
||||
QByteArray LimeSDROutputSettings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeS32(1, m_devSampleRate);
|
||||
s.writeU32(2, m_log2HardInterp);
|
||||
s.writeU32(5, m_log2SoftInterp);
|
||||
s.writeFloat(7, m_lpfBW);
|
||||
s.writeBool(8, m_lpfFIREnable);
|
||||
s.writeFloat(9, m_lpfFIRBW);
|
||||
s.writeU32(10, m_gain);
|
||||
s.writeBool(11, m_ncoEnable);
|
||||
s.writeS32(12, m_ncoFrequency);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool LimeSDROutputSettings::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if (!d.isValid())
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d.getVersion() == 1)
|
||||
{
|
||||
int intval;
|
||||
|
||||
d.readS32(1, &m_devSampleRate, 5000000);
|
||||
d.readU32(2, &m_log2HardInterp, 2);
|
||||
d.readU32(5, &m_log2SoftInterp, 0);
|
||||
d.readFloat(7, &m_lpfBW, 1.5e6);
|
||||
d.readBool(8, &m_lpfFIREnable, false);
|
||||
d.readFloat(9, &m_lpfFIRBW, 1.5e6);
|
||||
d.readU32(10, &m_gain, 0);
|
||||
d.readBool(11, &m_ncoEnable, false);
|
||||
d.readS32(12, &m_ncoFrequency, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
54
plugins/samplesink/limesdroutput/limesdroutputsettings.h
Normal file
54
plugins/samplesink/limesdroutput/limesdroutputsettings.h
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUTSETTINGS_H_
|
||||
#define PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUTSETTINGS_H_
|
||||
|
||||
#include <QByteArray>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* These are the settings individual to each hardware channel or software Tx chain
|
||||
* Plus the settings to be saved in the presets
|
||||
*/
|
||||
struct LimeSDROutputSettings
|
||||
{
|
||||
typedef enum {
|
||||
FC_POS_INFRA = 0,
|
||||
FC_POS_SUPRA,
|
||||
FC_POS_CENTER
|
||||
} fcPos_t;
|
||||
|
||||
// global settings to be saved
|
||||
uint64_t m_centerFrequency;
|
||||
int m_devSampleRate;
|
||||
uint32_t m_log2HardInterp;
|
||||
// channel settings
|
||||
uint32_t m_log2SoftInterp;
|
||||
float m_lpfBW; //!< LMS amalog lowpass filter bandwidth (Hz)
|
||||
bool m_lpfFIREnable; //!< Enable LMS digital lowpass FIR filters
|
||||
float m_lpfFIRBW; //!< LMS digital lowpass FIR filters bandwidth (Hz)
|
||||
uint32_t m_gain; //!< Optimally distributed gain (dB)
|
||||
bool m_ncoEnable; //!< Enable TSP NCO and mixing
|
||||
int m_ncoFrequency; //!< Actual NCO frequency (the resulting frequency with mixing is displayed)
|
||||
|
||||
LimeSDROutputSettings();
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
};
|
||||
|
||||
#endif /* PLUGINS_SAMPLESOURCE_LIMESDRINPUT_LIMESDRINPUTSETTINGS_H_ */
|
142
plugins/samplesink/limesdroutput/limesdroutputthread.cpp
Normal file
142
plugins/samplesink/limesdroutput/limesdroutputthread.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "limesdroutputthread.h"
|
||||
#include "limesdroutputsettings.h"
|
||||
|
||||
LimeSDROutputThread::LimeSDROutputThread(lms_stream_t* stream, SampleSourceFifo* sampleFifo, QObject* parent) :
|
||||
QThread(parent),
|
||||
m_running(false),
|
||||
m_stream(stream),
|
||||
m_sampleFifo(sampleFifo),
|
||||
m_log2Interp(0),
|
||||
m_fcPos(LimeSDROutputSettings::FC_POS_CENTER)
|
||||
{
|
||||
m_sampleFifo->resize(16*LIMESDROUTPUT_BLOCKSIZE);
|
||||
}
|
||||
|
||||
LimeSDROutputThread::~LimeSDROutputThread()
|
||||
{
|
||||
stopWork();
|
||||
}
|
||||
|
||||
void LimeSDROutputThread::startWork()
|
||||
{
|
||||
if (m_running) return; // return if running already
|
||||
|
||||
m_startWaitMutex.lock();
|
||||
start();
|
||||
while(!m_running)
|
||||
m_startWaiter.wait(&m_startWaitMutex, 100);
|
||||
m_startWaitMutex.unlock();
|
||||
}
|
||||
|
||||
void LimeSDROutputThread::stopWork()
|
||||
{
|
||||
if (!m_running) return; // return if not running
|
||||
|
||||
m_running = false;
|
||||
wait();
|
||||
}
|
||||
|
||||
void LimeSDROutputThread::setLog2Interpolation(unsigned int log2_interp)
|
||||
{
|
||||
m_log2Interp = log2_interp;
|
||||
}
|
||||
|
||||
void LimeSDROutputThread::setFcPos(int fcPos)
|
||||
{
|
||||
m_fcPos = fcPos;
|
||||
}
|
||||
|
||||
void LimeSDROutputThread::run()
|
||||
{
|
||||
int res;
|
||||
|
||||
lms_stream_meta_t metadata; //Use metadata for additional control over sample receive function behaviour
|
||||
metadata.flushPartialPacket = false; //Do not discard data remainder when read size differs from packet size
|
||||
metadata.waitForTimestamp = false; //Do not wait for specific timestamps
|
||||
|
||||
m_running = true;
|
||||
m_startWaiter.wakeAll();
|
||||
|
||||
if (LMS_StartStream(m_stream) < 0) {
|
||||
qCritical("LimeSDROutputThread::run: could not start stream");
|
||||
} else {
|
||||
qDebug("LimeSDROutputThread::run: stream started");
|
||||
}
|
||||
|
||||
while (m_running)
|
||||
{
|
||||
callback(m_buf, 2 * res);
|
||||
|
||||
if ((res = LMS_SendStream(m_stream, (void *) m_buf, LIMESDR_BLOCKSIZE, &metadata, 1000)) < 0)
|
||||
{
|
||||
qCritical("LimeSDROutputThread::run write error: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (LMS_StopStream(m_stream) < 0) {
|
||||
qCritical("LimeSDROutputThread::run: could not stop stream");
|
||||
} else {
|
||||
qDebug("LimeSDROutputThread::run: stream stopped");
|
||||
}
|
||||
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
// Interpolate according to specified log2 (ex: log2=4 => decim=16)
|
||||
void LimeSDROutputThread::callback(const qint16* buf, qint32 len)
|
||||
{
|
||||
SampleVector::iterator beginRead;
|
||||
m_sampleFifo->readAdvance(beginRead, len/(1<<m_log2Interp));
|
||||
beginRead -= len;
|
||||
|
||||
if (m_log2Interp == 0)
|
||||
{
|
||||
m_interpolators.interpolate1(&beginRead, buf, len*2);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (m_log2Interp)
|
||||
{
|
||||
case 1:
|
||||
m_interpolators.interpolate2_cen(&beginRead, buf, len*2);
|
||||
break;
|
||||
case 2:
|
||||
m_interpolators.interpolate4_cen(&beginRead, buf, len*2);
|
||||
break;
|
||||
case 3:
|
||||
m_interpolators.interpolate8_cen(&beginRead, buf, len*2);
|
||||
break;
|
||||
case 4:
|
||||
m_interpolators.interpolate16_cen(&beginRead, buf, len*2);
|
||||
break;
|
||||
case 5:
|
||||
m_interpolators.interpolate32_cen(&beginRead, buf, len*2);
|
||||
break;
|
||||
case 6:
|
||||
m_interpolators.interpolate64_cen(&beginRead, buf, len*2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
64
plugins/samplesink/limesdroutput/limesdroutputthread.h
Normal file
64
plugins/samplesink/limesdroutput/limesdroutputthread.h
Normal file
@ -0,0 +1,64 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUTTHREAD_H_
|
||||
#define PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUTTHREAD_H_
|
||||
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include "lime/LimeSuite.h"
|
||||
|
||||
#include "dsp/samplesourcefifo.h"
|
||||
#include "dsp/interpolators.h"
|
||||
|
||||
#define LIMESDROUTPUT_BLOCKSIZE (1<<14) //complex samples per buffer ~10k (16k)
|
||||
|
||||
class LimeSDROutputThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LimeSDROutputThread(lms_stream_t* stream, SampleSourceFifo* sampleFifo, QObject* parent = 0);
|
||||
~LimeSDROutputThread();
|
||||
|
||||
void startWork();
|
||||
void stopWork();
|
||||
void setLog2Interpolation(unsigned int log2_ioterp);
|
||||
void setFcPos(int fcPos);
|
||||
|
||||
private:
|
||||
QMutex m_startWaitMutex;
|
||||
QWaitCondition m_startWaiter;
|
||||
bool m_running;
|
||||
|
||||
lms_stream_t* m_stream;
|
||||
qint16 m_buf[2*LIMESDROUTPUT_BLOCKSIZE]; //must hold I+Q values of each sample hence 2xcomplex size
|
||||
SampleSourceFifo* m_sampleFifo;
|
||||
|
||||
unsigned int m_log2Interp; // soft decimation
|
||||
int m_fcPos;
|
||||
|
||||
Interpolators<qint16, SDR_SAMP_SZ, 12> m_interpolators;
|
||||
|
||||
void run();
|
||||
void callback(const qint16* buf, qint32 len);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUTTHREAD_H_ */
|
Loading…
Reference in New Issue
Block a user