mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-05-23 18:52:28 -04:00
XTRX: make clock gen setting consistent
This commit is contained in:
parent
e3069d3a69
commit
733f3d4ba4
@ -205,7 +205,7 @@ If you use your own location for libmirisdr-4 install directory you need to spec
|
||||
|
||||
<h2>XTRX</h2>
|
||||
|
||||
Experimental. Compile from source.
|
||||
Experimental and Linux only. Compile from source.
|
||||
|
||||
[XTRX](https://xtrx.io) is supported through the set of [xtrx libraries](https://github.com/xtrx-sdr/images).
|
||||
|
||||
|
@ -36,7 +36,11 @@ const uint32_t DeviceXTRX::m_pgaTbl[m_nbGains] = {
|
||||
};
|
||||
|
||||
DeviceXTRX::DeviceXTRX() :
|
||||
m_dev(0)
|
||||
m_dev(0),
|
||||
m_inputRate(0),
|
||||
m_outputRate(0),
|
||||
m_masterRate(0),
|
||||
m_clockGen(0)
|
||||
{}
|
||||
|
||||
DeviceXTRX::~DeviceXTRX()
|
||||
@ -84,3 +88,56 @@ void DeviceXTRX::getAutoGains(uint32_t autoGain, uint32_t& lnaGain, uint32_t& ti
|
||||
lnaGain = m_lnaTbl[value];
|
||||
pgaGain = m_pgaTbl[value];
|
||||
}
|
||||
|
||||
double DeviceXTRX::set_samplerate(double rate, double master, bool output)
|
||||
{
|
||||
if (output)
|
||||
{
|
||||
m_outputRate = rate;
|
||||
|
||||
if (master != 0.0) {
|
||||
m_masterRate = master;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_inputRate = rate;
|
||||
|
||||
if (master != 0.0) {
|
||||
m_masterRate = master;
|
||||
}
|
||||
}
|
||||
|
||||
int res = xtrx_set_samplerate(m_dev,
|
||||
m_masterRate,
|
||||
m_inputRate,
|
||||
m_outputRate,
|
||||
0,
|
||||
&m_clockGen,
|
||||
&m_actualInputRate,
|
||||
&m_actualOutputRate);
|
||||
|
||||
if (res)
|
||||
{
|
||||
qCritical("DeviceXTRX::set_samplerate: Unable to set %s samplerate, m_masterRate: %f, m_inputRate: %f, m_outputRate: %f, error=%d\n",
|
||||
output ? "output" : "input", m_masterRate, m_inputRate, m_outputRate, res);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "DeviceXTRXShared::set_samplerate: sample rate set: "
|
||||
<< "output: "<< output
|
||||
<< "m_masterRate: " << m_masterRate
|
||||
<< "m_inputRate: " << m_inputRate
|
||||
<< "m_outputRate: " << m_outputRate
|
||||
<< "m_clockGen: " << m_clockGen
|
||||
<< "m_actualInputRate: " << m_actualInputRate
|
||||
<< "m_actualOutputRate: " << m_actualOutputRate;
|
||||
}
|
||||
|
||||
if (output) {
|
||||
return m_outputRate;
|
||||
}
|
||||
|
||||
return m_inputRate;
|
||||
}
|
@ -32,6 +32,11 @@ public:
|
||||
bool open(const char* deviceStr);
|
||||
void close();
|
||||
struct xtrx_dev *getDevice() { return m_dev; }
|
||||
double set_samplerate(double rate, double master, bool output);
|
||||
double getMasterRate() const { return m_masterRate; }
|
||||
double getClockGen() const { return m_clockGen; }
|
||||
double getActualInputRate() const { return m_actualInputRate; }
|
||||
double getActualOutputRate() const { return m_actualOutputRate; }
|
||||
static void getAutoGains(uint32_t autoGain, uint32_t& lnaGain, uint32_t& tiaGain, uint32_t& pgaGain);
|
||||
|
||||
static const uint32_t m_nbGains = 74;
|
||||
@ -39,6 +44,12 @@ public:
|
||||
|
||||
private:
|
||||
struct xtrx_dev *m_dev; //!< device handle
|
||||
double m_inputRate;
|
||||
double m_outputRate;
|
||||
double m_masterRate;
|
||||
double m_clockGen;
|
||||
double m_actualInputRate;
|
||||
double m_actualOutputRate;
|
||||
|
||||
static const uint32_t m_lnaTbl[m_nbGains];
|
||||
static const uint32_t m_pgaTbl[m_nbGains];
|
||||
|
@ -31,9 +31,7 @@ DeviceXTRXShared::DeviceXTRXShared() :
|
||||
m_channel(-1),
|
||||
m_source(0),
|
||||
m_sink(0),
|
||||
m_inputRate(0),
|
||||
m_outputRate(0),
|
||||
m_masterRate(0),
|
||||
|
||||
m_thread(0),
|
||||
m_threadWasRunning(false)
|
||||
{}
|
||||
@ -41,63 +39,6 @@ DeviceXTRXShared::DeviceXTRXShared() :
|
||||
DeviceXTRXShared::~DeviceXTRXShared()
|
||||
{}
|
||||
|
||||
double DeviceXTRXShared::set_samplerate(double rate, double master, bool output)
|
||||
{
|
||||
if (output)
|
||||
{
|
||||
m_outputRate = rate;
|
||||
|
||||
if (master != 0.0) {
|
||||
m_masterRate = master;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_inputRate = rate;
|
||||
|
||||
if (master != 0.0) {
|
||||
m_masterRate = master;
|
||||
}
|
||||
}
|
||||
|
||||
double actualcgen;
|
||||
double actualrx;
|
||||
double actualtx;
|
||||
|
||||
int res = xtrx_set_samplerate(m_dev->getDevice(),
|
||||
m_masterRate,
|
||||
m_inputRate,
|
||||
m_outputRate,
|
||||
0,
|
||||
&actualcgen,
|
||||
&actualrx,
|
||||
&actualtx);
|
||||
|
||||
if (res)
|
||||
{
|
||||
qCritical("DeviceXTRXShared::set_samplerate: Unable to set %s samplerate, m_masterRate: %f, m_inputRate: %f, m_outputRate: %f, error=%d\n",
|
||||
output ? "output" : "input", m_masterRate, m_inputRate, m_outputRate, res);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "DeviceXTRXShared::set_samplerate: sample rate set: "
|
||||
<< "output: "<< output
|
||||
<< "m_masterRate: " << m_masterRate
|
||||
<< "m_inputRate: " << m_inputRate
|
||||
<< "m_outputRate: " << m_outputRate
|
||||
<< "actualcgen: " << actualcgen
|
||||
<< "actualrx: " << actualrx
|
||||
<< "actualtx: " << actualtx;
|
||||
}
|
||||
|
||||
if (output) {
|
||||
return m_outputRate;
|
||||
}
|
||||
|
||||
return m_inputRate;
|
||||
}
|
||||
|
||||
double DeviceXTRXShared::get_board_temperature()
|
||||
{
|
||||
uint64_t val = 0;
|
||||
|
@ -136,9 +136,7 @@ public:
|
||||
int m_channel; //!< allocated channel (-1 if none)
|
||||
XTRXInput *m_source;
|
||||
XTRXOutput *m_sink;
|
||||
double m_inputRate;
|
||||
double m_outputRate;
|
||||
double m_masterRate;
|
||||
|
||||
ThreadInterface *m_thread; //!< holds the thread address if started else 0
|
||||
bool m_threadWasRunning; //!< flag to know if thread needs to be resumed after suspend
|
||||
|
||||
@ -148,7 +146,6 @@ public:
|
||||
DeviceXTRXShared();
|
||||
~DeviceXTRXShared();
|
||||
|
||||
double set_samplerate(double rate, double master, bool output);
|
||||
double get_board_temperature();
|
||||
bool get_gps_status();
|
||||
};
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
⚠ Support is experimental and for Linux only. You have to compile it from source.
|
||||
|
||||
This output sample sink plugin sends its samples to a [XTRX device](https://xtrx.io).
|
||||
|
||||
XTRX is a 2x2 MIMO device so it has two transmitting channels that can run concurrently. To activate the second channel when the first is already active just open a new sink tab in the main window (Devices -> Add sink device) and select the same LimeSDR device.
|
||||
@ -89,6 +91,8 @@ The TSP block in the LMS7002M hardware has an interpolation chain that acts on b
|
||||
|
||||
Thus the actual sample rate of the DAC is the stream sample rate (11) multiplied by this factor. In the screenshot example this yields a 12.288 MS/s rate at the DAC (3.072 * 4).
|
||||
|
||||
The first position in the combo is marked as "A". This is because interpolation by 1 is not implemented and instead an automatic interpolation factor is applied. The DAC rate display is updated automatically and siblings are updated with the actual DAC and hardware interpolation factor.
|
||||
|
||||
<h3>10: Software interpolation factor</h3>
|
||||
|
||||
The I/Q stream from the baseband is upsampled by a power of two by software inside the plugin before being sent to the LimeSDR device. Possible values are increasing powers of two: 1 (no interpolation), 2, 4, 8, 16, 32.
|
||||
|
@ -41,6 +41,7 @@
|
||||
MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgConfigureXTRX, Message)
|
||||
MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgGetStreamInfo, Message)
|
||||
MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgGetDeviceInfo, Message)
|
||||
MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgReportClockGenChange, Message)
|
||||
MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgReportStreamInfo, Message)
|
||||
MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgStartStop, Message)
|
||||
|
||||
@ -492,9 +493,45 @@ const QString& XTRXOutput::getDeviceDescription() const
|
||||
int XTRXOutput::getSampleRate() const
|
||||
{
|
||||
double rate = m_settings.m_devSampleRate;
|
||||
|
||||
if (m_deviceShared.m_dev) {
|
||||
rate = m_deviceShared.m_dev->getActualOutputRate();
|
||||
}
|
||||
|
||||
return (int)((rate / (1<<m_settings.m_log2SoftInterp)));
|
||||
}
|
||||
|
||||
uint32_t XTRXOutput::getDevSampleRate() const
|
||||
{
|
||||
uint32_t devSampleRate = m_settings.m_devSampleRate;
|
||||
|
||||
if (m_deviceShared.m_dev) {
|
||||
devSampleRate = m_deviceShared.m_dev->getActualOutputRate();
|
||||
}
|
||||
|
||||
return devSampleRate;
|
||||
}
|
||||
|
||||
uint32_t XTRXOutput::getLog2HardInterp() const
|
||||
{
|
||||
uint32_t log2HardInterp = m_settings.m_log2HardInterp;
|
||||
|
||||
if (m_deviceShared.m_dev && (m_deviceShared.m_dev->getActualOutputRate() != 0.0)) {
|
||||
log2HardInterp = log2(m_deviceShared.m_dev->getClockGen() / m_deviceShared.m_dev->getActualOutputRate() / 4);
|
||||
}
|
||||
|
||||
return log2HardInterp;
|
||||
}
|
||||
|
||||
double XTRXOutput::getClockGen() const
|
||||
{
|
||||
if (m_deviceShared.m_dev) {
|
||||
return m_deviceShared.m_dev->getClockGen();
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
quint64 XTRXOutput::getCenterFrequency() const
|
||||
{
|
||||
return m_settings.m_centerFrequency + (m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0);
|
||||
@ -573,13 +610,13 @@ bool XTRXOutput::handleMessage(const Message& message)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings.m_devSampleRate = m_deviceShared.m_inputRate;
|
||||
m_settings.m_log2HardInterp = log2(m_deviceShared.m_masterRate / m_deviceShared.m_inputRate / 4);
|
||||
m_settings.m_devSampleRate = m_deviceShared.m_dev->getActualOutputRate();
|
||||
m_settings.m_log2HardInterp = getLog2HardInterp();
|
||||
|
||||
qDebug() << "XTRXOutput::handleMessage: MsgReportBuddyChange:"
|
||||
<< " host_Hz: " << m_deviceShared.m_inputRate
|
||||
<< " rf_Hz: " << m_deviceShared.m_masterRate / 4
|
||||
<< " m_log2HardDecim: " << m_settings.m_log2HardInterp;
|
||||
<< " host_Hz: " << m_deviceShared.m_dev->getActualOutputRate()
|
||||
<< " dac_Hz: " << m_deviceShared.m_dev->getClockGen() / 4
|
||||
<< " m_log2HardInterp: " << m_settings.m_log2HardInterp;
|
||||
}
|
||||
|
||||
if (m_settings.m_ncoEnable) // need to reset NCO after sample rate change
|
||||
@ -917,9 +954,9 @@ bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, b
|
||||
|
||||
double master = (settings.m_log2HardInterp == 0) ? 0 : (settings.m_devSampleRate * 4 * (1 << settings.m_log2HardInterp));
|
||||
|
||||
if (m_deviceShared.set_samplerate(settings.m_devSampleRate,
|
||||
master, //(settings.m_devSampleRate<<settings.m_log2HardDecim)*4,
|
||||
true) < 0)
|
||||
if (m_deviceShared.m_dev->set_samplerate(settings.m_devSampleRate,
|
||||
master, //(settings.m_devSampleRate<<settings.m_log2HardDecim)*4,
|
||||
true) < 0)
|
||||
{
|
||||
qCritical("XTRXOutput::applySettings: could not set sample rate to %f with oversampling of %d",
|
||||
settings.m_devSampleRate,
|
||||
@ -929,10 +966,11 @@ bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, b
|
||||
{
|
||||
doChangeFreq = true;
|
||||
forceNCOFrequency = true;
|
||||
forwardChangeAllDSP = true;
|
||||
|
||||
qDebug("XTRXOutput::applySettings: sample rate set to %f with oversampling of %d",
|
||||
settings.m_devSampleRate,
|
||||
1<<settings.m_log2HardInterp);
|
||||
m_deviceShared.m_dev->getActualOutputRate(),
|
||||
1 << getLog2HardInterp());
|
||||
}
|
||||
|
||||
resumeRxThread();
|
||||
@ -1005,11 +1043,15 @@ bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, b
|
||||
int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
|
||||
|
||||
// send to self first
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(
|
||||
m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp),
|
||||
m_settings.m_centerFrequency + ncoShift);
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(getSampleRate(), m_settings.m_centerFrequency + ncoShift);
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
|
||||
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportClockGenChange *report = MsgReportClockGenChange::create();
|
||||
getMessageQueueToGUI()->push(report);
|
||||
}
|
||||
|
||||
// send to source buddies
|
||||
const std::vector<DeviceSourceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
|
||||
std::vector<DeviceSourceAPI*>::const_iterator itSource = sourceBuddies.begin();
|
||||
@ -1017,7 +1059,7 @@ bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, b
|
||||
for (; itSource != sourceBuddies.end(); ++itSource)
|
||||
{
|
||||
DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create(
|
||||
m_settings.m_devSampleRate, m_settings.m_log2HardInterp, m_settings.m_centerFrequency, true);
|
||||
getDevSampleRate(), getLog2HardInterp(), m_settings.m_centerFrequency, true);
|
||||
(*itSource)->getSampleSourceInputMessageQueue()->push(report);
|
||||
}
|
||||
|
||||
@ -1028,7 +1070,7 @@ bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, b
|
||||
for (; itSink != sinkBuddies.end(); ++itSink)
|
||||
{
|
||||
DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create(
|
||||
m_settings.m_devSampleRate, m_settings.m_log2HardInterp, m_settings.m_centerFrequency, true);
|
||||
getDevSampleRate(), getLog2HardInterp(), m_settings.m_centerFrequency, true);
|
||||
(*itSink)->getSampleSinkInputMessageQueue()->push(report);
|
||||
}
|
||||
}
|
||||
@ -1036,13 +1078,18 @@ bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, b
|
||||
{
|
||||
qDebug("XTRXOutput::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);
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(getSampleRate(), m_settings.m_centerFrequency + ncoShift);
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
|
||||
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportClockGenChange *report = MsgReportClockGenChange::create();
|
||||
getMessageQueueToGUI()->push(report);
|
||||
}
|
||||
|
||||
// send to sink buddies
|
||||
const std::vector<DeviceSinkAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
|
||||
std::vector<DeviceSinkAPI*>::const_iterator itSink = sinkBuddies.begin();
|
||||
@ -1050,7 +1097,7 @@ bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, b
|
||||
for (; itSink != sinkBuddies.end(); ++itSink)
|
||||
{
|
||||
DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create(
|
||||
m_settings.m_devSampleRate, m_settings.m_log2HardInterp, m_settings.m_centerFrequency, true);
|
||||
getDevSampleRate(), getLog2HardInterp(), m_settings.m_centerFrequency, true);
|
||||
(*itSink)->getSampleSinkInputMessageQueue()->push(report);
|
||||
}
|
||||
}
|
||||
@ -1058,10 +1105,16 @@ bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, b
|
||||
{
|
||||
qDebug("XTRXOutput::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);
|
||||
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(getSampleRate(), m_settings.m_centerFrequency + ncoShift);
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
|
||||
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportClockGenChange *report = MsgReportClockGenChange::create();
|
||||
getMessageQueueToGUI()->push(report);
|
||||
}
|
||||
}
|
||||
|
||||
if (forwardClockSource)
|
||||
@ -1090,8 +1143,10 @@ bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, b
|
||||
}
|
||||
|
||||
qDebug() << "XTRXOutput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz"
|
||||
<< " device stream sample rate: " << m_settings.m_devSampleRate << "S/s"
|
||||
<< " sample rate with soft interpolation: " << m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp) << "S/s"
|
||||
<< " device stream sample rate: " << getDevSampleRate() << "S/s"
|
||||
<< " sample rate with soft interpolation: " << getSampleRate() << "S/s"
|
||||
<< " m_devSampleRate: " << m_settings.m_devSampleRate
|
||||
<< " m_log2SoftInterp: " << m_settings.m_log2SoftInterp
|
||||
<< " m_gain: " << m_settings.m_gain
|
||||
<< " m_lpfBW: " << m_settings.m_lpfBW
|
||||
<< " m_ncoEnable: " << m_settings.m_ncoEnable
|
||||
|
@ -90,6 +90,21 @@ public:
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportClockGenChange : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
static MsgReportClockGenChange* create()
|
||||
{
|
||||
return new MsgReportClockGenChange();
|
||||
}
|
||||
|
||||
private:
|
||||
MsgReportClockGenChange() :
|
||||
Message()
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportStreamInfo : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
@ -170,6 +185,9 @@ public:
|
||||
virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; }
|
||||
virtual const QString& getDeviceDescription() const;
|
||||
virtual int getSampleRate() const;
|
||||
uint32_t getDevSampleRate() const;
|
||||
uint32_t getLog2HardInterp() const;
|
||||
double getClockGen() const;
|
||||
virtual quint64 getCenterFrequency() const;
|
||||
virtual void setCenterFrequency(qint64 centerFrequency);
|
||||
|
||||
|
@ -168,6 +168,16 @@ bool XTRXOutputGUI::handleMessage(const Message& message)
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (XTRXOutput::MsgReportClockGenChange::match(message))
|
||||
{
|
||||
m_settings.m_devSampleRate = m_XTRXOutput->getDevSampleRate();
|
||||
|
||||
blockApplySettings(true);
|
||||
displaySettings();
|
||||
blockApplySettings(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (XTRXOutput::MsgReportStreamInfo::match(message))
|
||||
{
|
||||
XTRXOutput::MsgReportStreamInfo& report = (XTRXOutput::MsgReportStreamInfo&) message;
|
||||
@ -243,7 +253,7 @@ void XTRXOutputGUI::handleInputMessages()
|
||||
|
||||
void XTRXOutputGUI::updateDACRate()
|
||||
{
|
||||
uint32_t dacRate = m_settings.m_devSampleRate * (1<<m_settings.m_log2HardInterp);
|
||||
uint32_t dacRate = m_XTRXOutput->getClockGen() / 4;
|
||||
|
||||
if (dacRate < 100000000) {
|
||||
ui->dacRateLabel->setText(tr("%1k").arg(QString::number(dacRate / 1000.0f, 'g', 5)));
|
||||
|
@ -328,7 +328,7 @@
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
<string>A</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
⚠ Support is experimental and for Linux only. You have to compile it from source.
|
||||
|
||||
This input sample source plugin gets its samples from a [XTRX device](https://xtrx.io).
|
||||
|
||||
XTRX is a 2x2 MIMO device so it has two receiving channels that can run concurrently. To activate the second channel when the first is already active just open a new source tab in the main window (Devices -> Add source device) and select the same LimeSDR device. You may need to change frequency back and forth in case reception is not working properly.
|
||||
@ -108,6 +110,8 @@ The TSP block in the LMS7002M hardware has a decimation chain that acts on both
|
||||
|
||||
Thus the actual sample rate of the ADC is the stream sample rate (5) multiplied by this factor.
|
||||
|
||||
The first position in the combo is marked as "A". This is because decimation by 1 is not implemented and instead an automatic decimation factor is applied. The ADC rate display is updated automatically and siblings are updated with the actual ADC and hardware decimation factor.
|
||||
|
||||
<h3>4: Software decimation factor</h3>
|
||||
|
||||
The I/Q stream from the XTRX is downsampled by a power of two by software inside the plugin before being sent to the passband. Possible values are increasing powers of two: 1 (no decimation), 2, 4, 8, 16, 32.
|
||||
|
@ -42,6 +42,7 @@
|
||||
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgConfigureXTRX, Message)
|
||||
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgGetStreamInfo, Message)
|
||||
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgGetDeviceInfo, Message)
|
||||
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgReportClockGenChange, Message)
|
||||
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgReportStreamInfo, Message)
|
||||
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgFileRecord, Message)
|
||||
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgStartStop, Message)
|
||||
@ -507,9 +508,45 @@ const QString& XTRXInput::getDeviceDescription() const
|
||||
int XTRXInput::getSampleRate() const
|
||||
{
|
||||
double rate = m_settings.m_devSampleRate;
|
||||
|
||||
if (m_deviceShared.m_dev) {
|
||||
rate = m_deviceShared.m_dev->getActualInputRate();
|
||||
}
|
||||
|
||||
return (int)((rate / (1<<m_settings.m_log2SoftDecim)));
|
||||
}
|
||||
|
||||
uint32_t XTRXInput::getDevSampleRate() const
|
||||
{
|
||||
uint32_t devSampleRate = m_settings.m_devSampleRate;
|
||||
|
||||
if (m_deviceShared.m_dev) {
|
||||
devSampleRate = m_deviceShared.m_dev->getActualInputRate();
|
||||
}
|
||||
|
||||
return devSampleRate;
|
||||
}
|
||||
|
||||
uint32_t XTRXInput::getLog2HardDecim() const
|
||||
{
|
||||
uint32_t log2HardDecim = m_settings.m_log2HardDecim;
|
||||
|
||||
if (m_deviceShared.m_dev && (m_deviceShared.m_dev->getActualInputRate() != 0.0)) {
|
||||
log2HardDecim = log2(m_deviceShared.m_dev->getClockGen() / m_deviceShared.m_dev->getActualInputRate() / 4);
|
||||
}
|
||||
|
||||
return log2HardDecim;
|
||||
}
|
||||
|
||||
double XTRXInput::getClockGen() const
|
||||
{
|
||||
if (m_deviceShared.m_dev) {
|
||||
return m_deviceShared.m_dev->getClockGen();
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
quint64 XTRXInput::getCenterFrequency() const
|
||||
{
|
||||
return m_settings.m_centerFrequency + (m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0);
|
||||
@ -588,12 +625,12 @@ bool XTRXInput::handleMessage(const Message& message)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings.m_devSampleRate = m_deviceShared.m_inputRate;
|
||||
m_settings.m_log2HardDecim = log2(m_deviceShared.m_masterRate / m_deviceShared.m_inputRate / 4);
|
||||
m_settings.m_devSampleRate = m_deviceShared.m_dev->getActualInputRate();
|
||||
m_settings.m_log2HardDecim = getLog2HardDecim();
|
||||
|
||||
qDebug() << "XTRXInput::handleMessage: MsgReportBuddyChange:"
|
||||
<< " host_Hz: " << m_deviceShared.m_inputRate
|
||||
<< " rf_Hz: " << m_deviceShared.m_masterRate / 4
|
||||
<< " host_Hz: " << m_deviceShared.m_dev->getActualInputRate()
|
||||
<< " adc_Hz: " << m_deviceShared.m_dev->getClockGen() / 4
|
||||
<< " m_log2HardDecim: " << m_settings.m_log2HardDecim;
|
||||
}
|
||||
|
||||
@ -1057,9 +1094,9 @@ bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, boo
|
||||
|
||||
double master = (settings.m_log2HardDecim == 0) ? 0 : (settings.m_devSampleRate * 4 * (1 << settings.m_log2HardDecim));
|
||||
|
||||
if (m_deviceShared.set_samplerate(settings.m_devSampleRate,
|
||||
master, //(settings.m_devSampleRate<<settings.m_log2HardDecim)*4,
|
||||
false) < 0)
|
||||
if (m_deviceShared.m_dev->set_samplerate(settings.m_devSampleRate,
|
||||
master, //(settings.m_devSampleRate<<settings.m_log2HardDecim)*4,
|
||||
false) < 0)
|
||||
{
|
||||
qCritical("XTRXInput::applySettings: could not set sample rate to %f with oversampling of %d",
|
||||
settings.m_devSampleRate,
|
||||
@ -1069,10 +1106,11 @@ bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, boo
|
||||
{
|
||||
doChangeFreq = true;
|
||||
forceNCOFrequency = true;
|
||||
forwardChangeAllDSP = true;
|
||||
|
||||
qDebug("XTRXInput::applySettings: sample rate set to %f with oversampling of %d",
|
||||
settings.m_devSampleRate,
|
||||
1<<settings.m_log2HardDecim);
|
||||
m_deviceShared.m_dev->getActualInputRate(),
|
||||
1 << getLog2HardDecim());
|
||||
}
|
||||
|
||||
resumeTxThread();
|
||||
@ -1162,11 +1200,15 @@ bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, boo
|
||||
int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
|
||||
|
||||
// send to self first
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(
|
||||
m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftDecim),
|
||||
m_settings.m_centerFrequency + ncoShift);
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(getSampleRate(), m_settings.m_centerFrequency + ncoShift);
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
|
||||
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportClockGenChange *report = MsgReportClockGenChange::create();
|
||||
getMessageQueueToGUI()->push(report);
|
||||
}
|
||||
|
||||
// send to source buddies
|
||||
const std::vector<DeviceSourceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
|
||||
std::vector<DeviceSourceAPI*>::const_iterator itSource = sourceBuddies.begin();
|
||||
@ -1174,7 +1216,7 @@ bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, boo
|
||||
for (; itSource != sourceBuddies.end(); ++itSource)
|
||||
{
|
||||
DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create(
|
||||
m_settings.m_devSampleRate, m_settings.m_log2HardDecim, m_settings.m_centerFrequency, true);
|
||||
getDevSampleRate(), getLog2HardDecim(), m_settings.m_centerFrequency, true);
|
||||
(*itSource)->getSampleSourceInputMessageQueue()->push(report);
|
||||
}
|
||||
|
||||
@ -1185,7 +1227,7 @@ bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, boo
|
||||
for (; itSink != sinkBuddies.end(); ++itSink)
|
||||
{
|
||||
DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create(
|
||||
m_settings.m_devSampleRate, m_settings.m_log2HardDecim, m_settings.m_centerFrequency, true);
|
||||
getDevSampleRate(), getLog2HardDecim(), m_settings.m_centerFrequency, true);
|
||||
(*itSink)->getSampleSinkInputMessageQueue()->push(report);
|
||||
}
|
||||
}
|
||||
@ -1193,13 +1235,18 @@ bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, boo
|
||||
{
|
||||
qDebug("XTRXInput::applySettings: forward change to Rx buddies");
|
||||
|
||||
int sampleRate = m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftDecim);
|
||||
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);
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(getSampleRate(), m_settings.m_centerFrequency + ncoShift);
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
|
||||
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportClockGenChange *report = MsgReportClockGenChange::create();
|
||||
getMessageQueueToGUI()->push(report);
|
||||
}
|
||||
|
||||
// send to source buddies
|
||||
const std::vector<DeviceSourceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
|
||||
std::vector<DeviceSourceAPI*>::const_iterator itSource = sourceBuddies.begin();
|
||||
@ -1207,7 +1254,7 @@ bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, boo
|
||||
for (; itSource != sourceBuddies.end(); ++itSource)
|
||||
{
|
||||
DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create(
|
||||
m_settings.m_devSampleRate, m_settings.m_log2HardDecim, m_settings.m_centerFrequency, true);
|
||||
getDevSampleRate(), getLog2HardDecim(), m_settings.m_centerFrequency, true);
|
||||
(*itSource)->getSampleSourceInputMessageQueue()->push(report);
|
||||
}
|
||||
}
|
||||
@ -1215,11 +1262,17 @@ bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, boo
|
||||
{
|
||||
qDebug("XTRXInput::applySettings: forward change to self only");
|
||||
|
||||
int sampleRate = m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftDecim);
|
||||
int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, m_settings.m_centerFrequency + ncoShift);
|
||||
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(getSampleRate(), m_settings.m_centerFrequency + ncoShift);
|
||||
m_fileSink->handleMessage(*notif); // forward to file sink
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
|
||||
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportClockGenChange *report = MsgReportClockGenChange::create();
|
||||
getMessageQueueToGUI()->push(report);
|
||||
}
|
||||
}
|
||||
|
||||
if (forwardClockSource)
|
||||
@ -1248,8 +1301,10 @@ bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, boo
|
||||
}
|
||||
|
||||
qDebug() << "XTRXInput::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_log2SoftDecim) << "S/s"
|
||||
<< " device stream sample rate: " << getDevSampleRate() << "S/s"
|
||||
<< " sample rate with soft decimation: " << getSampleRate() << "S/s"
|
||||
<< " m_devSampleRate: " << m_settings.m_devSampleRate
|
||||
<< " m_log2SoftDecim: " << m_settings.m_log2SoftDecim
|
||||
<< " m_gain: " << m_settings.m_gain
|
||||
<< " m_lpfBW: " << m_settings.m_lpfBW
|
||||
<< " m_ncoEnable: " << m_settings.m_ncoEnable
|
||||
|
@ -91,6 +91,21 @@ public:
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportClockGenChange : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
static MsgReportClockGenChange* create()
|
||||
{
|
||||
return new MsgReportClockGenChange();
|
||||
}
|
||||
|
||||
private:
|
||||
MsgReportClockGenChange() :
|
||||
Message()
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportStreamInfo : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
@ -190,6 +205,9 @@ public:
|
||||
virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; }
|
||||
virtual const QString& getDeviceDescription() const;
|
||||
virtual int getSampleRate() const;
|
||||
uint32_t getDevSampleRate() const;
|
||||
uint32_t getLog2HardDecim() const;
|
||||
double getClockGen() const;
|
||||
virtual quint64 getCenterFrequency() const;
|
||||
virtual void setCenterFrequency(qint64 centerFrequency);
|
||||
|
||||
|
@ -170,6 +170,16 @@ bool XTRXInputGUI::handleMessage(const Message& message)
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (XTRXInput::MsgReportClockGenChange::match(message))
|
||||
{
|
||||
m_settings.m_devSampleRate = m_XTRXInput->getDevSampleRate();
|
||||
|
||||
blockApplySettings(true);
|
||||
displaySettings();
|
||||
blockApplySettings(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (XTRXInput::MsgReportStreamInfo::match(message))
|
||||
{
|
||||
XTRXInput::MsgReportStreamInfo& report = (XTRXInput::MsgReportStreamInfo&) message;
|
||||
@ -245,7 +255,7 @@ void XTRXInputGUI::handleInputMessages()
|
||||
|
||||
void XTRXInputGUI::updateADCRate()
|
||||
{
|
||||
uint32_t adcRate = m_settings.m_devSampleRate * (1<<m_settings.m_log2HardDecim);
|
||||
uint32_t adcRate = m_XTRXInput->getClockGen() / 4;
|
||||
|
||||
if (adcRate < 100000000) {
|
||||
ui->adcRateLabel->setText(tr("%1k").arg(QString::number(adcRate / 1000.0f, 'g', 5)));
|
||||
|
@ -29,7 +29,7 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>LimeSDR Input</string>
|
||||
<string>XTRX Input</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
@ -363,7 +363,7 @@
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
<string>A</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
|
Loading…
x
Reference in New Issue
Block a user