1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-05-24 03:02:29 -04:00

XTRX: make clock gen setting consistent

This commit is contained in:
f4exb 2019-01-04 04:45:52 +01:00
parent e3069d3a69
commit 733f3d4ba4
15 changed files with 307 additions and 127 deletions

View File

@ -205,7 +205,7 @@ If you use your own location for libmirisdr-4 install directory you need to spec
<h2>XTRX</h2> <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). [XTRX](https://xtrx.io) is supported through the set of [xtrx libraries](https://github.com/xtrx-sdr/images).

View File

@ -36,7 +36,11 @@ const uint32_t DeviceXTRX::m_pgaTbl[m_nbGains] = {
}; };
DeviceXTRX::DeviceXTRX() : DeviceXTRX::DeviceXTRX() :
m_dev(0) m_dev(0),
m_inputRate(0),
m_outputRate(0),
m_masterRate(0),
m_clockGen(0)
{} {}
DeviceXTRX::~DeviceXTRX() DeviceXTRX::~DeviceXTRX()
@ -84,3 +88,56 @@ void DeviceXTRX::getAutoGains(uint32_t autoGain, uint32_t& lnaGain, uint32_t& ti
lnaGain = m_lnaTbl[value]; lnaGain = m_lnaTbl[value];
pgaGain = m_pgaTbl[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;
}

View File

@ -32,6 +32,11 @@ public:
bool open(const char* deviceStr); bool open(const char* deviceStr);
void close(); void close();
struct xtrx_dev *getDevice() { return m_dev; } 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 void getAutoGains(uint32_t autoGain, uint32_t& lnaGain, uint32_t& tiaGain, uint32_t& pgaGain);
static const uint32_t m_nbGains = 74; static const uint32_t m_nbGains = 74;
@ -39,6 +44,12 @@ public:
private: private:
struct xtrx_dev *m_dev; //!< device handle 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_lnaTbl[m_nbGains];
static const uint32_t m_pgaTbl[m_nbGains]; static const uint32_t m_pgaTbl[m_nbGains];

View File

@ -31,9 +31,7 @@ DeviceXTRXShared::DeviceXTRXShared() :
m_channel(-1), m_channel(-1),
m_source(0), m_source(0),
m_sink(0), m_sink(0),
m_inputRate(0),
m_outputRate(0),
m_masterRate(0),
m_thread(0), m_thread(0),
m_threadWasRunning(false) m_threadWasRunning(false)
{} {}
@ -41,63 +39,6 @@ DeviceXTRXShared::DeviceXTRXShared() :
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() double DeviceXTRXShared::get_board_temperature()
{ {
uint64_t val = 0; uint64_t val = 0;

View File

@ -136,9 +136,7 @@ public:
int m_channel; //!< allocated channel (-1 if none) int m_channel; //!< allocated channel (-1 if none)
XTRXInput *m_source; XTRXInput *m_source;
XTRXOutput *m_sink; XTRXOutput *m_sink;
double m_inputRate;
double m_outputRate;
double m_masterRate;
ThreadInterface *m_thread; //!< holds the thread address if started else 0 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 bool m_threadWasRunning; //!< flag to know if thread needs to be resumed after suspend
@ -148,7 +146,6 @@ public:
DeviceXTRXShared(); DeviceXTRXShared();
~DeviceXTRXShared(); ~DeviceXTRXShared();
double set_samplerate(double rate, double master, bool output);
double get_board_temperature(); double get_board_temperature();
bool get_gps_status(); bool get_gps_status();
}; };

View File

@ -2,6 +2,8 @@
<h2>Introduction</h2> <h2>Introduction</h2>
&#9888; 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). 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. 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). 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> <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. 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.

View File

@ -41,6 +41,7 @@
MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgConfigureXTRX, Message) MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgConfigureXTRX, Message)
MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgGetStreamInfo, Message) MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgGetStreamInfo, Message)
MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgGetDeviceInfo, Message) MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgGetDeviceInfo, Message)
MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgReportClockGenChange, Message)
MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgReportStreamInfo, Message) MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgReportStreamInfo, Message)
MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgStartStop, Message) MESSAGE_CLASS_DEFINITION(XTRXOutput::MsgStartStop, Message)
@ -492,9 +493,45 @@ const QString& XTRXOutput::getDeviceDescription() const
int XTRXOutput::getSampleRate() const int XTRXOutput::getSampleRate() const
{ {
double rate = m_settings.m_devSampleRate; 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))); 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 quint64 XTRXOutput::getCenterFrequency() const
{ {
return m_settings.m_centerFrequency + (m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0); 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 else
{ {
m_settings.m_devSampleRate = m_deviceShared.m_inputRate; m_settings.m_devSampleRate = m_deviceShared.m_dev->getActualOutputRate();
m_settings.m_log2HardInterp = log2(m_deviceShared.m_masterRate / m_deviceShared.m_inputRate / 4); m_settings.m_log2HardInterp = getLog2HardInterp();
qDebug() << "XTRXOutput::handleMessage: MsgReportBuddyChange:" qDebug() << "XTRXOutput::handleMessage: MsgReportBuddyChange:"
<< " host_Hz: " << m_deviceShared.m_inputRate << " host_Hz: " << m_deviceShared.m_dev->getActualOutputRate()
<< " rf_Hz: " << m_deviceShared.m_masterRate / 4 << " dac_Hz: " << m_deviceShared.m_dev->getClockGen() / 4
<< " m_log2HardDecim: " << m_settings.m_log2HardInterp; << " m_log2HardInterp: " << m_settings.m_log2HardInterp;
} }
if (m_settings.m_ncoEnable) // need to reset NCO after sample rate change 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)); double master = (settings.m_log2HardInterp == 0) ? 0 : (settings.m_devSampleRate * 4 * (1 << settings.m_log2HardInterp));
if (m_deviceShared.set_samplerate(settings.m_devSampleRate, if (m_deviceShared.m_dev->set_samplerate(settings.m_devSampleRate,
master, //(settings.m_devSampleRate<<settings.m_log2HardDecim)*4, master, //(settings.m_devSampleRate<<settings.m_log2HardDecim)*4,
true) < 0) true) < 0)
{ {
qCritical("XTRXOutput::applySettings: could not set sample rate to %f with oversampling of %d", qCritical("XTRXOutput::applySettings: could not set sample rate to %f with oversampling of %d",
settings.m_devSampleRate, settings.m_devSampleRate,
@ -929,10 +966,11 @@ bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, b
{ {
doChangeFreq = true; doChangeFreq = true;
forceNCOFrequency = true; forceNCOFrequency = true;
forwardChangeAllDSP = true;
qDebug("XTRXOutput::applySettings: sample rate set to %f with oversampling of %d", qDebug("XTRXOutput::applySettings: sample rate set to %f with oversampling of %d",
settings.m_devSampleRate, m_deviceShared.m_dev->getActualOutputRate(),
1<<settings.m_log2HardInterp); 1 << getLog2HardInterp());
} }
resumeRxThread(); 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; int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
// send to self first // send to self first
DSPSignalNotification *notif = new DSPSignalNotification( DSPSignalNotification *notif = new DSPSignalNotification(getSampleRate(), m_settings.m_centerFrequency + ncoShift);
m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp),
m_settings.m_centerFrequency + ncoShift);
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
if (getMessageQueueToGUI())
{
MsgReportClockGenChange *report = MsgReportClockGenChange::create();
getMessageQueueToGUI()->push(report);
}
// send to source buddies // send to source buddies
const std::vector<DeviceSourceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies(); const std::vector<DeviceSourceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
std::vector<DeviceSourceAPI*>::const_iterator itSource = sourceBuddies.begin(); 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) for (; itSource != sourceBuddies.end(); ++itSource)
{ {
DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create( 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); (*itSource)->getSampleSourceInputMessageQueue()->push(report);
} }
@ -1028,7 +1070,7 @@ bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, b
for (; itSink != sinkBuddies.end(); ++itSink) for (; itSink != sinkBuddies.end(); ++itSink)
{ {
DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create( 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); (*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"); 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; int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
// send to self first // 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); m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
if (getMessageQueueToGUI())
{
MsgReportClockGenChange *report = MsgReportClockGenChange::create();
getMessageQueueToGUI()->push(report);
}
// send to sink buddies // send to sink buddies
const std::vector<DeviceSinkAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies(); const std::vector<DeviceSinkAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
std::vector<DeviceSinkAPI*>::const_iterator itSink = sinkBuddies.begin(); 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) for (; itSink != sinkBuddies.end(); ++itSink)
{ {
DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create( 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); (*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"); 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; 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); m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
if (getMessageQueueToGUI())
{
MsgReportClockGenChange *report = MsgReportClockGenChange::create();
getMessageQueueToGUI()->push(report);
}
} }
if (forwardClockSource) 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" qDebug() << "XTRXOutput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz"
<< " device stream sample rate: " << m_settings.m_devSampleRate << "S/s" << " device stream sample rate: " << getDevSampleRate() << "S/s"
<< " sample rate with soft interpolation: " << m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftInterp) << "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_gain: " << m_settings.m_gain
<< " m_lpfBW: " << m_settings.m_lpfBW << " m_lpfBW: " << m_settings.m_lpfBW
<< " m_ncoEnable: " << m_settings.m_ncoEnable << " m_ncoEnable: " << m_settings.m_ncoEnable

View File

@ -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 { class MsgReportStreamInfo : public Message {
MESSAGE_CLASS_DECLARATION MESSAGE_CLASS_DECLARATION
@ -170,6 +185,9 @@ public:
virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; } virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; }
virtual const QString& getDeviceDescription() const; virtual const QString& getDeviceDescription() const;
virtual int getSampleRate() const; virtual int getSampleRate() const;
uint32_t getDevSampleRate() const;
uint32_t getLog2HardInterp() const;
double getClockGen() const;
virtual quint64 getCenterFrequency() const; virtual quint64 getCenterFrequency() const;
virtual void setCenterFrequency(qint64 centerFrequency); virtual void setCenterFrequency(qint64 centerFrequency);

View File

@ -168,6 +168,16 @@ bool XTRXOutputGUI::handleMessage(const Message& message)
return true; 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)) else if (XTRXOutput::MsgReportStreamInfo::match(message))
{ {
XTRXOutput::MsgReportStreamInfo& report = (XTRXOutput::MsgReportStreamInfo&) message; XTRXOutput::MsgReportStreamInfo& report = (XTRXOutput::MsgReportStreamInfo&) message;
@ -243,7 +253,7 @@ void XTRXOutputGUI::handleInputMessages()
void XTRXOutputGUI::updateDACRate() 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) { if (dacRate < 100000000) {
ui->dacRateLabel->setText(tr("%1k").arg(QString::number(dacRate / 1000.0f, 'g', 5))); ui->dacRateLabel->setText(tr("%1k").arg(QString::number(dacRate / 1000.0f, 'g', 5)));

View File

@ -328,7 +328,7 @@
</property> </property>
<item> <item>
<property name="text"> <property name="text">
<string>1</string> <string>A</string>
</property> </property>
</item> </item>
<item> <item>

View File

@ -2,6 +2,8 @@
<h2>Introduction</h2> <h2>Introduction</h2>
&#9888; 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). 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. 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. 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> <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. 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.

View File

@ -42,6 +42,7 @@
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgConfigureXTRX, Message) MESSAGE_CLASS_DEFINITION(XTRXInput::MsgConfigureXTRX, Message)
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgGetStreamInfo, Message) MESSAGE_CLASS_DEFINITION(XTRXInput::MsgGetStreamInfo, Message)
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgGetDeviceInfo, Message) MESSAGE_CLASS_DEFINITION(XTRXInput::MsgGetDeviceInfo, Message)
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgReportClockGenChange, Message)
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgReportStreamInfo, Message) MESSAGE_CLASS_DEFINITION(XTRXInput::MsgReportStreamInfo, Message)
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgFileRecord, Message) MESSAGE_CLASS_DEFINITION(XTRXInput::MsgFileRecord, Message)
MESSAGE_CLASS_DEFINITION(XTRXInput::MsgStartStop, Message) MESSAGE_CLASS_DEFINITION(XTRXInput::MsgStartStop, Message)
@ -507,9 +508,45 @@ const QString& XTRXInput::getDeviceDescription() const
int XTRXInput::getSampleRate() const int XTRXInput::getSampleRate() const
{ {
double rate = m_settings.m_devSampleRate; 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))); 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 quint64 XTRXInput::getCenterFrequency() const
{ {
return m_settings.m_centerFrequency + (m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0); 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 else
{ {
m_settings.m_devSampleRate = m_deviceShared.m_inputRate; m_settings.m_devSampleRate = m_deviceShared.m_dev->getActualInputRate();
m_settings.m_log2HardDecim = log2(m_deviceShared.m_masterRate / m_deviceShared.m_inputRate / 4); m_settings.m_log2HardDecim = getLog2HardDecim();
qDebug() << "XTRXInput::handleMessage: MsgReportBuddyChange:" qDebug() << "XTRXInput::handleMessage: MsgReportBuddyChange:"
<< " host_Hz: " << m_deviceShared.m_inputRate << " host_Hz: " << m_deviceShared.m_dev->getActualInputRate()
<< " rf_Hz: " << m_deviceShared.m_masterRate / 4 << " adc_Hz: " << m_deviceShared.m_dev->getClockGen() / 4
<< " m_log2HardDecim: " << m_settings.m_log2HardDecim; << " 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)); double master = (settings.m_log2HardDecim == 0) ? 0 : (settings.m_devSampleRate * 4 * (1 << settings.m_log2HardDecim));
if (m_deviceShared.set_samplerate(settings.m_devSampleRate, if (m_deviceShared.m_dev->set_samplerate(settings.m_devSampleRate,
master, //(settings.m_devSampleRate<<settings.m_log2HardDecim)*4, master, //(settings.m_devSampleRate<<settings.m_log2HardDecim)*4,
false) < 0) false) < 0)
{ {
qCritical("XTRXInput::applySettings: could not set sample rate to %f with oversampling of %d", qCritical("XTRXInput::applySettings: could not set sample rate to %f with oversampling of %d",
settings.m_devSampleRate, settings.m_devSampleRate,
@ -1069,10 +1106,11 @@ bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, boo
{ {
doChangeFreq = true; doChangeFreq = true;
forceNCOFrequency = true; forceNCOFrequency = true;
forwardChangeAllDSP = true;
qDebug("XTRXInput::applySettings: sample rate set to %f with oversampling of %d", qDebug("XTRXInput::applySettings: sample rate set to %f with oversampling of %d",
settings.m_devSampleRate, m_deviceShared.m_dev->getActualInputRate(),
1<<settings.m_log2HardDecim); 1 << getLog2HardDecim());
} }
resumeTxThread(); 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; int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
// send to self first // send to self first
DSPSignalNotification *notif = new DSPSignalNotification( DSPSignalNotification *notif = new DSPSignalNotification(getSampleRate(), m_settings.m_centerFrequency + ncoShift);
m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftDecim),
m_settings.m_centerFrequency + ncoShift);
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
if (getMessageQueueToGUI())
{
MsgReportClockGenChange *report = MsgReportClockGenChange::create();
getMessageQueueToGUI()->push(report);
}
// send to source buddies // send to source buddies
const std::vector<DeviceSourceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies(); const std::vector<DeviceSourceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
std::vector<DeviceSourceAPI*>::const_iterator itSource = sourceBuddies.begin(); 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) for (; itSource != sourceBuddies.end(); ++itSource)
{ {
DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create( 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); (*itSource)->getSampleSourceInputMessageQueue()->push(report);
} }
@ -1185,7 +1227,7 @@ bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, boo
for (; itSink != sinkBuddies.end(); ++itSink) for (; itSink != sinkBuddies.end(); ++itSink)
{ {
DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create( 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); (*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"); 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; int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
// send to self first // 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); m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
if (getMessageQueueToGUI())
{
MsgReportClockGenChange *report = MsgReportClockGenChange::create();
getMessageQueueToGUI()->push(report);
}
// send to source buddies // send to source buddies
const std::vector<DeviceSourceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies(); const std::vector<DeviceSourceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
std::vector<DeviceSourceAPI*>::const_iterator itSource = sourceBuddies.begin(); 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) for (; itSource != sourceBuddies.end(); ++itSource)
{ {
DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create( 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); (*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"); 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; 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_fileSink->handleMessage(*notif); // forward to file sink
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
if (getMessageQueueToGUI())
{
MsgReportClockGenChange *report = MsgReportClockGenChange::create();
getMessageQueueToGUI()->push(report);
}
} }
if (forwardClockSource) 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" qDebug() << "XTRXInput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz"
<< " device stream sample rate: " << m_settings.m_devSampleRate << "S/s" << " device stream sample rate: " << getDevSampleRate() << "S/s"
<< " sample rate with soft decimation: " << m_settings.m_devSampleRate/(1<<m_settings.m_log2SoftDecim) << "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_gain: " << m_settings.m_gain
<< " m_lpfBW: " << m_settings.m_lpfBW << " m_lpfBW: " << m_settings.m_lpfBW
<< " m_ncoEnable: " << m_settings.m_ncoEnable << " m_ncoEnable: " << m_settings.m_ncoEnable

View File

@ -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 { class MsgReportStreamInfo : public Message {
MESSAGE_CLASS_DECLARATION MESSAGE_CLASS_DECLARATION
@ -190,6 +205,9 @@ public:
virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; } virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; }
virtual const QString& getDeviceDescription() const; virtual const QString& getDeviceDescription() const;
virtual int getSampleRate() const; virtual int getSampleRate() const;
uint32_t getDevSampleRate() const;
uint32_t getLog2HardDecim() const;
double getClockGen() const;
virtual quint64 getCenterFrequency() const; virtual quint64 getCenterFrequency() const;
virtual void setCenterFrequency(qint64 centerFrequency); virtual void setCenterFrequency(qint64 centerFrequency);

View File

@ -170,6 +170,16 @@ bool XTRXInputGUI::handleMessage(const Message& message)
return true; 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)) else if (XTRXInput::MsgReportStreamInfo::match(message))
{ {
XTRXInput::MsgReportStreamInfo& report = (XTRXInput::MsgReportStreamInfo&) message; XTRXInput::MsgReportStreamInfo& report = (XTRXInput::MsgReportStreamInfo&) message;
@ -245,7 +255,7 @@ void XTRXInputGUI::handleInputMessages()
void XTRXInputGUI::updateADCRate() 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) { if (adcRate < 100000000) {
ui->adcRateLabel->setText(tr("%1k").arg(QString::number(adcRate / 1000.0f, 'g', 5))); ui->adcRateLabel->setText(tr("%1k").arg(QString::number(adcRate / 1000.0f, 'g', 5)));

View File

@ -29,7 +29,7 @@
</font> </font>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>LimeSDR Input</string> <string>XTRX Input</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing"> <property name="spacing">
@ -363,7 +363,7 @@
</property> </property>
<item> <item>
<property name="text"> <property name="text">
<string>1</string> <string>A</string>
</property> </property>
</item> </item>
<item> <item>