This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
Xavier Zu gerrit-no-reply at lists.osmocom.orgXavier Zu has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-trx/+/19548 ) Change subject: Added PCIeSDR support ...................................................................... Added PCIeSDR support Change-Id: I5b49b61d1c8f357e33acc066f44d6c7acfc3ba79 --- M Transceiver52M/Makefile.am M Transceiver52M/device/Makefile.am A Transceiver52M/device/pciesdr/Makefile.am A Transceiver52M/device/pciesdr/PCIESDRDevice.cpp A Transceiver52M/device/pciesdr/PCIESDRDevice.h M configure.ac A doc/examples/osmo-trx-pciesdr/osmo-trx-pciesdr.cfg 7 files changed, 897 insertions(+), 0 deletions(-) git pull ssh://gerrit.osmocom.org:29418/osmo-trx refs/changes/48/19548/1 diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am index ade4e30..6d4dad2 100644 --- a/Transceiver52M/Makefile.am +++ b/Transceiver52M/Makefile.am @@ -104,3 +104,12 @@ $(LMS_LIBS) osmo_trx_lms_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS) endif + +if DEVICE_PCIESDR +bin_PROGRAMS += osmo-trx-pciesdr +osmo_trx_pciesdr_SOURCES = osmo-trx.cpp +osmo_trx_pciesdr_LDADD = \ + $(builddir)/device/pciesdr/libdevice.la \ + $(COMMON_LDADD) +osmo_trx_pciesdr_CPPFLAGS = $(AM_CPPFLAGS) +endif diff --git a/Transceiver52M/device/Makefile.am b/Transceiver52M/device/Makefile.am index 369e877..36366e3 100644 --- a/Transceiver52M/device/Makefile.am +++ b/Transceiver52M/device/Makefile.am @@ -13,3 +13,7 @@ if DEVICE_LMS SUBDIRS += lms endif + +if DEVICE_PCIESDR +SUBDIRS += pciesdr +endif diff --git a/Transceiver52M/device/pciesdr/Makefile.am b/Transceiver52M/device/pciesdr/Makefile.am new file mode 100644 index 0000000..6f7ad1b --- /dev/null +++ b/Transceiver52M/device/pciesdr/Makefile.am @@ -0,0 +1,11 @@ +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common +AM_CXXFLAGS = -lpthread -lsdr $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) + +noinst_HEADERS = PCIESDRDevice.h + +noinst_LTLIBRARIES = libdevice.la + +libdevice_la_SOURCES = PCIESDRDevice.cpp +libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la diff --git a/Transceiver52M/device/pciesdr/PCIESDRDevice.cpp b/Transceiver52M/device/pciesdr/PCIESDRDevice.cpp new file mode 100644 index 0000000..8dd0b1d --- /dev/null +++ b/Transceiver52M/device/pciesdr/PCIESDRDevice.cpp @@ -0,0 +1,642 @@ +/* +* Copyright 2018 sysmocom - s.f.m.c. GmbH +* +* SPDX-License-Identifier: AGPL-3.0+ +* + 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 <map> + +#include "trx_vty.h" +#include "Logger.h" +#include "Threads.h" +#include "PCIESDRDevice.h" +#include "Utils.h" + +#define LIBSDR_HAS_MSDR_CONVERT +extern "C" { +#include "libsdr.h" +} + +extern "C" { +#include "../../arch/common/convert.h" +} + +extern "C" { +#include "osmo_signal.h" +#include <osmocom/core/utils.h> +} + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +using namespace std; + +#define PSAMPLES_NUM 4096 + +/* Size of Rx / Tx timestamp based Ring buffer, in bytes */ +#define SAMPLE_BUF_SZ (1 << 20) + +/* greatest common divisor */ +static long gcd(long a, long b) +{ + if (a == 0) + return b; + else if (b == 0) + return a; + + if (a < b) + return gcd(a, b % a); + else + return gcd(b, a % b); +} + +PCIESDRDevice::PCIESDRDevice(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) +{ + LOGC(DDEV, INFO) << "creating PCIESDR device..."; + + /* The parameter dma_buffer_len is significant for functionality of the Rx chain */ + dma_buffer_count = 10; + dma_buffer_len = 1000; + + device = NULL; + + 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)); + +} + +PCIESDRDevice::~PCIESDRDevice() +{ + LOGC(DDEV, INFO) << "Closing PCIESDR device"; + if (device) { + msdr_close(device); + device = NULL; + } + + for (size_t i = 0; i < rx_buffers.size(); i++) + delete rx_buffers[i]; +} + +static int parse_config(const char* line, const char* argument, int default_value) +{ + const char* arg_found = strstr(line, argument); + if (!arg_found) + return default_value; + + const char* qe_pos = strchr(arg_found, '='); + if (!qe_pos) + return default_value; + + int res = strtol(qe_pos + 1, NULL, 10); + if (res == 0 && errno) + return default_value; + + return res; +} + +int PCIESDRDevice::open(const std::string &args, int ref, bool swap_channels) +{ + int lb_param = parse_config(args.c_str(), "loopback", 0); + char pciesdr_name[500]; + const char* lend = strchr(args.c_str(), ','); + int len = (lend) ? (lend - args.c_str()) : sizeof(pciesdr_name) - 1; + + LOGC(DDEV, INFO) << "Opening PCIESDR device.."; + + strncpy(pciesdr_name, args.c_str(), len); + pciesdr_name[len] = 0; + started = false; + + if (lb_param) { + LOGC(DDEV, ERROR) << "PCIESDR LOOPBACK mode is not supported!"; + } + LOGC(DDEV, INFO) << "pciesdr_name" << pciesdr_name << ""; + device = msdr_open(pciesdr_name); + if (device == NULL) { + LOGC(DDEV, ERROR) << "PCIESDR creating failed, device " << pciesdr_name << ""; + return -1; + } + + msdr_set_default_start_params(device, &StartParams); + /* RF interface */ + StartParams.interface_type = SDR_INTERFACE_RF; + /* no time synchronisation */ + StartParams.sync_source = SDR_SYNC_NONE; + /* sync on internal PPS */ + //StartParams.sync_source = SDR_SYNC_INTERNAL; + + /* calculate sample rate using Euclidean algorithm */ + double rate = (double)GSMRATE*tx_sps; + double integral = floor(rate); + double frac = rate - integral; + /* This is the accuracy */ + const long precision = 1000000000; + long gcd_ = gcd(round(frac * precision), precision); + long denominator = precision / gcd_; + long numerator = round(frac * precision) / gcd_; + StartParams.sample_rate_num[0] = (int64_t)(integral * denominator + numerator); + StartParams.sample_rate_den[0] = (int64_t)denominator; + actualSampleRate = (double)StartParams.sample_rate_num[0] / (double)StartParams.sample_rate_den[0]; + StartParams.rx_bandwidth[0] = actualSampleRate * 0.75; + StartParams.tx_bandwidth[0] = actualSampleRate * 0.75; + LOGC(DDEV, INFO) << "PCIESDR device txsps:" << tx_sps << " rxsps:" << rx_sps + << " GSMRATE * tx_sps:" << (double)GSMRATE * tx_sps; + LOGC(DDEV, INFO) << "PCIESDR sample_rate_num:" << StartParams.sample_rate_num[0] + << " sample_rate_den:" << StartParams.sample_rate_den[0] + << " BW:" << StartParams.rx_bandwidth[0]; + + switch (ref) { + case REF_INTERNAL: + LOGC(DDEV, INFO) << "Setting Internal clock reference"; + /* internal clock, using PPS to correct it */ + StartParams.clock_source = SDR_CLOCK_INTERNAL; + break; + default: + LOGC(DDEV, ERROR) << "Invalid reference type"; + goto out_close; + } + /* complex float32 */ + StartParams.rx_sample_fmt = SDR_SAMPLE_FMT_CF32; + StartParams.tx_sample_fmt = SDR_SAMPLE_FMT_CF32; + /* choose best format fitting the bandwidth */ + StartParams.rx_sample_hw_fmt = SDR_SAMPLE_HW_FMT_AUTO; + StartParams.tx_sample_hw_fmt = SDR_SAMPLE_HW_FMT_AUTO; + StartParams.rx_channel_count = 1; + StartParams.tx_channel_count = 1; + StartParams.rx_freq[0] = 1550e6; + StartParams.tx_freq[0] = 1500e6; + StartParams.rx_gain[0] = 60; + StartParams.tx_gain[0] = 40; + StartParams.rx_antenna[0] = SDR_RX_ANTENNA_RX; + StartParams.rf_port_count = 1; + StartParams.tx_port_channel_count[0] = 1; + StartParams.rx_port_channel_count[0] = 1; + /* if != 0, set a custom DMA buffer configuration. Otherwise the default is 150 buffers per 10 ms */ + StartParams.dma_buffer_count = dma_buffer_count; + /* in samples */ + StartParams.dma_buffer_len = dma_buffer_len; + + /* FIXME: estimate it properly */ + /* PCIe radio should have this close to zero */ + /* The MS can connect if the value is between -30 and +5 */ + ts_offset = -16; + + started = false; + return NORMAL; + +out_close: + LOGC(DDEV, FATAL) << "Error in PCIESDR open, closing"; + msdr_close(device); + device = NULL; + + return -1; +} + +bool PCIESDRDevice::start() +{ + SDRStats stats; + int res; + + LOGC(DDEV, INFO) << "starting PCIESDR..."; + + if (started) { + LOGC(DDEV, ERROR) << "Device already started"; + return false; + } + LOGC(DDEV, INFO) << "starting PCIESDR..., sample rate:" << actualSampleRate; + res = msdr_start(device, &StartParams); + if (res) { + LOGC(DDEV, ERROR) << "msdr_start failed:"<< res; + return false; + } + res = msdr_get_stats(device, &stats); + if (res != 0) { + LOGC(DDEV, ERROR) << "PCIESDRDevice start: get_stats failed:" << res; + } else { + tx_underflow = stats.tx_underflow_count; + rx_overflow = stats.rx_overflow_count; + } + + flush_recv(); + + started = true; + return true; +} + +bool PCIESDRDevice::stop() +{ + int res; + + LOGC(DDEV, INFO) << "PCIESDRDevice stop"; + + if (started) { + res = msdr_stop(device); + if (res) { + LOGC(DDEV, ERROR) << "PCIESDR stop failed res: " << res; + } else { + LOGC(DDEV, INFO) << "PCIESDR stopped"; + started = false; + } + } + + return true; +} + +double PCIESDRDevice::maxTxGain() +{ + return 90; +} + +double PCIESDRDevice::maxRxGain() +{ + return 50; +} + +double PCIESDRDevice::minRxGain() +{ + return 0; +} + +double PCIESDRDevice::getTxGain(size_t chan) +{ + if (chan) { + LOGC(DDEV, ERROR) << "Invalid channel " << chan; + return 0.0; + } + + return msdr_get_tx_gain(device, chan); +} + +double PCIESDRDevice::setTxGain(double dB, size_t chan) +{ + int res = 0; + + if (chan) { + LOGC(DDEV, ERROR) << "Invalid channel " << chan; + return 0.0; + } + + LOGC(DDEV, INFO) << "Setting TX gain to " << dB << " dB. device:" << device << " chan:" << chan; + StartParams.tx_gain[chan] = dB; + + if (started) { + res = msdr_set_tx_gain(device, chan, StartParams.tx_gain[chan]); + if (res) { + LOGC(DDEV, INFO) << "Error setting TX gain res: " << res; + } + } + + return StartParams.tx_gain[chan]; +} + +double PCIESDRDevice::setPowerAttenuation(int atten, size_t chan) { + double rfGain; + + rfGain = setTxGain(maxTxGain() - atten, chan); + return maxTxGain() - rfGain; +} + +double PCIESDRDevice::getPowerAttenuation(size_t chan) { + return maxTxGain() - getTxGain(chan); +} + +double PCIESDRDevice::setRxGain(double dB, size_t chan) +{ + int res = 0; + + if (chan) { + LOGC(DDEV, ERROR) << "Invalid channel " << chan; + return 0.0; + } + + LOGC(DDEV, INFO) << "Setting RX gain to " << dB << " dB."; + StartParams.rx_gain[chan] = dB; + + if (started) { + res = msdr_set_rx_gain(device, chan, dB); + if (res) { + LOGC(DDEV, ERROR) << "Error setting RX gain res: " << res; + } + } + + return StartParams.rx_gain[chan]; +} + +int PCIESDRDevice::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 0; +} + +bool PCIESDRDevice::flush_recv() +{ + unsigned int chan = 0; + static sample_t samples[PSAMPLES_NUM]; + static sample_t *psamples; + int64_t timestamp_tmp; + int expect_smpls = sizeof(samples) / sizeof(samples[0]); + int rc; + + LOGC(DDEV, INFO) << "PCIESDRDevice flush"; + + psamples = &samples[0]; + + while ((rc = msdr_read(device, ×tamp_tmp, (void**)&psamples, expect_smpls, chan, 100)) > 1) { + if (rc < (int)expect_smpls) + break; + } + if (rc < 0) + return false; + + ts_initial = (TIMESTAMP)timestamp_tmp + rc; + + LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl; + + return true; +} + +/* NOTE: Assumes sequential reads */ +int PCIESDRDevice::readSamples(std::vector <short *> &bufs, int len, bool *overrun, + TIMESTAMP timestamp, bool *underrun) +{ + int rc, num_smpls, expect_smpls; + ssize_t avail_smpls; + TIMESTAMP expect_timestamp; + unsigned int i; + static sample_t samples[PSAMPLES_NUM]; + static sample_t *psamples; + int64_t timestamp_tmp; +#ifndef LIBSDR_HAS_MSDR_CONVERT + float powerScaling[] = {1, 1, 1, 1}; +#endif + + if (!started) + return -1; + + if (bufs.size() != chans) { + LOGC(DDEV, ERROR) << "Invalid channel combination " << bufs.size(); + return -1; + } + + if (len > (int)(sizeof(samples) / sizeof(*samples))) { + LOGC(DDEV, ERROR) << "Sample buffer:" << (sizeof(samples) / sizeof(*samples)) + << " is smaller than len:" << len; + return -1; + } + + *overrun = false; + *underrun = false; + + /* Check that timestamp is valid */ + rc = rx_buffers[0]->avail_smpls(timestamp); + if (rc < 0) { + LOGC(DDEV, ERROR) << "rc < 0"; + LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc); + LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp); + return 0; + } + + for (i = 0; i < chans; i++) { + /* Receive samples from HW until we have enough */ + while ((avail_smpls = rx_buffers[i]->avail_smpls(timestamp)) < len) { + expect_smpls = len - avail_smpls; + expect_smpls = expect_smpls > (int)dma_buffer_len ? expect_smpls : (int)dma_buffer_len; + expect_timestamp = timestamp + avail_smpls; + timestamp_tmp = 0; + psamples = &samples[0]; + num_smpls = msdr_read(device, ×tamp_tmp, (void**)&psamples, expect_smpls, i, 100); + if (num_smpls < 0) { + LOGC(DDEV, ERROR) << "PCIESDR readSamples msdr_read failed num_smpls " << num_smpls + << " device: " << device + << " expect_smpls: " << expect_smpls + << ", expTs:" << expect_timestamp << " got " << timestamp_tmp; + LOGCHAN(i, DDEV, ERROR) << "Device receive timed out (" << rc + << " vs exp " << len << ")."; + return -1; + } + + LOGCHAN(i, DDEV, DEBUG) << "Received timestamp = " << (TIMESTAMP)timestamp_tmp + << " (" << num_smpls << ")"; + +#ifdef LIBSDR_HAS_MSDR_CONVERT + msdr_convert_cf32_to_ci16(bufs[i], (float *)psamples, num_smpls); +#else + convert_float_short(bufs[i], (float *)psamples, powerScaling[0], num_smpls * 2); +#endif + + if (expect_smpls != num_smpls) { + LOGCHAN(i, DDEV, DEBUG) << "Unexpected recv buffer len: expect " + << expect_smpls << " got " << num_smpls + << ", diff=" << expect_smpls - num_smpls + << ", expTs:" << expect_timestamp << " got " << timestamp_tmp; + } + + if (expect_timestamp != (TIMESTAMP)timestamp_tmp) { + LOGCHAN(i, DDEV, ERROR) << "Unexpected recv buffer timestamp: expect " + << expect_timestamp << " got " << timestamp_tmp + << ", diff=" << timestamp_tmp - expect_timestamp; + } + rc = rx_buffers[i]->write(bufs[i], num_smpls, (TIMESTAMP)timestamp_tmp); + if (rc < 0) { + if (rc != smpl_buf::ERROR_OVERFLOW) { + return 0; + } + } + } + } + + /* We have enough samples */ + for (size_t i = 0; i < rx_buffers.size(); i++) { + rc = rx_buffers[i]->read(bufs[i], len, timestamp); + if ((rc < 0) || (rc != len)) { + LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc) << ". " + << rx_buffers[i]->str_status(timestamp) + << ", (len=" << len << ")"; + return 0; + } + } + + return len; +} + +int PCIESDRDevice::writeSamples(std::vector<short *> &bufs, int len, + bool *underrun, unsigned long long timestamp) +{ + int rc = 0; + unsigned int i; + static sample_t samples[PSAMPLES_NUM]; + static sample_t *psamples; + int64_t hw_time; + int64_t timestamp_tmp; + SDRStats stats; + + if (!started) + return -1; + + if (bufs.size() != chans) { + LOGC(DDEV, ERROR) << "Invalid channel combination " << bufs.size(); + return -1; + } + + if (len > (int)(sizeof(samples) / sizeof(*samples))) { + LOGC(DDEV, ERROR) << "Sample buffer:" << (sizeof(samples) / sizeof(*samples)) + << " is smaller than len:" << len; + return -1; + } + + timestamp_tmp = timestamp - ts_offset; /* Shift Tx time by offset */ + + *underrun = false; + i = 0; + for (i = 0; i < chans; i++) { + LOGCHAN(i, DDEV, DEBUG) << "send buffer of len " << len << " timestamp " << std::hex << timestamp_tmp; + psamples = &samples[0]; + +#ifdef LIBSDR_HAS_MSDR_CONVERT + msdr_convert_ci16_to_cf32((float*)psamples, bufs[i], len); +#else + convert_short_float((float*)psamples, bufs[i], len * 2); +#endif + rc = msdr_write(device, timestamp_tmp, (const void**)&psamples, len, i, &hw_time); + if (rc != len) { + LOGC(DDEV, ALERT) << "PCIESDR writeSamples: Device send timed out rc:" << rc + << " timestamp" << timestamp_tmp << " len:" << len << " hwtime:" << hw_time; + LOGCHAN(i, DDEV, ERROR) << "PCIESDR: Device Tx timed out (" << rc << " vs exp " << len << ")."; + return -1; + } + if (msdr_get_stats(device, &stats)) { + LOGC(DDEV, ALERT) << "PCIESDR: get_stats failed:" << rc; + } else if (stats.tx_underflow_count > tx_underflow) { + tx_underflow = stats.tx_underflow_count; + LOGC(DDEV, ALERT) << "tx_underflow_count:" << stats.tx_underflow_count + << " rx_overflow_count:" << stats.rx_overflow_count; + *underrun = true; + } + + if (timestamp_tmp - hw_time > (int64_t)actualSampleRate / 10) + LOGC(DDEV, ALERT) << "PCIESDR: tx diff more ts_tmp:" << timestamp_tmp << " ts:" << timestamp + << " hwts:" << hw_time; + + if (hw_time > timestamp_tmp) { + LOGC(DDEV, ALERT) << "PCIESDR: tx underrun ts_tmp:" << timestamp_tmp << " ts:" << timestamp + << " hwts:" << hw_time; + *underrun = true; + } + } + + return rc; +} + +bool PCIESDRDevice::setRxAntenna(const std::string & ant, size_t chan) +{ + return true; +} + +std::string PCIESDRDevice::getRxAntenna(size_t chan) +{ + return ""; +} + +bool PCIESDRDevice::setTxAntenna(const std::string & ant, size_t chan) +{ + return true; +} + +std::string PCIESDRDevice::getTxAntenna(size_t chan ) +{ + return ""; +} + +bool PCIESDRDevice::requiresRadioAlign() +{ + return false; +} + +GSM::Time PCIESDRDevice::minLatency() +{ + return GSM::Time(6,7); +} + +bool PCIESDRDevice::updateAlignment(TIMESTAMP timestamp) +{ + LOGC(DDEV, INFO) << "Update Alignment "; + + return true; +} + +bool PCIESDRDevice::setTxFreq(double wFreq, size_t chan) +{ + double actual = 0; + + LOGCHAN(chan, DDEV, NOTICE) << "PCIESDR setTxFreq"; + if (chan) { + LOGC(DDEV, ERROR) << "Invalid channel " << chan; + return false; + } + actual = StartParams.tx_freq[chan]; + StartParams.tx_freq[chan] = wFreq; + LOGC(DDEV, INFO) << "set TX: " << wFreq << std::endl + << " actual freq: " << actual << std::endl; + + return true; +} + +bool PCIESDRDevice::setRxFreq(double wFreq, size_t chan) +{ + double actual = 0; + + LOGCHAN(chan, DDEV, NOTICE) << "PCIESDR setRxFreq"; + + if (chan) { + LOGC(DDEV, ERROR) << "Invalid channel " << chan; + return false; + } + actual = StartParams.rx_freq[chan]; + StartParams.rx_freq[chan] = wFreq; + LOGC(DDEV, INFO) << "set RX: " << wFreq << std::endl + << " actual freq: " << actual << std::endl; + + 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) +{ + if (tx_sps != rx_sps) { + LOGC(DDEV, ERROR) << "PCIESDR Requires tx_sps == rx_sps"; + return NULL; + } + if (lo_offset != 0.0) { + LOGC(DDEV, ERROR) << "PCIESDR doesn't support lo_offset"; + return NULL; + } + return new PCIESDRDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths); +} diff --git a/Transceiver52M/device/pciesdr/PCIESDRDevice.h b/Transceiver52M/device/pciesdr/PCIESDRDevice.h new file mode 100644 index 0000000..a811078 --- /dev/null +++ b/Transceiver52M/device/pciesdr/PCIESDRDevice.h @@ -0,0 +1,199 @@ +/* +* Copyright 2018 sysmocom - s.f.m.c. GmbH +* +* SPDX-License-Identifier: AGPL-3.0+ +* +* This software is distributed under multiple licenses; see the COPYING file in +* the main directory for licensing information for this specific distribution. +* +* 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 _PCIESDR_DEVICE_H_ +#define _PCIESDR_DEVICE_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "radioDevice.h" +#include "smpl_buf.h" + +#include <sys/time.h> +#include <math.h> +#include <limits.h> +#include <string> +#include <iostream> +extern "C" { +#include "libsdr.h" +} + +#define PCIESDR_TX_AMPL 0.707 + +/** A class to handle a PCIESDR supported device */ +class PCIESDRDevice:public RadioDevice { + +private: + MultiSDRState* device; + SDRStartParams StartParams; + typedef struct { + float re; + float im; + } sample_t; + unsigned int dma_buffer_count; + unsigned int dma_buffer_len; + + std::vector<smpl_buf *> rx_buffers; + + double actualSampleRate; ///< the actual sampling rate + + bool started; ///< flag indicates device has started + bool skipRx; ///< set if device is transmit-only. + + TIMESTAMP ts_initial, ts_offset; + + std::vector<double> tx_gains, rx_gains; + + bool flush_recv(); + + int64_t tx_underflow; + int64_t rx_overflow; + + /** sets the transmit chan gain, returns the gain setting **/ + double setTxGain(double dB, size_t chan = 0); + + /** get transmit gain */ + double getTxGain(size_t chan = 0); + + /** return maximum Tx Gain **/ + double maxTxGain(void); + +public: + + /** Object constructor */ + PCIESDRDevice(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); + ~PCIESDRDevice(); + + /** Instantiate the PCIESDR */ + int open(const std::string &args, int ref, bool swap_channels); + + /** Start the PCIESDR */ + bool start(); + + /** Stop the PCIESDR */ + bool stop(); + + enum TxWindowType getWindowType() { + return TX_WINDOW_LMS1; + } + + /** + Read samples from the PCIESDR. + @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 PCIESDR does not have data to transmit, e.g. data not being sent fast enough + @return The number of samples actually read + */ + int readSamples(std::vector <short *> &buf, int len, bool *overrun, + TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL); + + /** + Write samples to the PCIESDR. + @param buf Contains the data to be written. + @param len number of samples to write. + @param underrun Set if PCIESDR 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. + @return The number of samples actually written + */ + int writeSamples(std::vector <short *> &bufs, int len, bool *underrun, + TIMESTAMP timestamp = 0xffffffff); + + /** 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 * PCIESDR_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 rx_gains[chan]; + } + + /** return maximum Rx Gain **/ + double maxRxGain(void); + + /** 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 */ + 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; + } +}; + +#endif // _PCIESDR_DEVICE_H_ diff --git a/configure.ac b/configure.ac index 07d4bf4..f792bc5 100644 --- a/configure.ac +++ b/configure.ac @@ -140,6 +140,11 @@ [enable LimeSuite based transceiver]) ]) +AC_ARG_WITH(pciesdr, [ + AS_HELP_STRING([--with-pciesdr], + [enable PCIeSDR (Amarisoft) based transceiver]) +]) + AC_ARG_WITH(singledb, [ AS_HELP_STRING([--with-singledb], [enable single daughterboard use on USRP1]) @@ -179,6 +184,10 @@ PKG_CHECK_MODULES(LMS, LimeSuite) ]) +AS_IF([test "x$with_pciesdr" = "xyes"], [ + PKG_CHECK_MODULES(PCIESDR, libsdr) +]) + 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)], @@ -241,6 +250,7 @@ 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(DEVICE_PCIESDR, [test "x$with_pciesdr" = "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"]) @@ -325,6 +335,7 @@ Transceiver52M/device/uhd/Makefile \ Transceiver52M/device/usrp1/Makefile \ Transceiver52M/device/lms/Makefile \ + Transceiver52M/device/pciesdr/Makefile \ tests/Makefile \ tests/CommonLibs/Makefile \ tests/Transceiver52M/Makefile \ diff --git a/doc/examples/osmo-trx-pciesdr/osmo-trx-pciesdr.cfg b/doc/examples/osmo-trx-pciesdr/osmo-trx-pciesdr.cfg new file mode 100644 index 0000000..b74d50c --- /dev/null +++ b/doc/examples/osmo-trx-pciesdr/osmo-trx-pciesdr.cfg @@ -0,0 +1,21 @@ +log stderr + logging filter all 1 + logging color 1 + logging print category 1 + logging timestamp 1 + logging print file basename + logging level set-all info +! +line vty + no login +! +trx + bind-ip 127.0.0.1 + remote-ip 127.0.0.1 + base-port 5700 + egprs disable + dev-args dev0=/dev/sdr0 + tx-sps 4 + rx-sps 4 + rt-prio 18 + chan 0 -- To view, visit https://gerrit.osmocom.org/c/osmo-trx/+/19548 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-trx Gerrit-Branch: master Gerrit-Change-Id: I5b49b61d1c8f357e33acc066f44d6c7acfc3ba79 Gerrit-Change-Number: 19548 Gerrit-PatchSet: 1 Gerrit-Owner: Xavier Zu <xaver1zu at gmail.com> Gerrit-MessageType: newchange -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200807/d8f6ecfa/attachment.htm>