1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-25 09:18:54 -05: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>
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).

View File

@ -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;
}

View File

@ -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];

View File

@ -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;

View File

@ -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();
};

View File

@ -2,6 +2,8 @@
<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).
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.
@ -24,12 +26,12 @@ If libraries are installed in a custom place like `/opt/install/xtrx-images` add
<h3>1: Start/Stop</h3>
Device start / stop button.
Device start / stop button.
- Blue triangle icon: device is ready and can be started
- Green square icon: device is running and can be stopped
- Magenta (or pink) square icon: an error occurred. In the case the device was accidentally disconnected you may click on the icon to stop, plug back in, check the source on the sampling devices control panel and start again.
<h3>2: DAC sample rate</h3>
This is the sample rate at which the DAC runs in kS/s (k) or MS/s (M) after hardware interpolation (9). Thus this is the host to device sample rate (11) multiplied by the hardware interpolation factor (9). Please note that a hardware decimation of 4 is required for the device to work properly.
@ -57,7 +59,7 @@ Use this button to activate/deactivate the TSP NCO. The LMS7002M chip has an ind
This is the frequency shift applied when the NCO is engaged thus the actual LO frequency is the center frequency of transmission minus this value. Use the thumbwheels to adjust frequency as done with the LO (1.1). Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. The boundaries are dynamically calculated from the LO center frequency, sample rate and hardware interpolation factor.
&#9758; In the LMS7002M TSP block the NCO sits after the interpolator (see Fig.14 of the [datasheet](http://www.limemicro.com/wp-content/uploads/2015/09/LMS7002M-Data-Sheet-v2.8.0.pdf) p.7) so it runs at the actual DAC rate. Hence the NCO limits are calculated as +/- half the device to host sample rate multiplied by the hardware interpolation factor. For example with a 4 MS/s device to host sample rate (10) and a hardware interpolation of 16 (9) you have +/- 32 MHz span around the LO for the NCO. In this example you can tune all HF frequencies with the center frequency set at its lowest (30 MHz).
&#9758; In the LMS7002M TSP block the NCO sits after the interpolator (see Fig.14 of the [datasheet](http://www.limemicro.com/wp-content/uploads/2015/09/LMS7002M-Data-Sheet-v2.8.0.pdf) p.7) so it runs at the actual DAC rate. Hence the NCO limits are calculated as +/- half the device to host sample rate multiplied by the hardware interpolation factor. For example with a 4 MS/s device to host sample rate (10) and a hardware interpolation of 16 (9) you have +/- 32 MHz span around the LO for the NCO. In this example you can tune all HF frequencies with the center frequency set at its lowest (30 MHz).
<h3>8: External clock control</h3>
@ -78,7 +80,7 @@ Use this checkbox to enable or disable the external clock input
<h4>8.3: Confirm changes</h4>
Use the "OK" button to confirm your changes
<h4>8.4: Dismiss changes</h4>
Use the "Cancel" button to dismiss your changes
@ -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.
@ -123,7 +127,7 @@ This label turns green when status can be obtained from the current stream. Usua
<h3>17: GPSDO lock indicator</h3>
This label turns green when the GPS used for the GPSDO is locked.
<h3>18: Stream global (all Tx) throughput in MB/s</h3>
This is the stream throughput in MB/s and is usually about 3 times the sample rate for a single stream and 6 times for a dual Tx stream. This is due to the fact that 12 bits samples are used and although they are represented as 16 bit values only 12 bits travel on the USB link.

View File

@ -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

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 {
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);

View File

@ -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)));

View File

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

View File

@ -2,6 +2,8 @@
<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).
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.
@ -30,12 +32,12 @@ This is the center frequency of reception in kHz.
<h4>1.2: Start/Stop</h4>
Device start / stop button.
Device start / stop button.
- Blue triangle icon: device is ready and can be started
- Green square icon: device is running and can be stopped
- Magenta (or pink) square icon: an error occurred. In the case the device was accidentally disconnected you may click on the icon to stop, plug back in, check the source on the sampling devices control panel and start again.
<h4>1.3: Record</h4>
Record baseband I/Q stream toggle button
@ -48,7 +50,7 @@ This is the sample rate at which the ADC runs in kS/s (k) or MS/s (M) before har
<h4>1.5: Stream sample rate</h4>
Baseband I/Q sample rate in kS/s. This is the device to host sample rate (5) divided by the software decimation factor (4).
Baseband I/Q sample rate in kS/s. This is the device to host sample rate (5) divided by the software decimation factor (4).
<h4>1.6: Channel number</h4>
@ -68,7 +70,7 @@ Use this button to activate/deactivate the TSP NCO. The LMS7002M chip has an ind
This is the frequency shift applied when the NCO is engaged thus the actual LO frequency is the center frequency of reception minus this value. Use the thumbwheels to adjust frequency as done with the LO (1.1). Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. The boundaries are dynamically calculated from the LO center frequency, sample rate and hardware decimation factor.
&#9758; In the LMS7002M TSP block the NCO sits before the decimator (see Fig.14 of the [datasheet](http://www.limemicro.com/wp-content/uploads/2015/09/LMS7002M-Data-Sheet-v2.8.0.pdf) p.7) so it runs at the actual ADC rate. Hence the NCO limits are calculated as +/- half the device to host sample rate multiplied by the hardware decimation factor. For example with a 4 MS/s device to host sample rate (5) and a hardware decimation of 16 (3) you have +/- 32 MHz span around the LO for the NCO. In this example you can tune all HF frequencies with the center frequency set at its lowest (30 MHz).
&#9758; In the LMS7002M TSP block the NCO sits before the decimator (see Fig.14 of the [datasheet](http://www.limemicro.com/wp-content/uploads/2015/09/LMS7002M-Data-Sheet-v2.8.0.pdf) p.7) so it runs at the actual ADC rate. Hence the NCO limits are calculated as +/- half the device to host sample rate multiplied by the hardware decimation factor. For example with a 4 MS/s device to host sample rate (5) and a hardware decimation of 16 (3) you have +/- 32 MHz span around the LO for the NCO. In this example you can tune all HF frequencies with the center frequency set at its lowest (30 MHz).
<h4>2.3: DC component auto correction</h4>
@ -97,16 +99,18 @@ Use this checkbox to enable or disable the external clock input
<h5>2.6.3: Confirm changes</h5>
Use the "OK" button to confirm your changes
<h5>2.6.4: Dismiss changes</h5>
Use the "Cancel" button to dismiss your changes
<h3>3: LMS7002M hardware decimation factor</h3>
The TSP block in the LMS7002M hardware has a decimation chain that acts on both Rx channels. It is composed of 5 halfband decimation stages and therefore can achieve decimation between 1 (no decimation) and 32 in increasing powers of 2: 1, 2, 4, 8, 16, 32.
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>
@ -158,7 +162,7 @@ Use this combo box to select the antenna input:
- **Lo**: Selects the low frequency input (700 to 900 MHz nominally)
- **Hi**: Selects the high frequency input (2 to 2.6 GHz)
<h3>10: Stream status indicator</h3>
This label turns green when status can be obtained from the current stream. Usually this means that the stream is up and running but not necessarily streaming data. The various status elements appear next on the same line (12)

View File

@ -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

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 {
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);

View File

@ -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)));

View File

@ -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>