mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-11-04 06:03:17 +00:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			whytek/ocs
			...
			pespin/lms
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					223a15c2b2 | 
							
								
								
									
										2
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								COPYING
									
									
									
									
									
								
							@@ -666,7 +666,7 @@ For more information on this, and how to apply and follow the GNU AGPL, see
 | 
			
		||||
=========================================================================
 | 
			
		||||
 | 
			
		||||
This marks the end of the AGPLv3 text.  The following text is appended to the
 | 
			
		||||
same file for convenience but constituting a distinct document, not part of the
 | 
			
		||||
same file for convience but constituting a distinct document, not part of the
 | 
			
		||||
actual AGPL text and not part of an attempt to create a deriviative work based
 | 
			
		||||
on the AGPLv3 text.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -232,7 +232,7 @@ template <class T> class Vector {
 | 
			
		||||
		assert(mStart+span<=mEnd);
 | 
			
		||||
		for (i = 0; i < span; i++, src++, dst++)
 | 
			
		||||
			*dst = *src;
 | 
			
		||||
		/*TODO if not non-trivially copiable type class, optimize:
 | 
			
		||||
		/*TODO if not non-trivially copyable type class, optimize:
 | 
			
		||||
		memcpy(dst,mStart,span*sizeof(T)); */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,17 +21,7 @@
 | 
			
		||||
 * See the COPYING file in the main directory for details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
/* If HAVE_GETTID, then "_GNU_SOURCE" may need to be defined to use gettid() */
 | 
			
		||||
#if HAVE_GETTID
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/syscall.h>
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
@@ -87,15 +77,3 @@ const struct log_info log_info = {
 | 
			
		||||
	.cat = default_categories,
 | 
			
		||||
	.num_cat = ARRAY_SIZE(default_categories),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pid_t my_gettid(void)
 | 
			
		||||
{
 | 
			
		||||
#if HAVE_GETTID
 | 
			
		||||
	return gettid();
 | 
			
		||||
#elif defined(LINUX) && defined(__NR_gettid)
 | 
			
		||||
	return (pid_t) syscall(__NR_gettid);
 | 
			
		||||
#else
 | 
			
		||||
	#pragma message ("use pid as tid")
 | 
			
		||||
	return getpid();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
@@ -18,12 +18,10 @@ enum {
 | 
			
		||||
	DDEVDRV,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pid_t my_gettid(void);
 | 
			
		||||
 | 
			
		||||
#define CLOGC(category, level, fmt, args...) do { \
 | 
			
		||||
	LOGP(category, level, "[tid=%ld] " fmt, (long int) my_gettid(), ##args);  \
 | 
			
		||||
	LOGP(category, level, "[tid=%lu] " fmt, pthread_self(), ##args);  \
 | 
			
		||||
} while(0)
 | 
			
		||||
 | 
			
		||||
#define CLOGCHAN(chan, category, level, fmt, args...) do { \
 | 
			
		||||
	LOGP(category, level, "[tid=%ld][chan=%zu] " fmt, (long int) my_gettid(), chan, ##args);  \
 | 
			
		||||
	LOGP(category, level, "[tid=%lu][chan=%lu] " fmt, pthread_self(), chan, ##args);  \
 | 
			
		||||
} while(0)
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@
 | 
			
		||||
 | 
			
		||||
static struct trx_ctx* g_trx_ctx;
 | 
			
		||||
 | 
			
		||||
const struct value_string clock_ref_names[] = {
 | 
			
		||||
static const struct value_string clock_ref_names[] = {
 | 
			
		||||
	{ REF_INTERNAL,	"internal" },
 | 
			
		||||
	{ REF_EXTERNAL,	"external" },
 | 
			
		||||
	{ REF_GPS,	"gpsdo" },
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@
 | 
			
		||||
#include "config_defs.h"
 | 
			
		||||
 | 
			
		||||
extern struct vty_app_info g_vty_info;
 | 
			
		||||
extern const struct value_string clock_ref_names[];
 | 
			
		||||
extern const struct value_string filler_names[];
 | 
			
		||||
 | 
			
		||||
/* Maximum number of physical RF channels */
 | 
			
		||||
 
 | 
			
		||||
@@ -77,20 +77,23 @@ class RadioDevice {
 | 
			
		||||
	@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 radio 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
 | 
			
		||||
  */
 | 
			
		||||
  virtual int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
                          TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0) = 0;
 | 
			
		||||
                          TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0,
 | 
			
		||||
                          unsigned *RSSI = 0) = 0;
 | 
			
		||||
  /**
 | 
			
		||||
        Write samples to the radio.
 | 
			
		||||
        @param buf Contains the data to be written.
 | 
			
		||||
        @param len number of samples to write.
 | 
			
		||||
        @param underrun Set if radio 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
 | 
			
		||||
  */
 | 
			
		||||
  virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
 | 
			
		||||
                           TIMESTAMP timestamp) = 0;
 | 
			
		||||
                           TIMESTAMP timestamp, bool isControl = false) = 0;
 | 
			
		||||
 | 
			
		||||
  /** Update the alignment between the read and write timestamps */
 | 
			
		||||
  virtual bool updateAlignment(TIMESTAMP timestamp)=0;
 | 
			
		||||
 
 | 
			
		||||
@@ -20,10 +20,6 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#include "trx_vty.h"
 | 
			
		||||
#include "Logger.h"
 | 
			
		||||
#include "Threads.h"
 | 
			
		||||
#include "LMSDevice.h"
 | 
			
		||||
@@ -48,69 +44,11 @@ using namespace std;
 | 
			
		||||
#define LMS_CALIBRATE_BW_HZ OSMO_MAX(GSM_CARRIER_BW, LMS_MIN_BW_SUPPORTED)
 | 
			
		||||
#define SAMPLE_BUF_SZ    (1 << 20) /* Size of Rx timestamp based Ring buffer, in bytes */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Device Name Prefixes as presented by LimeSuite API LMS_GetDeviceInfo(): */
 | 
			
		||||
#define LMS_DEV_SDR_USB_PREFIX_NAME "LimeSDR-USB"
 | 
			
		||||
#define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini"
 | 
			
		||||
#define LMS_DEV_NET_MICRO_PREFIX_NAME "LimeNET-Micro"
 | 
			
		||||
 | 
			
		||||
/* Device parameter descriptor */
 | 
			
		||||
struct dev_desc {
 | 
			
		||||
	/* Does LimeSuite allow switching the clock source for this device?
 | 
			
		||||
	 * LimeSDR-Mini does not have switches but needs soldering to select
 | 
			
		||||
	 * external/internal clock. Any call to LMS_SetClockFreq() will fail.
 | 
			
		||||
	 */
 | 
			
		||||
	bool clock_src_switchable;
 | 
			
		||||
	/* Does LimeSuite allow using REF_INTERNAL for this device?
 | 
			
		||||
	 * 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 */
 | 
			
		||||
	double rate_multiarfcn;
 | 
			
		||||
	/* Coefficient multiplied by TX sample rate in order to shift Tx time */
 | 
			
		||||
	double ts_offset_coef;
 | 
			
		||||
	/* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
 | 
			
		||||
	double ts_offset_coef_multiarfcn;
 | 
			
		||||
	/* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
 | 
			
		||||
	std::string name_prefix;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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" } },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
 | 
			
		||||
	const lms_dev_info_t* device_info = LMS_GetDeviceInfo(m_lms_dev);
 | 
			
		||||
 | 
			
		||||
	while (it != dev_param_map.end())
 | 
			
		||||
	{
 | 
			
		||||
		enum lms_dev_type dev_type = it->first;
 | 
			
		||||
		struct dev_desc desc = it->second;
 | 
			
		||||
 | 
			
		||||
		if (strncmp(device_info->deviceName, desc.name_prefix.c_str(), desc.name_prefix.length()) == 0) {
 | 
			
		||||
			LOGC(DDEV, INFO) << "Device identified as " << desc.name_prefix;
 | 
			
		||||
			return dev_type;
 | 
			
		||||
		}
 | 
			
		||||
		it++;
 | 
			
		||||
	}
 | 
			
		||||
	return LMS_DEV_UNKNOWN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
			
		||||
		     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)
 | 
			
		||||
{
 | 
			
		||||
	LOGC(DDEV, INFO) << "creating LMS device...";
 | 
			
		||||
 | 
			
		||||
@@ -120,11 +58,6 @@ LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t c
 | 
			
		||||
	tx_gains.resize(chans);
 | 
			
		||||
 | 
			
		||||
	rx_buffers.resize(chans);
 | 
			
		||||
 | 
			
		||||
	/* Set up per-channel Rx timestamp based Ring buffers */
 | 
			
		||||
	for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
			
		||||
		rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LMSDevice::~LMSDevice()
 | 
			
		||||
@@ -176,7 +109,7 @@ static void print_range(const char* name, lms_range_t *range)
 | 
			
		||||
int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::string &args)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i, j;
 | 
			
		||||
	std::vector<string> filters;
 | 
			
		||||
	vector<string> filters;
 | 
			
		||||
 | 
			
		||||
	filters = comma_delimited_to_vector(args.c_str());
 | 
			
		||||
 | 
			
		||||
@@ -200,11 +133,12 @@ int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::str
 | 
			
		||||
int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
{
 | 
			
		||||
	lms_info_str_t* info_list;
 | 
			
		||||
	const lms_dev_info_t* device_info;
 | 
			
		||||
	lms_range_t range_sr;
 | 
			
		||||
	float_type sr_host, sr_rf;
 | 
			
		||||
	unsigned int i, n;
 | 
			
		||||
	int rc, dev_id;
 | 
			
		||||
	struct dev_desc dev_desc;
 | 
			
		||||
	int sample_rate;
 | 
			
		||||
 | 
			
		||||
	LOGC(DDEV, INFO) << "Opening LMS device..";
 | 
			
		||||
 | 
			
		||||
@@ -241,20 +175,19 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
 | 
			
		||||
	delete [] info_list;
 | 
			
		||||
 | 
			
		||||
	m_dev_type = parse_dev_type(m_lms_dev);
 | 
			
		||||
	dev_desc = dev_param_map.at(m_dev_type);
 | 
			
		||||
	device_info = LMS_GetDeviceInfo(m_lms_dev);
 | 
			
		||||
 | 
			
		||||
	if ((ref != REF_EXTERNAL) && (ref != REF_INTERNAL)){
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Invalid reference type";
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* if reference clock is external, setup must happen _before_ calling LMS_Init */
 | 
			
		||||
	/* if reference clock is external setup must happen _before_ calling LMS_Init */
 | 
			
		||||
	/* FIXME make external reference frequency configurable */
 | 
			
		||||
	if (ref == REF_EXTERNAL) {
 | 
			
		||||
		LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
 | 
			
		||||
		/* FIXME: Assume an external 10 MHz reference clock. make
 | 
			
		||||
		   external reference frequency configurable */
 | 
			
		||||
		if (!do_clock_src_freq(REF_EXTERNAL, 10000000.0))
 | 
			
		||||
		/* Assume an external 10 MHz reference clock */
 | 
			
		||||
		if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, 10000000.0) < 0)
 | 
			
		||||
			goto out_close;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -264,13 +197,22 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* if reference clock is internal, setup must happen _after_ calling LMS_Init */
 | 
			
		||||
	/* LimeSDR-Mini does not have switches but needs soldering to select external/internal clock */
 | 
			
		||||
	/* LimeNET-Micro also does not like selecting internal clock*/
 | 
			
		||||
	/* also set device specific maximum tx levels selected by phasenoise measurements*/
 | 
			
		||||
	if (strncmp(device_info->deviceName,"LimeSDR-USB",11) == 0){
 | 
			
		||||
		/* if reference clock is internal setup must happen _after_ calling LMS_Init */
 | 
			
		||||
		/* according to lms using LMS_CLOCK_EXTREF with a frequency <= 0 is the correct way to set clock to internal reference*/
 | 
			
		||||
		if (ref == REF_INTERNAL) {
 | 
			
		||||
			LOGC(DDEV, INFO) << "Setting Internal clock reference";
 | 
			
		||||
		/* Internal freq param is not used */
 | 
			
		||||
		if (!do_clock_src_freq(REF_INTERNAL, 0))
 | 
			
		||||
			if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, -1) < 0)
 | 
			
		||||
				goto out_close;
 | 
			
		||||
		}
 | 
			
		||||
		maxTxGainClamp = 73.0;
 | 
			
		||||
	} else if (strncmp(device_info->deviceName,"LimeSDR-Mini",12) == 0)
 | 
			
		||||
		maxTxGainClamp = 66.0;
 | 
			
		||||
	else
 | 
			
		||||
		maxTxGainClamp = 71.0; /* "LimeNET-Micro", etc FIXME pciE based LMS boards?*/
 | 
			
		||||
 | 
			
		||||
	/* enable all used channels */
 | 
			
		||||
	for (i=0; i<chans; i++) {
 | 
			
		||||
@@ -285,22 +227,18 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	print_range("Sample Rate", &range_sr);
 | 
			
		||||
 | 
			
		||||
	if (iface == MULTI_ARFCN)
 | 
			
		||||
		sr_host = dev_desc.rate_multiarfcn * tx_sps;
 | 
			
		||||
	else
 | 
			
		||||
		sr_host = dev_desc.rate * tx_sps;
 | 
			
		||||
	LOGC(DDEV, INFO) << "Setting sample rate to " << sr_host << " " << tx_sps;
 | 
			
		||||
	if (LMS_SetSampleRate(m_lms_dev, sr_host, 32) < 0)
 | 
			
		||||
	sample_rate = (iface == MULTI_ARFCN ? MCBTS_SPACING : GSMRATE) * tx_sps;
 | 
			
		||||
 | 
			
		||||
	LOGC(DDEV, INFO) << "Setting sample rate to " << sample_rate << " " << tx_sps;
 | 
			
		||||
	if (LMS_SetSampleRate(m_lms_dev, sample_rate, 32) < 0)
 | 
			
		||||
		goto out_close;
 | 
			
		||||
 | 
			
		||||
	if (LMS_GetSampleRate(m_lms_dev, LMS_CH_RX, 0, &sr_host, &sr_rf))
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	LOGC(DDEV, INFO) << "Sample Rate: Host=" << sr_host << " RF=" << sr_rf;
 | 
			
		||||
 | 
			
		||||
	if (iface == MULTI_ARFCN)
 | 
			
		||||
		ts_offset = static_cast<TIMESTAMP>(dev_desc.ts_offset_coef_multiarfcn * sr_host);
 | 
			
		||||
	else
 | 
			
		||||
		ts_offset = static_cast<TIMESTAMP>(dev_desc.ts_offset_coef * sr_host);
 | 
			
		||||
	/* FIXME: make this device/model dependent, like UHDDevice:dev_param_map! */
 | 
			
		||||
	ts_offset = static_cast<TIMESTAMP>(8.9e-5 * sample_rate);
 | 
			
		||||
 | 
			
		||||
	/* configure antennas */
 | 
			
		||||
	if (!set_antennas()) {
 | 
			
		||||
@@ -308,6 +246,10 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Set up per-channel Rx timestamp based Ring buffers */
 | 
			
		||||
	for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
			
		||||
		rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
 | 
			
		||||
 | 
			
		||||
	return iface == MULTI_ARFCN ? MULTI_ARFCN : NORMAL;
 | 
			
		||||
 | 
			
		||||
out_close:
 | 
			
		||||
@@ -402,43 +344,6 @@ bool LMSDevice::stop()
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
 | 
			
		||||
{
 | 
			
		||||
	struct dev_desc dev_desc = dev_param_map.at(m_dev_type);
 | 
			
		||||
	size_t lms_clk_id;
 | 
			
		||||
 | 
			
		||||
	switch (ref) {
 | 
			
		||||
	case REF_EXTERNAL:
 | 
			
		||||
		lms_clk_id = LMS_CLOCK_EXTREF;
 | 
			
		||||
		break;
 | 
			
		||||
	case REF_INTERNAL:
 | 
			
		||||
		if (!dev_desc.clock_src_int_usable) {
 | 
			
		||||
			LOGC(DDEV, ERROR) << "Device type " << dev_desc.name_prefix
 | 
			
		||||
					  << " doesn't support internal reference clock";
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		/* According to lms using LMS_CLOCK_EXTREF with a
 | 
			
		||||
		   frequency <= 0 is the correct way to set clock to
 | 
			
		||||
		   internal reference */
 | 
			
		||||
		lms_clk_id = LMS_CLOCK_EXTREF;
 | 
			
		||||
		freq = -1;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Invalid reference type " << get_value_string(clock_ref_names, ref);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (dev_desc.clock_src_switchable) {
 | 
			
		||||
		if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0)
 | 
			
		||||
			return false;
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGC(DDEV, INFO) << "Device type " << dev_desc.name_prefix
 | 
			
		||||
				 << " doesn't support switching clock source through SW";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* do rx/tx calibration - depends on gain, freq and bw */
 | 
			
		||||
bool LMSDevice::do_calib(size_t chan)
 | 
			
		||||
{
 | 
			
		||||
@@ -480,7 +385,7 @@ bool LMSDevice::do_filters(size_t chan)
 | 
			
		||||
 | 
			
		||||
double LMSDevice::maxTxGain()
 | 
			
		||||
{
 | 
			
		||||
	return dev_param_map.at(m_dev_type).max_tx_gain;
 | 
			
		||||
	return maxTxGainClamp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double LMSDevice::minTxGain()
 | 
			
		||||
@@ -739,7 +644,7 @@ void LMSDevice::update_stream_stats_rx(size_t chan, bool *overrun)
 | 
			
		||||
 | 
			
		||||
// NOTE: Assumes sequential reads
 | 
			
		||||
int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
 | 
			
		||||
			   TIMESTAMP timestamp, bool * underrun)
 | 
			
		||||
			   TIMESTAMP timestamp, bool * underrun, unsigned *RSSI)
 | 
			
		||||
{
 | 
			
		||||
	int rc, num_smpls, expect_smpls;
 | 
			
		||||
	ssize_t avail_smpls;
 | 
			
		||||
@@ -858,7 +763,8 @@ void LMSDevice::update_stream_stats_tx(size_t chan, bool *underrun)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
 | 
			
		||||
			    bool * underrun, unsigned long long timestamp)
 | 
			
		||||
			    bool * underrun, unsigned long long timestamp,
 | 
			
		||||
			    bool isControl)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
@@ -867,6 +773,11 @@ int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
 | 
			
		||||
	tx_metadata.waitForTimestamp = true;
 | 
			
		||||
	tx_metadata.timestamp = timestamp - ts_offset;	/* Shift Tx time by offset */
 | 
			
		||||
 | 
			
		||||
	if (isControl) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Control packets not supported";
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bufs.size() != chans) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Invalid channel combination " << bufs.size();
 | 
			
		||||
		return -1;
 | 
			
		||||
 
 | 
			
		||||
@@ -41,13 +41,6 @@
 | 
			
		||||
 * 	A^2 = 1 */
 | 
			
		||||
#define LIMESDR_TX_AMPL  0.707
 | 
			
		||||
 | 
			
		||||
enum lms_dev_type {
 | 
			
		||||
	LMS_DEV_SDR_USB,   /* LimeSDR-USB */
 | 
			
		||||
	LMS_DEV_SDR_MINI,  /* LimeSDR-Mini */
 | 
			
		||||
	LMS_DEV_NET_MICRO, /* LimeNet-micro */
 | 
			
		||||
	LMS_DEV_UNKNOWN,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** A class to handle a LimeSuite supported device */
 | 
			
		||||
class LMSDevice:public RadioDevice {
 | 
			
		||||
 | 
			
		||||
@@ -66,8 +59,7 @@ private:
 | 
			
		||||
	TIMESTAMP ts_initial, ts_offset;
 | 
			
		||||
 | 
			
		||||
	std::vector<double> tx_gains, rx_gains;
 | 
			
		||||
 | 
			
		||||
	enum lms_dev_type m_dev_type;
 | 
			
		||||
	double maxTxGainClamp;
 | 
			
		||||
 | 
			
		||||
	bool do_calib(size_t chan);
 | 
			
		||||
	bool do_filters(size_t chan);
 | 
			
		||||
@@ -76,7 +68,6 @@ private:
 | 
			
		||||
	bool flush_recv(size_t num_pkts);
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
@@ -106,21 +97,24 @@ public:
 | 
			
		||||
	@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);
 | 
			
		||||
			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);
 | 
			
		||||
			 TIMESTAMP timestamp = 0xffffffff, bool isControl =
 | 
			
		||||
			 false);
 | 
			
		||||
 | 
			
		||||
	/** Update the alignment between the read and write timestamps */
 | 
			
		||||
	bool updateAlignment(TIMESTAMP timestamp);
 | 
			
		||||
 
 | 
			
		||||
@@ -121,8 +121,6 @@ static const std::map<dev_key, dev_desc> dev_param_map {
 | 
			
		||||
	{ std::make_tuple(UMTRX, 4, 4), { 2, 0.0,  GSMRATE, 5.1503e-5,  "UmTRX 4 SPS"        } },
 | 
			
		||||
	{ std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS"  } },
 | 
			
		||||
	{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
 | 
			
		||||
	{ std::make_tuple(OCR01, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "OCR01 4/1 Tx/Rx SPS"} },
 | 
			
		||||
	{ std::make_tuple(OCR01, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "OCR01 4/4 Tx/Rx SPS"} },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void *async_event_loop(uhd_device *dev)
 | 
			
		||||
@@ -370,7 +368,6 @@ bool uhd_device::parse_dev_type()
 | 
			
		||||
		{ "USRP2",    { USRP2,   TX_WINDOW_FIXED } },
 | 
			
		||||
		{ "UmTRX",    { UMTRX,   TX_WINDOW_FIXED } },
 | 
			
		||||
		{ "LimeSDR",  { LIMESDR, TX_WINDOW_FIXED } },
 | 
			
		||||
		{ "OCR01",    { OCR01,   TX_WINDOW_USRP1 } },
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Compare UHD motherboard and device strings */
 | 
			
		||||
@@ -412,7 +409,7 @@ static bool uhd_e3xx_version_chk()
 | 
			
		||||
void uhd_device::set_channels(bool swap)
 | 
			
		||||
{
 | 
			
		||||
	if (iface == MULTI_ARFCN) {
 | 
			
		||||
		if (dev_type != B200 && dev_type != B210 && dev_type != OCR01)
 | 
			
		||||
		if (dev_type != B200 && dev_type != B210)
 | 
			
		||||
			throw std::invalid_argument("Device does not support MCBTS");
 | 
			
		||||
		dev_type = B2XX_MCBTS;
 | 
			
		||||
	}
 | 
			
		||||
@@ -424,7 +421,6 @@ void uhd_device::set_channels(bool swap)
 | 
			
		||||
	switch (dev_type) {
 | 
			
		||||
	case B210:
 | 
			
		||||
	case E3XX:
 | 
			
		||||
	case OCR01:
 | 
			
		||||
		if (chans == 1)
 | 
			
		||||
			subdev_string = swap ? "A:B" : "A:A";
 | 
			
		||||
		else if (chans == 2)
 | 
			
		||||
@@ -586,7 +582,6 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
	case E1XX:
 | 
			
		||||
	case E3XX:
 | 
			
		||||
	case LIMESDR:
 | 
			
		||||
	case OCR01:
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
@@ -726,7 +721,7 @@ int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
			    TIMESTAMP timestamp, bool *underrun)
 | 
			
		||||
			    TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
 | 
			
		||||
{
 | 
			
		||||
	ssize_t rc;
 | 
			
		||||
	uhd::time_spec_t ts;
 | 
			
		||||
@@ -809,7 +804,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
 | 
			
		||||
			     unsigned long long timestamp)
 | 
			
		||||
			     unsigned long long timestamp,bool isControl)
 | 
			
		||||
{
 | 
			
		||||
	uhd::tx_metadata_t metadata;
 | 
			
		||||
	metadata.has_time_spec = true;
 | 
			
		||||
@@ -819,6 +814,12 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
 | 
			
		||||
 | 
			
		||||
	*underrun = false;
 | 
			
		||||
 | 
			
		||||
	// No control packets
 | 
			
		||||
	if (isControl) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Control packets not supported";
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bufs.size() != chans) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
 | 
			
		||||
		return -1;
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,6 @@ enum uhd_dev_type {
 | 
			
		||||
	X3XX,
 | 
			
		||||
	UMTRX,
 | 
			
		||||
	LIMESDR,
 | 
			
		||||
	OCR01,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -75,10 +74,10 @@ public:
 | 
			
		||||
	enum TxWindowType getWindowType() { return tx_window; }
 | 
			
		||||
 | 
			
		||||
	int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
			TIMESTAMP timestamp, bool *underrun);
 | 
			
		||||
			TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
 | 
			
		||||
 | 
			
		||||
	int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
 | 
			
		||||
			 TIMESTAMP timestamp);
 | 
			
		||||
			 TIMESTAMP timestamp, bool isControl);
 | 
			
		||||
 | 
			
		||||
	bool updateAlignment(TIMESTAMP timestamp);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -365,7 +365,7 @@ GSM::Time USRPDevice::minLatency() {
 | 
			
		||||
 | 
			
		||||
// NOTE: Assumes sequential reads
 | 
			
		||||
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
                            TIMESTAMP timestamp, bool *underrun)
 | 
			
		||||
                            TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
 | 
			
		||||
{
 | 
			
		||||
#ifndef SWLOOPBACK
 | 
			
		||||
  if (!m_uRx)
 | 
			
		||||
@@ -433,10 +433,8 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
	*underrun = true;
 | 
			
		||||
	LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
 | 
			
		||||
      }
 | 
			
		||||
#if 0
 | 
			
		||||
      /* FIXME: Do something with this ? */
 | 
			
		||||
      unsigned RSSI = (word0 >> 21) & 0x3f;
 | 
			
		||||
#endif
 | 
			
		||||
      if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
 | 
			
		||||
 | 
			
		||||
      if (!isAligned) continue;
 | 
			
		||||
 | 
			
		||||
      unsigned cursorStart = pktTimestamp - timeStart + dataStart;
 | 
			
		||||
@@ -515,8 +513,9 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int USRPDevice::writeSamplesControl(std::vector<short *> &bufs, int len,
 | 
			
		||||
                             bool *underrun, unsigned long long timestamp, bool isControl)
 | 
			
		||||
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
 | 
			
		||||
                             bool *underrun, unsigned long long timestamp,
 | 
			
		||||
                             bool isControl)
 | 
			
		||||
{
 | 
			
		||||
  writeLock.lock();
 | 
			
		||||
 | 
			
		||||
@@ -570,12 +569,6 @@ int USRPDevice::writeSamplesControl(std::vector<short *> &bufs, int len,
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
 | 
			
		||||
                             bool *underrun, unsigned long long timestamp)
 | 
			
		||||
{
 | 
			
		||||
  return writeSamplesControl(bufs, len, underrun, timestamp, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
 | 
			
		||||
{
 | 
			
		||||
#ifndef SWLOOPBACK
 | 
			
		||||
@@ -585,7 +578,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
 | 
			
		||||
  bool tmpUnderrun;
 | 
			
		||||
 | 
			
		||||
  std::vector<short *> buf(1, data);
 | 
			
		||||
  if (writeSamplesControl(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
 | 
			
		||||
  if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
 | 
			
		||||
    pingTimestamp = timestamp;
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -82,9 +82,6 @@ private:
 | 
			
		||||
  double rxGain;
 | 
			
		||||
  double txGain;
 | 
			
		||||
 | 
			
		||||
  int writeSamplesControl(std::vector<short *> &bufs, int len, bool *underrun,
 | 
			
		||||
                   TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
 | 
			
		||||
 | 
			
		||||
#ifdef SWLOOPBACK
 | 
			
		||||
  short loopbackBuffer[1000000];
 | 
			
		||||
  int loopbackBufferSize;
 | 
			
		||||
@@ -120,20 +117,23 @@ private:
 | 
			
		||||
	@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 USRP 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);
 | 
			
		||||
                  TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
 | 
			
		||||
                  unsigned *RSSI = NULL);
 | 
			
		||||
  /**
 | 
			
		||||
        Write samples to the USRP.
 | 
			
		||||
        @param buf Contains the data to be written.
 | 
			
		||||
        @param len number of samples to write.
 | 
			
		||||
        @param underrun Set if USRP 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);
 | 
			
		||||
                   TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
 | 
			
		||||
 | 
			
		||||
  /** Update the alignment between the read and write timestamps */
 | 
			
		||||
  bool updateAlignment(TIMESTAMP timestamp);
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ void signalVector::operator=(const signalVector& vector)
 | 
			
		||||
	complex *src = vector.mData;
 | 
			
		||||
	for (i = 0; i < size(); i++, src++, dst++)
 | 
			
		||||
		*dst = *src;
 | 
			
		||||
	/* TODO: optimize for non non-trivially copiable types: */
 | 
			
		||||
	/* TODO: optimize for non non-trivially copyable types: */
 | 
			
		||||
	/*memcpy(mData, vector.mData, bytes()); */
 | 
			
		||||
	mStart = mData + vector.getStart();
 | 
			
		||||
}
 | 
			
		||||
@@ -70,7 +70,7 @@ size_t signalVector::updateHistory()
 | 
			
		||||
	complex *src = mStart + this->size() - num;
 | 
			
		||||
	for (i = 0; i < num; i++, src++, dst++)
 | 
			
		||||
		*dst = *src;
 | 
			
		||||
	/* TODO: optimize for non non-trivially copiable types: */
 | 
			
		||||
	/* TODO: optimize for non non-trivially copyable types: */
 | 
			
		||||
	/*memmove(mData, mStart + this->size() - num, num * sizeof(complex)); */
 | 
			
		||||
 | 
			
		||||
	return num;
 | 
			
		||||
 
 | 
			
		||||
@@ -75,15 +75,6 @@ AC_TYPE_SIZE_T
 | 
			
		||||
AC_HEADER_TIME
 | 
			
		||||
AC_C_BIGENDIAN
 | 
			
		||||
 | 
			
		||||
# Check if gettid is available (despite not being documented in glibc doc, it requires __USE_GNU on some systems)
 | 
			
		||||
# C compiler is used since __USE_GNU seems to be always defined for g++.
 | 
			
		||||
save_CPPFLAGS=$CPPFLAGS
 | 
			
		||||
AC_LANG_PUSH(C)
 | 
			
		||||
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
 | 
			
		||||
AC_CHECK_FUNCS([gettid])
 | 
			
		||||
AC_LANG_POP(C)
 | 
			
		||||
CPPFLAGS=$save_CPPFLAGS
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.3.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.3.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.3.0)
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,15 @@ mychroot() {
 | 
			
		||||
        mychroot_nocwd -w / "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
base="$PWD"
 | 
			
		||||
deps="$base/deps"
 | 
			
		||||
inst="$deps/install"
 | 
			
		||||
export deps inst
 | 
			
		||||
 | 
			
		||||
if [ -z "${INSIDE_CHROOT}" ]; then
 | 
			
		||||
 | 
			
		||||
        osmo-clean-workspace.sh
 | 
			
		||||
 | 
			
		||||
        # Only use ARM chroot if host is not ARM and the target is ARM:
 | 
			
		||||
        if ! $(substr "arm" "$(uname -m)") && [ "x${INSTR}" = "x--with-neon" -o "x${INSTR}" = "x--with-neon-vfpv4" ]; then
 | 
			
		||||
 | 
			
		||||
@@ -62,20 +69,6 @@ if [ -z "${INSIDE_CHROOT}" ]; then
 | 
			
		||||
        fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
set -ex
 | 
			
		||||
 | 
			
		||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
 | 
			
		||||
	echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
 | 
			
		||||
	exit 2
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
base="$PWD"
 | 
			
		||||
deps="$base/deps"
 | 
			
		||||
inst="$deps/install"
 | 
			
		||||
export deps inst
 | 
			
		||||
 | 
			
		||||
osmo-clean-workspace.sh
 | 
			
		||||
 | 
			
		||||
mkdir "$deps" || true
 | 
			
		||||
 | 
			
		||||
osmo-build-dep.sh libosmocore "" "--enable-sanitize --disable-doxygen --disable-pcsc"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user