LimeSDR input: NCO support (draft #1)

This commit is contained in:
f4exb 2017-04-20 18:21:01 +02:00
parent 01b469ec80
commit 826941ba80
9 changed files with 396 additions and 48 deletions

View File

@ -1,10 +1,12 @@
project(limesdrdevice)
set(limesdrdevice_SOURCES
devicelimesdr.cpp
devicelimesdrparam.cpp
)
set(limesdrdevice_HEADERS
devicelimesdr.h
devicelimesdrparam.h
)

View File

@ -0,0 +1,167 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <cstdio>
#include <cstring>
#include "devicelimesdr.h"
bool DeviceLimeSDR::enableNCO(lms_device_t *device, bool dir_tx, std::size_t chan, float frequency, bool enable)
{
if (LMS_WriteParam(device, LMS7param(MAC), chan+1) < 0)
{
fprintf(stderr, "DeviceLimeSDR::enableNCO: cannot address channel #%lu\n", chan);
return false;
}
if (dir_tx)
{
if (LMS_WriteParam(device, LMS7param(CMIX_BYP_RXTSP), enable ? 0 : 1) < 0)
{
fprintf(stderr, "DeviceLimeSDR::enableNCO: cannot %s Rx NCO\n", enable ? "enable" : "disable");
return false;
}
else
{
return true;
}
}
else
{
if (LMS_WriteParam(device, LMS7param(CMIX_BYP_TXTSP), enable ? 0 : 1) < 0)
{
fprintf(stderr, "DeviceLimeSDR::enableNCO: cannot %s Tx NCO\n", enable ? "enable" : "disable");
return false;
}
else
{
return true;
}
}
}
bool DeviceLimeSDR::setNCOFrequency(lms_device_t *device, bool dir_tx, std::size_t chan, float frequency)
{
bool positive;
float freqs[LMS_NCO_VAL_COUNT];
float phos[LMS_NCO_VAL_COUNT];
if (LMS_GetNCOFrequency(device, dir_tx, chan, freqs, phos) < 0)
{
fprintf(stderr, "DeviceLimeSDR::setNCOFrequency: cannot get NCO frequencies and phases\n");
}
if (frequency < 0)
{
positive = false;
frequency = -frequency;
}
else
{
positive = true;
}
freqs[0] = frequency;
if (LMS_SetNCOFrequency(device, dir_tx, chan, freqs, phos) < 0)
{
fprintf(stderr, "DeviceLimeSDR::setNCOFrequency: cannot set frequency to %f\n", frequency);
return false;
}
if (LMS_SetNCOIndex(device, dir_tx, chan, 0, positive) < 0) // TODO: verify positive = downconvert ?
{
fprintf(stderr, "DeviceLimeSDR::setNCOFrequency: cannot set conversion direction %s\n", positive ? "down" : "up");
return false;
}
return true;
}
bool DeviceLimeSDR::setNCOFrequency(lms_device_t *device, bool dir_tx, std::size_t chan, bool enable, float frequency)
{
if (enable)
{
bool positive;
float freqs[LMS_NCO_VAL_COUNT];
float phos[LMS_NCO_VAL_COUNT];
if (LMS_GetNCOFrequency(device, dir_tx, chan, freqs, phos) < 0)
{
fprintf(stderr, "DeviceLimeSDR::setNCOFrequency: cannot get NCO frequencies and phases\n");
}
if (frequency < 0)
{
positive = false;
frequency = -frequency;
}
else
{
positive = true;
}
freqs[0] = frequency;
if (LMS_SetNCOFrequency(device, dir_tx, chan, freqs, phos) < 0)
{
fprintf(stderr, "DeviceLimeSDR::setNCOFrequency: cannot set frequency to %f\n", frequency);
return false;
}
if (LMS_SetNCOIndex(device, dir_tx, chan, 0, positive) < 0) // TODO: verify positive = downconvert ?
{
fprintf(stderr, "DeviceLimeSDR::setNCOFrequency: cannot set conversion direction %s\n", positive ? "down" : "up");
return false;
}
return true;
}
else
{
if (LMS_WriteParam(device, LMS7param(MAC), chan+1) < 0)
{
fprintf(stderr, "DeviceLimeSDR::setNCOFrequency: cannot address channel #%lu\n", chan);
return false;
}
if (dir_tx)
{
if (LMS_WriteParam(device, LMS7param(CMIX_BYP_RXTSP), 1) < 0)
{
fprintf(stderr, "DeviceLimeSDR::enableNCO: cannot disable Rx NCO on channel %lu\n", chan);
return false;
}
else
{
return true;
}
}
else
{
if (LMS_WriteParam(device, LMS7param(CMIX_BYP_TXTSP), 1) < 0)
{
fprintf(stderr, "DeviceLimeSDR::enableNCO: cannot disable Tx NCO on channel %lu\n", chan);
return false;
}
else
{
return true;
}
}
}
}

View File

@ -0,0 +1,33 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef DEVICES_LIMESDR_DEVICELIMESDR_H_
#define DEVICES_LIMESDR_DEVICELIMESDR_H_
#include "lime/LimeSuite.h"
class DeviceLimeSDR
{
public:
/** enable or disable NCO. If re-enabled frequency should have been set once */
static bool enableNCO(lms_device_t *device, bool dir_tx, std::size_t chan, bool enable);
/** set NCO frequency with positive or negative frequency (deals with up/down convert). Enables NCO */
static bool setNCOFrequency(lms_device_t *device, bool dir_tx, std::size_t chan, float frequency);
/** combination of the two like LMS_SetGFIRLPF */
static bool setNCOFrequency(lms_device_t *device, bool dir_tx, std::size_t chan, bool enable, float frequency);
};
#endif /* DEVICES_LIMESDR_DEVICELIMESDR_H_ */

View File

@ -26,6 +26,7 @@
#include "limesdrinput.h"
#include "limesdrinputthread.h"
#include "limesdr/devicelimesdrparam.h"
#include "limesdr/devicelimesdr.h"
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgConfigureLimeSDR, Message)
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgGetStreamInfo, Message)
@ -457,7 +458,9 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc
if ((m_settings.m_gain != settings.m_gain) ||
(m_settings.m_lpfBW != settings.m_lpfBW) ||
(m_settings.m_lpfFIRBW != settings.m_lpfFIRBW) ||
(m_settings.m_lpfFIREnable != settings.m_lpfFIREnable) || force)
(m_settings.m_lpfFIREnable != settings.m_lpfFIREnable) ||
(m_settings.m_ncoEnable != settings.m_ncoEnable) ||
(m_settings.m_ncoFrequency != settings.m_ncoFrequency) || force)
{
suspendOwnThread = true;
}
@ -648,6 +651,34 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc
}
}
if ((m_settings.m_ncoFrequency != settings.m_ncoFrequency) ||
(m_settings.m_ncoEnable != settings.m_ncoEnable) || force)
{
m_settings.m_ncoFrequency = settings.m_ncoFrequency;
m_settings.m_ncoEnable = settings.m_ncoEnable;
if (m_deviceShared.m_deviceParams->getDevice() != 0)
{
if (DeviceLimeSDR::setNCOFrequency(m_deviceShared.m_deviceParams->getDevice(),
LMS_CH_RX,
m_deviceShared.m_channel,
m_settings.m_ncoEnable,
m_settings.m_ncoFrequency))
{
doCalibration = true;
qDebug("LimeSDRInput::applySettings: %sd and set NCO to %f Hz",
m_settings.m_ncoEnable ? "enable" : "disable",
m_settings.m_ncoFrequency);
}
else
{
qCritical("LimeSDRInput::applySettings: could %s and set LPF FIR to %f Hz",
m_settings.m_ncoEnable ? "enable" : "disable",
m_settings.m_ncoFrequency);
}
}
}
if ((m_settings.m_log2SoftDecim != settings.m_log2SoftDecim) || force)
{
m_settings.m_log2SoftDecim = settings.m_log2SoftDecim;
@ -682,6 +713,7 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc
}
}
if (doCalibration)
{
if (LMS_Calibrate(m_deviceShared.m_deviceParams->getDevice(),

View File

@ -61,6 +61,8 @@ LimeSDRInputGUI::LimeSDRInputGUI(DeviceSourceAPI *deviceAPI, QWidget* parent) :
ui->lpFIR->setColorMapper(ColorMapper(ColorMapper::ReverseGold));
ui->lpFIR->setValueRange(5, 1U, 56000U);
ui->ncoFrequency->setColorMapper(ColorMapper(ColorMapper::ReverseGold));
ui->channelNumberText->setText(tr("#%1").arg(m_limeSDRInput->getChannelIndex()));
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
@ -244,6 +246,12 @@ void LimeSDRInputGUI::displaySettings()
ui->gain->setValue(m_settings.m_gain);
ui->gainText->setText(tr("%1dB").arg(m_settings.m_gain));
int ncoHalfRange = (m_settings.m_devSampleRate * (1<<(m_settings.m_log2HardDecim)))/2;
ui->ncoFrequency->setValueRange(7,
(m_settings.m_centerFrequency - ncoHalfRange)/1000,
(m_settings.m_centerFrequency + ncoHalfRange)/1000); // frequency dial is in kHz
ui->ncoFrequency->setValue(m_settings.m_centerFrequency + m_settings.m_ncoFrequency);
}
void LimeSDRInputGUI::sendSettings()
@ -345,6 +353,25 @@ void LimeSDRInputGUI::on_centerFrequency_changed(quint64 value)
sendSettings();
}
void LimeSDRInputGUI::on_ncoFrequency_changed(quint64 value)
{
m_settings.m_ncoFrequency = (int64_t) value - (int64_t) m_settings.m_centerFrequency;
sendSettings();
}
void LimeSDRInputGUI::on_ncoEnable_toggled(bool checked)
{
m_settings.m_ncoEnable = checked;
sendSettings();
}
void LimeSDRInputGUI::on_ncoReset_clicked(bool checked)
{
m_settings.m_ncoFrequency = 0;
ui->ncoFrequency->setValue(m_settings.m_centerFrequency);
sendSettings();
}
void LimeSDRInputGUI::on_dcOffset_toggled(bool checked)
{
m_settings.m_dcBlock = checked;
@ -393,7 +420,10 @@ void LimeSDRInputGUI::on_lpFIREnable_toggled(bool checked)
void LimeSDRInputGUI::on_lpFIR_changed(quint64 value)
{
m_settings.m_lpfFIRBW = value * 1000;
sendSettings();
if (m_settings.m_lpfFIREnable) { // do not send the update if the FIR is disabled
sendSettings();
}
}
void LimeSDRInputGUI::on_gain_valueChanged(int value)

View File

@ -74,6 +74,9 @@ private slots:
void on_startStop_toggled(bool checked);
void on_record_toggled(bool checked);
void on_centerFrequency_changed(quint64 value);
void on_ncoFrequency_changed(quint64 value);
void on_ncoEnable_toggled(bool checked);
void on_ncoReset_clicked(bool checked);
void on_dcOffset_toggled(bool checked);
void on_iqImbalance_toggled(bool checked);
void on_sampleRate_changed(quint64 value);

View File

@ -29,7 +29,7 @@
</font>
</property>
<property name="windowTitle">
<string>BladeRF</string>
<string>LimeSDR Input</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
@ -190,43 +190,79 @@
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<layout class="QHBoxLayout" name="ncoSampleRateLayout">
<property name="leftMargin">
<number>6</number>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_corr">
<item row="0" column="7">
<widget class="QLabel" name="samplerateUnit">
<property name="text">
<string>S/s</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="ButtonSwitch" name="iqImbalance">
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="ButtonSwitch" name="ncoEnable">
<property name="toolTip">
<string>Automatic IQ imbalance correction</string>
<string>Enable the TSP NCO</string>
</property>
<property name="text">
<string>IQ</string>
<string>NCO</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="ButtonSwitch" name="dcOffset">
<item>
<widget class="QPushButton" name="ncoReset">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>Automatic DC offset removal</string>
<string>Reset the NCO to zero frequency</string>
</property>
<property name="text">
<string>DC</string>
<string>R</string>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_2">
<item>
<widget class="QWidget" name="ncoFrequency" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>Monospace</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="toolTip">
<string>Center frequency with NCO engaged (kHz)</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="ncoUnits">
<property name="text">
<string>kHz</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -238,14 +274,20 @@
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QLabel" name="corrLabel">
<item>
<widget class="QLabel" name="samplerateLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Auto</string>
<string>SR</string>
</property>
</widget>
</item>
<item row="0" column="5">
<item>
<widget class="ValueDial" name="sampleRate" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
@ -267,16 +309,10 @@
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="samplerateLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<widget class="QLabel" name="samplerateUnit">
<property name="text">
<string>SR</string>
<string>S/s</string>
</property>
</widget>
</item>
@ -290,13 +326,47 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="sampleRateLayout">
<layout class="QHBoxLayout" name="corrDecimLayout">
<property name="topMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="corrLabel">
<property name="text">
<string>Auto</string>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="dcOffset">
<property name="toolTip">
<string>Automatic DC offset removal</string>
</property>
<property name="text">
<string>DC</string>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="iqImbalance">
<property name="toolTip">
<string>Automatic IQ imbalance correction</string>
</property>
<property name="text">
<string>IQ</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="hwDecimLabel">
<property name="text">
@ -422,13 +492,6 @@
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_vga1">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="lpfLayout">
<property name="leftMargin">
@ -539,6 +602,13 @@
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_vga1">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="gainLayout">
<item>
@ -591,6 +661,13 @@
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="statusLayout">
<property name="leftMargin">

View File

@ -35,6 +35,8 @@ void LimeSDRInputSettings::resetToDefaults()
m_lpfFIREnable = false;
m_lpfFIRBW = 2.5e6f;
m_gain = 30;
m_ncoEnable = false;
m_ncoFrequency = 0;
}
QByteArray LimeSDRInputSettings::serialize() const

View File

@ -45,6 +45,8 @@ struct LimeSDRInputSettings
bool m_lpfFIREnable; //!< Enable LMS digital lowpass FIR filters
float m_lpfFIRBW; //!< LMS digital lowpass FIR filters bandwidth (Hz)
uint32_t m_gain; //!< Optimally distributed gain (dB)
bool m_ncoEnable; //!< Enable TSP NCO and mixing
int m_ncoFrequency; //!< Actual NCO frequency (the resulting frequency with mixing is displayed)
LimeSDRInputSettings();
void resetToDefaults();