Compare commits

...

28 Commits

Author SHA1 Message Date
Harald Welte
35a067ea7c lms: Fix coding style
In Change-Id Ib2fca81b76d027b08e2891056fa076d071597783 we introduced
some coding style violations.  Let's make newly-added code follows
standard Osmocom coding style.

Change-Id: Ib7ddd275014f03a2eed3cddc02b1356e2b00c0bc
2018-06-13 23:32:42 +02:00
Harald Welte
9939ccd0de radioDevice: better encapsulation in base class
It's not good style to have the derived classes initialize members
inherited from the base class using "this->foo = bar".  Rather, let's
make the base class have a constructor, and call that constructor to
initialize the members of the base class.

While doing this
* rename 'offset' to 'lo_offset' to avoid confusion with timestamp offset
* move 'InterfaceType' into the base class
* move 'chans' into the base class
* move 'rx_sps' into the base class
* mark base class members as 'protected'

Change-Id: Ib885675a7612a392aa7f75fca81269ddcff2f6ab
2018-06-13 23:21:57 +02:00
Harald Welte
9eda0229a3 radioDevice: Move tx_sps from derived into base class
All three derived classes use a tx_sps member, let's move this into
the base class.

Change-Id: I73b4aa2705c5049561e2d7b21301a0d2b3c96ced
2018-06-13 22:47:48 +02:00
Harald Welte
a1031f1c9b lms: Call set_antennas() during open() method
Without this call, the antenna/path configuration is not applied.

Change-Id: I0bca58266b59f1315ec72b6407fe4f4495aff678
2018-06-13 21:56:24 +02:00
Harald Welte
66efb7c538 lms: Fix support for rx_paths / tx_paths
Before this patch, any configuration in osmo-trx.cfg regarding the rx
and tx "antenna" (path) would have been completely ignored, as the
radioDevice::make() function would simply drop those arguments to the
floor.

Change-Id: Ie50f854abbc9dcf351cddc052d10206382e1d5d3
2018-06-13 21:55:09 +02:00
Harald Welte
4f3aedbfee move set_antennas() from UHD to generic radioDevice base class
Change-Id: I806143e9db21f0be4dcc6a376b3a630be7aeb3ba
2018-06-13 19:48:43 +02:00
Zydrunas Tamosevicius
70621b7484 lms: Reduce Rx gain from 47 to 34 dB
Initially, Rx gain was hardcoded to be 47. This was too high for our
setup and we were constantly getting "clipping detected" messages.

Reducing Rx gain to 34 solved the issue. However, it looks like gains
should be controlled through configuration files.

Change-Id: I30580f18c4ad630c09f725b1d24c125fc3119809
2018-06-12 00:36:49 +02:00
Zydrunas Tamosevicius
1568f87014 lms: fix LMS_StartStream() handling for multiple channels
LMS_StartStream() (in LMSDevice::start()) was moved to separate loop. It
is because LMS_SetupStream() would fail for second channel if streaming
has already been started (LMS_StartStream()) for single channel
configuration.

Change-Id: I6704bb92864aa81417507c4ae24a22f41dc529c1
2018-06-12 00:36:49 +02:00
Zydrunas Tamosevicius
cace86ec0d lms: Reduce log level of "send buffer of len ..."
Log level of "send buffer of len ..." messages was changed as it was
causing problems on some machines.

Change-Id: I605d50e81966c7bd169b27788d62af6fb54c84e1
2018-06-12 00:36:49 +02:00
Zydrunas Tamosevicius
59437da099 lms: Use same timestamp offset like when using LimeSDR via UHD
The tx timestamp offset was not set. We set it to the same value as it
was in UHD interface for LimeSDR

Change-Id: I78bc40cd575097f71a5f82b63467fa81c3f8d837
2018-06-12 00:36:45 +02:00
Pau Espin Pedrol
c9ea6e3e8c lms: Check LPBFW to set is within supported range
As of LimeSuite 618fbb9c3188b36d75ad5785a97b8887dcc468f6, it seems 5e6
is within the returned range, but LMS_SetLPFBW fails anyway.

See for more information: https://github.com/myriadrf/LimeSuite/issues/184

Change-Id: I967e7da7c0e3e8138b76733ee4a0e6311d20b62e
2018-05-29 19:00:32 +02:00
Pau Espin Pedrol
4b00e736f0 lms: Makefile.am: Reorder params to fix link issue
It seems the order in which static code and -lfoo is passed to the
linker matters.

This commit is a lms specific follow-up of commit
2a8183bdf0.

Change-Id: I59c20d268ecac4c22689124165c47295bd9176d4
2018-05-25 17:14:43 +02:00
Pau Espin Pedrol
aeccb44c7a LMSDevice: Fix setup failure with LimeSuite > 18.04.1
Fixes: https://github.com/myriadrf/LimeSuite/issues/184

Change-Id: Ia9f37995cd10d19d6820e3e12b8ee8f3efbff5d4
2018-05-25 17:14:43 +02:00
Pau Espin Pedrol
09aa5a3e9f LMSDevice: Set correct values for Max{Tx,Rx}Gain
Change-Id: I3b3a7080a69e15d8d6770186810d922227439099
2018-05-25 17:14:43 +02:00
Harald Welte
1a090b698c LMSDevice: Reduce Rx logging verbosity: Only log unexpected timestamps
Change-Id: I06b35efb7368616b9f4d348da574cd524ffe3ea6
2018-05-25 17:14:43 +02:00
Harald Welte
c01ddf5ff3 LMS_Device: Set ts_offset to 0.
I'm not quite sure what the ts_offset is for, but by using "0"
we are now receiving exactly the timestamp that we're expecting:

LMSDevice.cpp:486 [tid=140576250332928] chan 0 recv buffer of len 2500 expect 305ed0 got 305ed0 (305ed0) diff=0

Change-Id: I270c94945b1af9662cfc468cfda1ae3af3ac0a27
2018-05-25 17:14:43 +02:00
Harald Welte
380067eeea LMSDevice: Fix initial timestamp offset of 2500
ts_initial must not point to the timestamp of the first sample
in the last "flush" sample buffer, but to the first timestamp we
expect in the next buffer.

Change-Id: I23af62870544d4c6cf5f6e2d6578936603bceb91
2018-05-25 17:14:43 +02:00
Harald Welte
6d000ba2f7 LMSDevice: Set low-pass filters to smallest possible option
Rx 1.4 MHz, Tx 5MHz.  Both massively too wide for GSM, but there's
no smaller band-width available.

Change-Id: I9723c9a2ea77f65bfa9d796d7c44adc2417e89cf
2018-05-25 17:14:43 +02:00
Harald Welte
2b764c33e5 LMSDevice: Typo fix: s/Internal/External
Change-Id: Icacfe6da90a89c7f00d62c580948fb913998eaa7
2018-05-25 17:14:43 +02:00
Harald Welte
e0d2f507ea LMSDevice: Print sample rate range + actual sample rate after setting it
Change-Id: I19c1a5b2d2431b8d39e277244e313f6e559e4d25
2018-05-25 17:14:43 +02:00
Harald Welte
7ca30375c9 LMSDevice: Call LMS_Init() before setting sample rate
LMS_Init() will override basically all device settings with their
default value, including the sample rate.  We hence have to make sure
to call it before any other API function that changes the device config
such as sample rate, frequency, filter bandwidth, ...

Change-Id: I4cdbae8406b5e1e93da491e90f8bad41d4be748b
2018-05-25 17:14:43 +02:00
Harald Welte
ce092147ac update .gitignore to include osmo-trx-lms
Change-Id: I52efd2f71eb61baa80427ab9f7b518f17d514792
2018-05-25 17:14:43 +02:00
Pau Espin Pedrol
6437192bf3 build: Add support for LimeSuite device backend
Change-Id: I239e1b37263a62b374d84974c9347e3654072e87
2018-05-25 17:14:43 +02:00
Pau Espin Pedrol
cdbe1e7ce2 lms: Several improvements and compilation/runtime fixes
Continuation of initial work done on LimeSuite support from Harald.

Change-Id: Ib2fca81b76d027b08e2891056fa076d071597783
2018-05-25 17:14:43 +02:00
Harald Welte
4a5484c770 Initial work towards direct LimeSuite support in OsmoTRX
This is work in progress towards a direct LimeSuite driver in OsmoTRX,
bypassing the currently rather complex stack of wrappers by going
through UHD, SoapyUHD, SoapySDR and LimeSuite.

Change-Id: Iaef29c4c2585ef8c2f94866c9591919f538c1a2d
2018-05-25 17:14:43 +02:00
Vadim Yanitskiy
01eea0aa42 trx_vty.c: fix: use CONFIG_NODE as parent by default
There are some configuration nodes, which are handled by extenral
libraries, such as libosmoctrl. So, when switching back to the
parent node, this should be kept in mind.

Instead of aborting, let's got to the CONFIG_NODE by default.

Fixes: OS#3250
Change-Id: Ia0600a46d19825806e5aed9257b6c57c3907808b
2018-05-09 15:19:56 +07:00
Pau Espin Pedrol
55df1e43e3 UHDDevice: Fix setup failure with LimeSuite > 18.04.1
Fixes: https://github.com/myriadrf/LimeSuite/issues/184

Change-Id: I48ead8b8996981263297b66c0c7d3d0972261316
2018-05-08 20:49:04 +02:00
Pau Espin Pedrol
e9424e241f doc: examples: Add umtrx sample config
Change-Id: Id38de0bbbe75e5e6bbb0de2eecb7d1984786d528
2018-05-04 14:01:50 +00:00
16 changed files with 969 additions and 63 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@
*.la
Transceiver52M/osmo-trx-uhd
Transceiver52M/osmo-trx-usrp1
Transceiver52M/osmo-trx-lms
# tests
tests/CommonLibs/BitVectorTest

View File

@@ -53,6 +53,9 @@ extern "C" {
#define LOGC(category, level) \
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
#define LOGLV(category, level) \
Log(category, level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
/**
A C++ stream-based thread-safe logger.
This object is NOT the global logger;

View File

@@ -10,6 +10,12 @@ static const struct log_info_cat default_categories[] = {
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DLMS] = {
.name = "DLMS",
.description = "LimeSuite category",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
const struct log_info log_info = {

View File

@@ -5,4 +5,5 @@ extern const struct log_info log_info;
/* Debug Areas of the code */
enum {
DMAIN,
DLMS,
};

View File

@@ -503,7 +503,9 @@ static int trx_vty_go_parent(struct vty *vty)
vty->index_sub = NULL;
break;
default:
OSMO_ASSERT(0);
vty->node = CONFIG_NODE;
vty->index = NULL;
vty->index_sub = NULL;
}
return vty->node;

View File

@@ -99,3 +99,13 @@ osmo_trx_usrp1_LDADD = \
$(USRP_LIBS)
osmo_trx_usrp1_CPPFLAGS = $(AM_CPPFLAGS) $(USRP_CFLAGS)
endif
if DEVICE_LMS
bin_PROGRAMS += osmo-trx-lms
osmo_trx_lms_SOURCES = osmo-trx.cpp
osmo_trx_lms_LDADD = \
$(builddir)/device/lms/libdevice.la \
$(COMMON_LDADD) \
$(LMS_LIBS)
osmo_trx_lms_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
endif

View File

@@ -11,3 +11,7 @@ endif
if DEVICE_UHD
SUBDIRS += uhd
endif
if DEVICE_LMS
SUBDIRS += lms
endif

View File

@@ -0,0 +1,629 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "Logger.h"
#include "Threads.h"
#include "LMSDevice.h"
#include <lime/LimeSuite.h>
#include <osmocom/core/utils.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
using namespace std;
constexpr double LMSDevice::masterClockRate;
#define MAX_ANTENNA_LIST_SIZE 10
#define LMS_SAMPLE_RATE GSMRATE*32
#define GSM_CARRIER_BW 270000.0 /* 270kHz */
#define LMS_MIN_BW_SUPPORTED 2.5e6 /* 2.5mHz, minimum supported by LMS */
#define LMS_CALIBRATE_BW_HZ OSMO_MAX(GSM_CARRIER_BW, LMS_MIN_BW_SUPPORTED)
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths),
m_lms_dev(NULL)
{
LOG(INFO) << "creating LMS device...";
m_lms_stream_rx.resize(chans);
m_lms_stream_tx.resize(chans);
m_last_rx_underruns.resize(chans, 0);
m_last_rx_overruns.resize(chans, 0);
m_last_tx_underruns.resize(chans, 0);
m_last_tx_overruns.resize(chans, 0);
}
static void lms_log_callback(int lvl, const char *msg)
{
/* map lime specific log levels */
static const int lvl_map[5] = {
[0] = LOGL_FATAL,
[1] = LOGL_ERROR,
[2] = LOGL_NOTICE,
[3] = LOGL_INFO,
[4] = LOGL_DEBUG,
};
/* protect against future higher log level values (lower importance) */
if ((unsigned int) lvl >= ARRAY_SIZE(lvl_map))
lvl = ARRAY_SIZE(lvl_map)-1;
LOGLV(DLMS, lvl) << msg;
}
static void thread_enable_cancel(bool cancel)
{
cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
}
static void print_range(const char* name, lms_range_t *range)
{
LOG(DEBUG) << name << ": Min=" << range->min << " Max=" << range->max
<< " Step=" << range->step;
}
int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
{
//lms_info_str_t dev_str;
lms_info_str_t* info_list;
lms_range_t range_lpfbw_rx, range_lpfbw_tx, range_sr;
float_type sr_host, sr_rf, lpfbw_rx, lpfbw_tx;
uint16_t dac_val;
unsigned int i, n;
int rc;
LOG(INFO) << "Opening LMS device..";
LMS_RegisterLogHandler(&lms_log_callback);
if ((n = LMS_GetDeviceList(NULL)) < 0)
LOG(ERROR) << "LMS_GetDeviceList(NULL) failed";
LOG(DEBUG) << "Devices found: " << n;
if (n < 1)
return -1;
info_list = new lms_info_str_t[n];
if (LMS_GetDeviceList(info_list) < 0)
LOG(ERROR) << "LMS_GetDeviceList(info_list) failed";
for (i = 0; i < n; i++)
LOG(DEBUG) << "Device [" << i << "]: " << info_list[i];
rc = LMS_Open(&m_lms_dev, info_list[0], NULL);
if (rc != 0) {
LOG(ERROR) << "LMS_GetDeviceList() failed)";
delete [] info_list;
return -1;
}
delete [] info_list;
LOG(INFO) << "Init LMS device";
if (LMS_Init(m_lms_dev) != 0) {
LOG(ERROR) << "LMS_Init() failed";
return -1;
}
if (LMS_GetSampleRateRange(m_lms_dev, LMS_CH_RX, &range_sr))
goto out_close;
print_range("Sample Rate", &range_sr);
LOG(DEBUG) << "Setting sample rate to " << GSMRATE*tx_sps << " " << tx_sps;
if (LMS_SetSampleRate(m_lms_dev, GSMRATE*tx_sps, 32) < 0)
goto out_close;
if (LMS_GetSampleRate(m_lms_dev, LMS_CH_RX, 0, &sr_host, &sr_rf))
goto out_close;
LOG(DEBUG) << "Sample Rate: Host=" << sr_host << " RF=" << sr_rf;
/* FIXME: make this device/model dependent, like UHDDevice:dev_param_map! */
ts_offset = static_cast<TIMESTAMP>(8.9e-5 * GSMRATE * tx_sps); /* time * sample_rate */
switch (ref) {
case REF_INTERNAL:
LOG(DEBUG) << "Setting Internal clock reference";
/* Ugly API: Selecting clock source implicit by writing to VCTCXO DAC ?!? */
if (LMS_VCTCXORead(m_lms_dev, &dac_val) < 0)
goto out_close;
LOG(DEBUG) << "Setting VCTCXO to " << dac_val;
if (LMS_VCTCXOWrite(m_lms_dev, dac_val) < 0)
goto out_close;
break;
case REF_EXTERNAL:
LOG(DEBUG) << "Setting External clock reference to " << 10000000.0;
/* Assume an external 10 MHz reference clock */
if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, 10000000.0) < 0)
goto out_close;
break;
default:
LOG(ALERT) << "Invalid reference type";
goto out_close;
}
if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_rx))
goto out_close;
print_range("LPFBWRange Rx", &range_lpfbw_rx);
if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_tx))
goto out_close;
print_range("LPFBWRange Tx", &range_lpfbw_tx);
lpfbw_rx = OSMO_MIN(OSMO_MAX(1.4001e6, range_lpfbw_rx.min), range_lpfbw_rx.max);
lpfbw_tx = OSMO_MIN(OSMO_MAX(5.2e6, range_lpfbw_tx.min), range_lpfbw_tx.max);
LOG(DEBUG) << "LPFBW: Rx=" << lpfbw_rx << " Tx=" << lpfbw_tx;
if (!set_antennas()) {
LOG(ALERT) << "LMS antenna setting failed";
return -1;
}
/* Perform Rx and Tx calibration */
for (i=0; i<chans; i++) {
LOG(INFO) << "Setting LPFBW chan " << i;
if (LMS_SetLPFBW(m_lms_dev, LMS_CH_RX, i, lpfbw_rx) < 0)
goto out_close;
if (LMS_SetLPFBW(m_lms_dev, LMS_CH_TX, i, lpfbw_tx) < 0)
goto out_close;
LOG(INFO) << "Calibrating chan " << i;
if (LMS_Calibrate(m_lms_dev, LMS_CH_RX, i, LMS_CALIBRATE_BW_HZ, 0) < 0)
goto out_close;
if (LMS_Calibrate(m_lms_dev, LMS_CH_TX, i, LMS_CALIBRATE_BW_HZ, 0) < 0)
goto out_close;
}
samplesRead = 0;
samplesWritten = 0;
started = false;
return NORMAL;
out_close:
LOG(ALERT) << "Error in LMS open, closing: " << LMS_GetLastErrorMessage();
LMS_Close(m_lms_dev);
return -1;
}
bool LMSDevice::start()
{
LOG(INFO) << "starting LMS...";
unsigned int i;
/* configure the channels/streams */
for (i=0; i<chans; i++) {
if (LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, true) < 0)
return false;
if (LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, true) < 0)
return false;
// Set gains to midpoint
setTxGain((minTxGain() + maxTxGain()) / 2, i);
setRxGain((minRxGain() + maxRxGain()) / 2, i);
m_lms_stream_rx[i] = {};
m_lms_stream_rx[i].isTx = false;
m_lms_stream_rx[i].channel = i;
m_lms_stream_rx[i].fifoSize = 1024 * 1024;
m_lms_stream_rx[i].throughputVsLatency = 0.3;
m_lms_stream_rx[i].dataFmt = lms_stream_t::LMS_FMT_I16;
m_lms_stream_tx[i] = {};
m_lms_stream_tx[i].isTx = true;
m_lms_stream_tx[i].channel = i;
m_lms_stream_tx[i].fifoSize = 1024 * 1024;
m_lms_stream_tx[i].throughputVsLatency = 0.3;
m_lms_stream_tx[i].dataFmt = lms_stream_t::LMS_FMT_I16;
if (LMS_SetupStream(m_lms_dev, &m_lms_stream_rx[i]) < 0)
return false;
if (LMS_SetupStream(m_lms_dev, &m_lms_stream_tx[i]) < 0)
return false;
}
/* now start the streams in a second loop, as we can no longer call
* LMS_SetupStream() after LMS_StartStream() of the first stream */
for (i = 0; i < chans; i++) {
if (LMS_StartStream(&m_lms_stream_rx[i]) < 0)
return false;
if (LMS_StartStream(&m_lms_stream_tx[i]) < 0)
return false;
}
flush_recv(10);
started = true;
return true;
}
bool LMSDevice::stop()
{
unsigned int i;
if (!started)
return true;
for (i=0; i<chans; i++) {
LMS_StopStream(&m_lms_stream_tx[i]);
LMS_StopStream(&m_lms_stream_rx[i]);
LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, false);
LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, false);
}
return true;
}
double LMSDevice::maxTxGain()
{
return 73.0;
}
double LMSDevice::minTxGain()
{
return 0.0;
}
double LMSDevice::maxRxGain()
{
return 73.0;
}
double LMSDevice::minRxGain()
{
return 0.0;
}
double LMSDevice::setTxGain(double dB, size_t chan)
{
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return 0.0;
}
if (dB > maxTxGain())
dB = maxTxGain();
if (dB < minTxGain())
dB = minTxGain();
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
if (LMS_SetGaindB(m_lms_dev, LMS_CH_TX, chan, dB) < 0)
LOG(ERR) << "Error setting TX gain";
return dB;
}
double LMSDevice::setRxGain(double dB, size_t chan)
{
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return 0.0;
}
dB = 34.0;
if (dB > maxRxGain())
dB = maxRxGain();
if (dB < minRxGain())
dB = minRxGain();
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
if (LMS_SetGaindB(m_lms_dev, LMS_CH_RX, chan, dB) < 0)
LOG(ERR) << "Error setting RX gain";
return dB;
}
int LMSDevice::get_ant_idx(const std::string & name, bool dir_tx, size_t chan)
{
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
const char* c_name = name.c_str();
int num_names;
int i;
num_names = LMS_GetAntennaList(m_lms_dev, dir_tx, chan, name_list);
for (i = 0; i < num_names; i++) {
if (!strcmp(c_name, name_list[i]))
return i;
}
return -1;
}
bool LMSDevice::flush_recv(size_t num_pkts)
{
#define CHUNK 625
int len = CHUNK * tx_sps;
short *buffer = new short[len * 2];
int rc;
lms_stream_meta_t rx_metadata = {};
rx_metadata.flushPartialPacket = false;
rx_metadata.waitForTimestamp = false;
ts_initial = 0;
while (!ts_initial || (num_pkts-- > 0)) {
rc = LMS_RecvStream(&m_lms_stream_rx[0], &buffer[0], len, &rx_metadata, 100);
LOG(DEBUG) << "Flush: Recv buffer of len " << rc << " at " << std::hex << rx_metadata.timestamp;
if (rc != len) {
LOG(ALERT) << "LMS: Device receive timed out";
delete[] buffer;
return false;
}
ts_initial = rx_metadata.timestamp + len;
}
LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
delete[] buffer;
return true;
}
bool LMSDevice::setRxAntenna(const std::string & ant, size_t chan)
{
int idx;
if (chan >= rx_paths.size()) {
LOG(ALERT) << "Requested non-existent channel " << chan;
return false;
}
idx = get_ant_idx(ant, LMS_CH_RX, chan);
if (idx < 0) {
LOG(ALERT) << "Invalid Rx Antenna";
return false;
}
if (LMS_SetAntenna(m_lms_dev, LMS_CH_RX, chan, idx) < 0) {
LOG(ALERT) << "Unable to set Rx Antenna";
}
return true;
}
std::string LMSDevice::getRxAntenna(size_t chan)
{
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
int idx;
if (chan >= rx_paths.size()) {
LOG(ALERT) << "Requested non-existent channel " << chan;
return "";
}
idx = LMS_GetAntenna(m_lms_dev, LMS_CH_RX, chan);
if (idx < 0) {
LOG(ALERT) << "Error getting Rx Antenna";
return "";
}
if (LMS_GetAntennaList(m_lms_dev, LMS_CH_RX, chan, name_list) < idx) {
LOG(ALERT) << "Error getting Rx Antenna List";
return "";
}
return name_list[idx];
}
bool LMSDevice::setTxAntenna(const std::string & ant, size_t chan)
{
int idx;
if (chan >= tx_paths.size()) {
LOG(ALERT) << "Requested non-existent channel " << chan;
return false;
}
idx = get_ant_idx(ant, LMS_CH_TX, chan);
if (idx < 0) {
LOG(ALERT) << "Invalid Rx Antenna";
return false;
}
if (LMS_SetAntenna(m_lms_dev, LMS_CH_TX, chan, idx) < 0) {
LOG(ALERT) << "Unable to set Rx Antenna";
}
return true;
}
std::string LMSDevice::getTxAntenna(size_t chan)
{
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
int idx;
if (chan >= tx_paths.size()) {
LOG(ALERT) << "Requested non-existent channel " << chan;
return "";
}
idx = LMS_GetAntenna(m_lms_dev, LMS_CH_TX, chan);
if (idx < 0) {
LOG(ALERT) << "Error getting Tx Antenna";
return "";
}
if (LMS_GetAntennaList(m_lms_dev, LMS_CH_TX, chan, name_list) < idx) {
LOG(ALERT) << "Error getting Tx Antenna List";
return "";
}
return name_list[idx];
}
bool LMSDevice::requiresRadioAlign()
{
return false;
}
GSM::Time LMSDevice::minLatency() {
/* Empirical data from a handful of
relatively recent machines shows that the B100 will underrun when
the transmit threshold is reduced to a time of 6 and a half frames,
so we set a minimum 7 frame threshold. */
return GSM::Time(6,7);
}
// NOTE: Assumes sequential reads
int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
TIMESTAMP timestamp, bool * underrun, unsigned *RSSI)
{
int rc = 0;
unsigned int i;
lms_stream_status_t status;
lms_stream_meta_t rx_metadata = {};
rx_metadata.flushPartialPacket = false;
rx_metadata.waitForTimestamp = false;
rx_metadata.timestamp = 0;
if (bufs.size() != chans) {
LOG(ALERT) << "Invalid channel combination " << bufs.size();
return -1;
}
*overrun = false;
*underrun = false;
for (i = 0; i<chans; i++) {
thread_enable_cancel(false);
rc = LMS_RecvStream(&m_lms_stream_rx[i], bufs[i], len, &rx_metadata, 100);
if (timestamp != (TIMESTAMP)rx_metadata.timestamp)
LOG(ALERT) << "chan "<< i << " recv buffer of len " << rc << " expect " << std::hex << timestamp << " got " << std::hex << (TIMESTAMP)rx_metadata.timestamp << " (" << std::hex << rx_metadata.timestamp <<") diff=" << rx_metadata.timestamp - timestamp;
if (rc != len) {
LOG(ALERT) << "LMS: Device receive timed out";
}
if (LMS_GetStreamStatus(&m_lms_stream_rx[i], &status) == 0) {
if (status.underrun > m_last_rx_underruns[i])
*underrun = true;
m_last_rx_underruns[i] = status.underrun;
if (status.overrun > m_last_rx_overruns[i])
*overrun = true;
m_last_rx_overruns[i] = status.overrun;
}
thread_enable_cancel(true);
}
samplesRead += rc;
if (((TIMESTAMP) rx_metadata.timestamp) < timestamp)
rc = 0;
return rc;
}
int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
bool * underrun, unsigned long long timestamp,
bool isControl)
{
int rc;
unsigned int i;
lms_stream_status_t status;
lms_stream_meta_t tx_metadata = {};
tx_metadata.flushPartialPacket = false;
tx_metadata.waitForTimestamp = true;
tx_metadata.timestamp = timestamp - ts_offset; /* Shift Tx time by offset */
if (isControl) {
LOG(ERR) << "Control packets not supported";
return 0;
}
if (bufs.size() != chans) {
LOG(ALERT) << "Invalid channel combination " << bufs.size();
return -1;
}
*underrun = false;
for (i = 0; i<chans; i++) {
LOG(DEBUG) << "chan "<< i << " send buffer of len " << len << " timestamp " << std::hex << tx_metadata.timestamp;
thread_enable_cancel(false);
rc = LMS_SendStream(&m_lms_stream_tx[i], bufs[i], len, &tx_metadata, 100);
if (rc != len) {
LOG(ALERT) << "LMS: Device send timed out";
}
if (LMS_GetStreamStatus(&m_lms_stream_tx[i], &status) == 0) {
if (status.underrun > m_last_tx_underruns[i])
*underrun = true;
m_last_tx_underruns[i] = status.underrun;
}
thread_enable_cancel(true);
}
samplesWritten += rc;
return rc;
}
bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
{
return true;
}
bool LMSDevice::setTxFreq(double wFreq, size_t chan)
{
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return false;
}
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
LOG(ALERT) << "set Tx: " << wFreq << " failed!";
return false;
}
return true;
}
bool LMSDevice::setRxFreq(double wFreq, size_t chan)
{
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return false;
}
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
LOG(ALERT) << "set Rx: " << wFreq << " failed!";
return false;
}
return true;
}
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector < std::string > &tx_paths,
const std::vector < std::string > &rx_paths)
{
return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
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.
*/
#ifndef _LMS_DEVICE_H_
#define _LMS_DEVICE_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "radioDevice.h"
#include <sys/time.h>
#include <math.h>
#include <limits.h>
#include <string>
#include <iostream>
#include <lime/LimeSuite.h>
#define LIMESDR_TX_AMPL 0.3
/** A class to handle a LimeSuite supported device */
class LMSDevice:public RadioDevice {
private:
static constexpr double masterClockRate = 52.0e6;
lms_device_t *m_lms_dev;
std::vector<lms_stream_t> m_lms_stream_rx;
std::vector<lms_stream_t> m_lms_stream_tx;
std::vector<uint32_t> m_last_rx_underruns;
std::vector<uint32_t> m_last_rx_overruns;
std::vector<uint32_t> m_last_tx_underruns;
std::vector<uint32_t> m_last_tx_overruns;
double actualSampleRate; ///< the actual USRP sampling rate
unsigned long long samplesRead; ///< number of samples read from LMS
unsigned long long samplesWritten; ///< number of samples sent to LMS
bool started; ///< flag indicates LMS has started
bool skipRx; ///< set if LMS is transmit-only.
TIMESTAMP ts_initial, ts_offset;
double rxGain;
int get_ant_idx(const std::string & name, bool dir_tx, size_t chan);
bool flush_recv(size_t num_pkts);
public:
/** Object constructor */
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
/** Instantiate the LMS */
int open(const std::string &args, int ref, bool swap_channels);
/** Start the LMS */
bool start();
/** Stop the LMS */
bool stop();
/** Set priority not supported */
void setPriority(float prio = 0.5) {
}
enum TxWindowType getWindowType() {
return TX_WINDOW_LMS1;
}
/**
Read samples from the LMS.
@param buf preallocated buf to contain read result
@param len number of samples desired
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
@param timestamp The timestamp of the first samples to be read
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
@param RSSI The received signal strength of the read result
@return The number of samples actually read
*/
int readSamples(std::vector < short *>&buf, int len, bool * overrun,
TIMESTAMP timestamp = 0xffffffff, bool * underrun =
NULL, unsigned *RSSI = NULL);
/**
Write samples to the LMS.
@param buf Contains the data to be written.
@param len number of samples to write.
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
@param timestamp The timestamp of the first sample of the data buffer.
@param isControl Set if data is a control packet, e.g. a ping command
@return The number of samples actually written
*/
int writeSamples(std::vector < short *>&bufs, int len, bool * underrun,
TIMESTAMP timestamp = 0xffffffff, bool isControl =
false);
/** Update the alignment between the read and write timestamps */
bool updateAlignment(TIMESTAMP timestamp);
/** Set the transmitter frequency */
bool setTxFreq(double wFreq, size_t chan = 0);
/** Set the receiver frequency */
bool setRxFreq(double wFreq, size_t chan = 0);
/** Returns the starting write Timestamp*/
TIMESTAMP initialWriteTimestamp(void) {
return ts_initial;
}
/** Returns the starting read Timestamp*/
TIMESTAMP initialReadTimestamp(void) {
return ts_initial;
}
/** returns the full-scale transmit amplitude **/
double fullScaleInputValue() {
return(double) SHRT_MAX * LIMESDR_TX_AMPL;
}
/** returns the full-scale receive amplitude **/
double fullScaleOutputValue() {
return (double) SHRT_MAX;
}
/** sets the receive chan gain, returns the gain setting **/
double setRxGain(double dB, size_t chan = 0);
/** get the current receive gain */
double getRxGain(size_t chan = 0) {
return rxGain;
}
/** return maximum Rx Gain **/
double maxRxGain(void);
/** return minimum Rx Gain **/
double minRxGain(void);
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
/** return maximum Tx Gain **/
double maxTxGain(void);
/** return minimum Rx Gain **/
double minTxGain(void);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setRxAntenna(const std::string & ant, size_t chan = 0);
/* return the used RX path */
std::string getRxAntenna(size_t chan = 0);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setTxAntenna(const std::string & ant, size_t chan = 0);
/* return the used RX path */
std::string getTxAntenna(size_t chan = 0);
/** return whether user drives synchronization of Tx/Rx of USRP */
bool requiresRadioAlign();
/** return whether user drives synchronization of Tx/Rx of USRP */
virtual GSM::Time minLatency();
/** Return internal status values */
inline double getTxFreq(size_t chan = 0) {
return 0;
}
inline double getRxFreq(size_t chan = 0) {
return 0;
}
inline double getSampleRate() {
return actualSampleRate;
}
inline double numberRead() {
return samplesRead;
}
inline double numberWritten() {
return samplesWritten;
}
};
#endif // _LMS_DEVICE_H_

View File

@@ -0,0 +1,10 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
noinst_HEADERS = LMSDevice.h
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = LMSDevice.cpp

View File

@@ -19,6 +19,7 @@
#include <vector>
#include "GSMCommon.h"
#include "Logger.h"
extern "C" {
#include "config_defs.h"
@@ -39,7 +40,7 @@ class RadioDevice {
public:
/* Available transport bus types */
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED, TX_WINDOW_LMS1 };
/* Radio interface types */
enum InterfaceType {
@@ -57,6 +58,13 @@ class RadioDevice {
/** Initialize the USRP */
virtual int open(const std::string &args, int ref, bool swap_channels)=0;
RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chans, double offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chans), lo_offset(offset),
tx_paths(tx_paths), rx_paths(rx_paths)
{ }
virtual ~RadioDevice() { }
/** Start the USRP */
@@ -163,6 +171,39 @@ class RadioDevice {
virtual double numberRead()=0;
virtual double numberWritten()=0;
protected:
size_t tx_sps, rx_sps;
InterfaceType iface;
size_t chans;
double lo_offset;
std::vector<std::string> tx_paths, rx_paths;
bool set_antennas() {
unsigned int i;
for (i = 0; i < tx_paths.size(); i++) {
if (tx_paths[i] == "")
continue;
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i];
if (!setTxAntenna(tx_paths[i], i)) {
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i];
return false;
}
}
for (i = 0; i < rx_paths.size(); i++) {
if (rx_paths[i] == "")
continue;
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i];
if (!setRxAntenna(rx_paths[i], i)) {
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i];
return false;
}
}
LOG(INFO) << "Antennas configured successfully";
return true;
}
};
#endif

View File

@@ -282,16 +282,13 @@ private:
enum TxWindowType tx_window;
enum uhd_dev_type dev_type;
size_t tx_sps, rx_sps, chans;
double tx_rate, rx_rate;
double tx_gain_min, tx_gain_max;
double rx_gain_min, rx_gain_max;
double offset;
std::vector<double> tx_gains, rx_gains;
std::vector<double> tx_freqs, rx_freqs;
std::vector<std::string> tx_paths, rx_paths;
size_t tx_spp, rx_spp;
bool started;
@@ -307,7 +304,6 @@ private:
void init_gains();
void set_channels(bool swap);
void set_rates();
bool set_antennas();
bool parse_dev_type();
bool flush_recv(size_t num_pkts);
int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
@@ -319,7 +315,6 @@ private:
bool set_freq(double freq, size_t chan, bool tx);
Thread *async_event_thrd;
InterfaceType iface;
Mutex tune_lock;
};
@@ -366,22 +361,16 @@ static void thread_enable_cancel(bool cancel)
}
uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double offset,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths)
: tx_gain_min(0.0), tx_gain_max(0.0),
: RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths),
tx_gain_min(0.0), tx_gain_max(0.0),
rx_gain_min(0.0), rx_gain_max(0.0),
tx_spp(0), rx_spp(0),
started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
{
this->tx_sps = tx_sps;
this->rx_sps = rx_sps;
this->chans = chans;
this->offset = offset;
this->iface = iface;
this->tx_paths = tx_paths;
this->rx_paths = rx_paths;
}
uhd_device::~uhd_device()
@@ -458,33 +447,6 @@ void uhd_device::set_rates()
LOG(INFO) << "Rates configured for " << desc.str;
}
bool uhd_device::set_antennas()
{
unsigned int i;
for (i = 0; i < tx_paths.size(); i++) {
if (tx_paths[i] == "")
continue;
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i];
if (!setTxAntenna(tx_paths[i], i)) {
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i];
return false;
}
}
for (i = 0; i < rx_paths.size(); i++) {
if (rx_paths[i] == "")
continue;
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i];
if (!setRxAntenna(rx_paths[i], i)) {
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i];
return false;
}
}
LOG(INFO) << "Antennas configured successfully";
return true;
}
double uhd_device::setTxGain(double db, size_t chan)
{
if (iface == MULTI_ARFCN)
@@ -732,8 +694,8 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
}
} else if (dev_type == LIMESDR) {
for (size_t i = 0; i < chans; i++) {
usrp_dev->set_tx_bandwidth(5e6, i);
usrp_dev->set_rx_bandwidth(5e6, i);
usrp_dev->set_tx_bandwidth(5.2e6, i);
usrp_dev->set_rx_bandwidth(1.4001e6, i);
}
}
@@ -1086,8 +1048,8 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
uhd::tune_request_t treq(freq);
if (dev_type == UMTRX) {
if (offset != 0.0)
return uhd::tune_request_t(freq, offset);
if (lo_offset != 0.0)
return uhd::tune_request_t(freq, lo_offset);
// Don't use DSP tuning, because LMS6002D PLL steps are small enough.
// We end up with DSP tuning just for 2-3Hz, which is meaningless and
@@ -1099,10 +1061,10 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
treq.dsp_freq = 0.0;
return treq;
} else if (chans == 1) {
if (offset == 0.0)
if (lo_offset == 0.0)
return treq;
return uhd::tune_request_t(freq, offset);
return uhd::tune_request_t(freq, lo_offset);
} else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
LOG(ALERT) << chans << " channels unsupported";
return treq;
@@ -1585,9 +1547,9 @@ std::string smpl_buf::str_code(ssize_t code)
}
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double offset,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths)
{
return new uhd_device(tx_sps, rx_sps, iface, chans, offset, tx_paths, rx_paths);
return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}

View File

@@ -58,12 +58,15 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(size_t sps)
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths)
{
LOG(INFO) << "creating USRP device...";
this->sps = sps;
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps));
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps));
actualSampleRate = masterClockRate/decimRate;
rxGain = 0;
@@ -73,9 +76,9 @@ USRPDevice::USRPDevice(size_t sps)
* split sample rate Tx/Rx - 4/1 sps we need to need to
* compensate for advance rather than delay.
*/
if (sps == 1)
if (tx_sps == 1)
pingOffset = 272;
else if (sps == 4)
else if (tx_sps == 4)
pingOffset = 269 - 7500;
else
pingOffset = 0;
@@ -100,7 +103,7 @@ int USRPDevice::open(const std::string &, int, bool)
if (!skipRx) {
try {
m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
0, decimRate * sps, 1, -1,
0, decimRate * tx_sps, 1, -1,
usrp_standard_rx::FPGA_MODE_NORMAL,
1024, 16 * 8, rbf));
m_uRx->set_fpga_master_clock_freq(masterClockRate);
@@ -648,9 +651,9 @@ bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double offset,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths)
{
return new USRPDevice(tx_sps);
return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}

View File

@@ -48,7 +48,6 @@ private:
usrp_subdev_spec rxSubdevSpec;
usrp_subdev_spec txSubdevSpec;
int sps;
double actualSampleRate; ///< the actual USRP sampling rate
unsigned int decimRate; ///< the USRP decimation rate
@@ -96,7 +95,9 @@ private:
public:
/** Object constructor */
USRPDevice(size_t sps);
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
/** Instantiate the USRP */
int open(const std::string &, int, bool);
@@ -203,8 +204,6 @@ private:
inline double getSampleRate() { return actualSampleRate; }
inline double numberRead() { return samplesRead; }
inline double numberWritten() { return samplesWritten; }
std::vector<std::string> tx_paths, rx_paths;
};
#endif // _USRP_DEVICE_H_

View File

@@ -100,6 +100,11 @@ AC_ARG_WITH(usrp1, [
[enable USRP1 gnuradio based transceiver])
])
AC_ARG_WITH(lms, [
AS_HELP_STRING([--with-lms],
[enable LimeSuite gnuradio based transceiver])
])
AC_ARG_WITH(singledb, [
AS_HELP_STRING([--with-singledb],
[enable single daughterboard use on USRP1])
@@ -133,6 +138,10 @@ AS_IF([test "x$with_usrp1" = "xyes"], [
PKG_CHECK_MODULES(USRP, usrp >= 3.3)
])
AS_IF([test "x$with_lms" = "xyes"], [
PKG_CHECK_MODULES(LMS, LimeSuite)
])
AS_IF([test "x$with_uhd" != "xno"],[
PKG_CHECK_MODULES(UHD, uhd >= 003.011,
[AC_DEFINE(USE_UHD_3_11, 1, UHD version 3.11.0 or higher)],
@@ -184,6 +193,7 @@ CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports],
AM_CONDITIONAL(DEVICE_UHD, [test "x$with_uhd" != "xno"])
AM_CONDITIONAL(DEVICE_USRP1, [test "x$with_usrp1" = "xyes"])
AM_CONDITIONAL(DEVICE_LMS, [test "x$with_lms" = "xyes"])
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
@@ -211,6 +221,7 @@ AC_CONFIG_FILES([\
Transceiver52M/device/Makefile \
Transceiver52M/device/uhd/Makefile \
Transceiver52M/device/usrp1/Makefile \
Transceiver52M/device/lms/Makefile \
tests/Makefile \
tests/CommonLibs/Makefile \
tests/Transceiver52M/Makefile \

View File

@@ -0,0 +1,22 @@
log stderr
logging filter all 1
logging color 1
logging print category 1
logging timestamp 1
logging level all info
!
line vty
no login
!
trx
bind-ip 127.0.0.1
remote-ip 127.0.0.1
base-port 5700
dev-args addr=192.168.10.2,pa=NONE,pa_power_max_dbm=23,fifo_ctrl_window=0,status_port=12345
egprs disable
tx-sps 4
rx-sps 4
rssi-offset 38
rt-prio 18
chan 0
chan 1