LMSDevice: Compute TxGain on LimeSuite API based on expected Tx output power

Right now, according to a few measurements taken on LimeMicro devices, we
expect the Tx Gain at UHD level to relate 1:1 with the slope in Tx output
power given a specific band.

If more fine-grained results are wanted or some device doesn't follow a
1:1 slope relationship, functions TxGain2TxPower and TxPower2TxGain need
to be adapted/improved.

This patch is basically doing the same thing as was done previously for
UHDDevice in 992c9bd1ce.

Related: OS#4583
Change-Id: If154fe4d4cd118aa30ea43c22ee7119117b77da6
This commit is contained in:
Pau Espin 2020-06-19 14:48:09 +02:00 committed by laforge
parent 8ac169f7ed
commit f68f19b110
2 changed files with 144 additions and 51 deletions

View File

@ -65,8 +65,6 @@ struct dev_desc {
* LimeNET-Micro does not like selecting internal clock
*/
bool clock_src_int_usable;
/* Device specific maximum tx levels selected by phasenoise measurements, in dB */
double max_tx_gain;
/* Sample rate coef (without having TX/RX samples per symbol into account) */
double rate;
/* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
@ -80,12 +78,48 @@ struct dev_desc {
};
static const std::map<enum lms_dev_type, struct dev_desc> dev_param_map {
{ LMS_DEV_SDR_USB, { true, true, 73.0, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_SDR_USB_PREFIX_NAME } },
{ LMS_DEV_SDR_MINI, { false, true, 66.0, GSMRATE, MCBTS_SPACING, 8.9e-5, 8.2e-5, LMS_DEV_SDR_MINI_PREFIX_NAME } },
{ LMS_DEV_NET_MICRO, { true, false, 71.0, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_NET_MICRO_PREFIX_NAME } },
{ LMS_DEV_UNKNOWN, { true, true, 73.0, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
{ LMS_DEV_SDR_USB, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_SDR_USB_PREFIX_NAME } },
{ LMS_DEV_SDR_MINI, { false, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 8.2e-5, LMS_DEV_SDR_MINI_PREFIX_NAME } },
{ LMS_DEV_NET_MICRO, { true, false, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_NET_MICRO_PREFIX_NAME } },
{ LMS_DEV_UNKNOWN, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
};
typedef std::tuple<lms_dev_type, enum gsm_band> dev_band_key;
/* Maximum LimeSuite Tx Gain which can be set/used without distorting the output
* signal, and the resulting real output power measured when that gain is used.
*/
struct dev_band_desc {
double nom_lms_tx_gain; /* dB */
double nom_out_tx_power; /* dBm */
};
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_850), { 73.0, 11.2 } },
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_900), { 73.0, 10.8 } },
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1800), { 65.0, -3.5 } }, /* FIXME: OS#4583: 1800Mhz is failing above TxGain=65, which is around -3.5dBm (already < 0 dBm) */
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1900), { 73.0, 1.7 } }, /* FIXME: OS#4583: 1900MHz is failing in all TxGain values */
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_850), { 66.0, 3.1 } }, /* FIXME: OS#4583: Ensure BAND2 is used at startup */
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_900), { 66.0, 2.8 } }, /* FIXME: OS#4583: Ensure BAND2 is used at startup */
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_1800), { 66.0, -11.6 } }, /* OS#4583: Any of BAND1 or BAND2 is fine */
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_1900), { 66.0, -9.2 } }, /* FIXME: OS#4583: Ensure BAND1 is used at startup */
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_850), { 71.0, 6.8 } },
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_900), { 71.0, 6.8 } },
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_1800), { 65.0, -10.5 } }, /* OS#4583: TxGain=71 (-4.4dBm) FAIL rms phase errors ~10° */
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_1900), { 71.0, -6.3 } }, /* FIXME: OS#4583: all FAIL, BAND1/BAND2 rms phase errors >23° */
};
/* So far measurements done for B210 show really close to linear relationship
* between gain and real output power, so we simply adjust the measured offset
*/
static double TxGain2TxPower(const dev_band_desc &desc, double tx_gain_db)
{
return desc.nom_out_tx_power - (desc.nom_lms_tx_gain - tx_gain_db);
}
static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
{
return desc.nom_lms_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
}
static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
{
std::map<enum lms_dev_type, struct dev_desc>::const_iterator it = dev_param_map.begin();
@ -110,7 +144,7 @@ LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t c
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
m_lms_dev(NULL), started(false), m_dev_type(LMS_DEV_UNKNOWN)
m_lms_dev(NULL), started(false), band((enum gsm_band)0), m_dev_type(LMS_DEV_UNKNOWN)
{
LOGC(DDEV, INFO) << "creating LMS device...";
@ -197,6 +231,27 @@ int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::str
return -1;
}
void LMSDevice::get_dev_band_desc(dev_band_desc& desc)
{
dev_band_map_it it;
enum gsm_band req_band = band;
if (req_band == 0) {
LOGC(DDEV, ERROR) << "Nominal Tx Power requested before Tx Frequency was set! Providing band 900 by default... ";
req_band = GSM_BAND_900;
}
it = dev_band_nom_power_param_map.find(dev_band_key(m_dev_type, req_band));
if (it == dev_band_nom_power_param_map.end()) {
dev_desc desc = dev_param_map.at(m_dev_type);
LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device "
<< desc.name_prefix << " on band " << gsm_band_name(req_band)
<< ", using LimeSDR-USB ones as fallback";
it = dev_band_nom_power_param_map.find(dev_band_key(LMS_DEV_SDR_USB, req_band));
}
OSMO_ASSERT(it != dev_band_nom_power_param_map.end())
desc = it->second;
}
int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
{
lms_info_str_t* info_list;
@ -322,17 +377,20 @@ bool LMSDevice::start()
LOGC(DDEV, INFO) << "starting LMS...";
unsigned int i;
dev_band_desc desc;
if (started) {
LOGC(DDEV, ERR) << "Device already started";
return false;
}
get_dev_band_desc(desc);
/* configure the channels/streams */
for (i=0; i<chans; i++) {
/* Set gains for calibration/filter setup */
/* TX gain to maximum */
setTxGain(maxTxGain(), i);
LMS_SetGaindB(m_lms_dev, LMS_CH_TX, i, TxPower2TxGain(desc, desc.nom_out_tx_power));
/* RX gain to midpoint */
setRxGain((minRxGain() + maxRxGain()) / 2, i);
@ -477,17 +535,6 @@ bool LMSDevice::do_filters(size_t chan)
return true;
}
double LMSDevice::maxTxGain()
{
return dev_param_map.at(m_dev_type).max_tx_gain;
}
double LMSDevice::minTxGain()
{
return 0.0;
}
double LMSDevice::maxRxGain()
{
return 73.0;
@ -498,22 +545,6 @@ double LMSDevice::minRxGain()
return 0.0;
}
double LMSDevice::setTxGain(double dB, size_t chan)
{
if (dB > maxTxGain())
dB = maxTxGain();
if (dB < minTxGain())
dB = minTxGain();
LOGCHAN(chan, DDEV, NOTICE) << "Setting TX gain to " << dB << " dB";
if (LMS_SetGaindB(m_lms_dev, LMS_CH_TX, chan, dB) < 0)
LOGCHAN(chan, DDEV, ERR) << "Error setting TX gain to " << dB << " dB";
else
tx_gains[chan] = dB;
return tx_gains[chan];
}
double LMSDevice::setRxGain(double dB, size_t chan)
{
if (dB > maxRxGain())
@ -530,12 +561,45 @@ double LMSDevice::setRxGain(double dB, size_t chan)
return rx_gains[chan];
}
double LMSDevice::setPowerAttenuation(int atten, size_t chan)
{
double dB;
dev_band_desc desc;
if (chan >= tx_gains.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return 0.0f;
}
get_dev_band_desc(desc);
dB = TxPower2TxGain(desc, desc.nom_out_tx_power - atten);
LOGCHAN(chan, DDEV, NOTICE) << "Setting TX gain to " << dB << " dB";
if (LMS_SetGaindB(m_lms_dev, LMS_CH_TX, chan, dB) < 0)
LOGCHAN(chan, DDEV, ERR) << "Error setting TX gain to " << dB << " dB";
else
tx_gains[chan] = dB;
return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
}
double LMSDevice::getPowerAttenuation(size_t chan) {
dev_band_desc desc;
if (chan >= tx_gains.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return 0.0f;
}
get_dev_band_desc(desc);
return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
}
int LMSDevice::getNominalTxPower(size_t chan)
{
/* TODO: return value based on some experimentally generated table depending on
* band/arfcn, which is known here thanks to TXTUNE
*/
return 23;
dev_band_desc desc;
get_dev_band_desc(desc);
return desc.nom_out_tx_power;
}
void LMSDevice::log_ant_list(bool dir_tx, size_t chan, std::ostringstream& os)
@ -904,13 +968,39 @@ bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
bool LMSDevice::setTxFreq(double wFreq, size_t chan)
{
uint16_t req_arfcn;
enum gsm_band req_band;
if (chan >= chans) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
return false;
}
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
<< " Hz (ARFCN " << req_arfcn << " )";
return false;
}
if (band != 0 && req_band != band) {
LOGCHAN(chan, DDEV, ALERT) << "Requesting Tx Frequency " << wFreq
<< " Hz different from previous band " << gsm_band_name(band);
return false;
}
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Error setting Tx Freq to " << wFreq << " Hz";
return false;
}
band = req_band;
return true;
}

View File

@ -32,6 +32,10 @@
#include <iostream>
#include <lime/LimeSuite.h>
extern "C" {
#include <osmocom/gsm/gsm_utils.h>
}
/* Definition of LIMESDR_TX_AMPL limits maximum amplitude of I and Q
* channels separately. Hence LIMESDR_TX_AMPL value must be 1/sqrt(2) =
* 0.7071.... to get an amplitude of 1 of the complex signal:
@ -48,6 +52,8 @@ enum lms_dev_type {
LMS_DEV_UNKNOWN,
};
struct dev_band_desc;
/** A class to handle a LimeSuite supported device */
class LMSDevice:public RadioDevice {
@ -66,6 +72,7 @@ private:
TIMESTAMP ts_initial, ts_offset;
std::vector<double> tx_gains, rx_gains;
enum gsm_band band;
enum lms_dev_type m_dev_type;
@ -77,19 +84,11 @@ private:
void update_stream_stats_rx(size_t chan, bool *overrun);
void update_stream_stats_tx(size_t chan, bool *underrun);
bool do_clock_src_freq(enum ReferenceType ref, double freq);
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
void get_dev_band_desc(dev_band_desc& desc);
/** get transmit gain */
double getTxGain(size_t chan = 0) {
return tx_gains[chan];
}
/** return maximum Tx Gain **/
double maxTxGain(void);
/** return minimum Rx Gain **/
double minTxGain(void);
double setTxGain(double db, size_t chan) {OSMO_ASSERT(false); return 0.0f; }
double getTxGain(size_t chan = 0) { OSMO_ASSERT(false); return 0.0f; };
double maxTxGain(void) { OSMO_ASSERT(false); return 0.0f; };
public:
@ -178,6 +177,10 @@ public:
/** return minimum Rx Gain **/
double minRxGain(void);
double setPowerAttenuation(int atten, size_t chan);
double getPowerAttenuation(size_t chan = 0);
int getNominalTxPower(size_t chan = 0);
/** sets the RX path to use, returns true if successful and false otherwise */