From c7a0bf1ffc7f92f8f51311a307f53e925f465320 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Wed, 25 Apr 2018 12:17:10 +0200 Subject: lms: Several improvements and compilation/runtime fixes Continuation of initial work done on LimeSuite support from Harald. Change-Id: Ib2fca81b76d027b08e2891056fa076d071597783 --- Transceiver52M/device/lms/LMSDevice.cpp | 335 +++++++++++++++++++++----------- Transceiver52M/device/lms/LMSDevice.h | 48 +++-- Transceiver52M/device/radioDevice.h | 2 +- 3 files changed, 261 insertions(+), 124 deletions(-) (limited to 'Transceiver52M') diff --git a/Transceiver52M/device/lms/LMSDevice.cpp b/Transceiver52M/device/lms/LMSDevice.cpp index ed51cdb..603b23d 100644 --- a/Transceiver52M/device/lms/LMSDevice.cpp +++ b/Transceiver52M/device/lms/LMSDevice.cpp @@ -24,26 +24,40 @@ #include +#include + #ifdef HAVE_CONFIG_H #include "config.h" #endif using namespace std; -const double LMSDevice::masterClockRate = 52.0e6; +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 sps) +LMSDevice::LMSDevice(size_t sps, size_t chans): + m_lms_dev(NULL), sps(sps), chans(chans) { LOG(INFO) << "creating LMS device..."; - m_lms_device = NULL; - this->sps = sps; + 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 lvl_map[4] = { + static const int lvl_map[5] = { [0] = LOGL_FATAL, [1] = LOGL_ERROR, [2] = LOGL_NOTICE, @@ -51,40 +65,71 @@ static void lms_log_callback(int lvl, const char *msg) [4] = LOGL_DEBUG, }; /* protect against future higher log level values (lower importance) */ - if (lvl >= ARRAY_SIZE(lvl_map)) + if ((unsigned int) lvl >= ARRAY_SIZE(lvl_map)) lvl = ARRAY_SIZE(lvl_map)-1; - LOG(lvl) << msg; + LOGLV(DLMS, lvl) << msg; } -int LMSDevice::open(const std::string &, int, bool) +static void thread_enable_cancel(bool cancel) { - lms_info_str dev_str; + cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) : + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); +} + +int LMSDevice::open(const std::string &args, int ref, bool swap_channels) +{ + //lms_info_str_t dev_str; + lms_info_str_t* info_list; uint16_t dac_val; + unsigned int i, n; + int rc; - LOG(INFO) << "opening LMS device.."; + LOG(INFO) << "Opening LMS device.."; LMS_RegisterLogHandler(&lms_log_callback); - rc = LMS_Open(&m_lms_dev, NULL, NULL); - if (rc != 0) + 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) //Populate device list + LOG(ERROR) << "LMS_GetDeviceList(info_list) failed"; + + for (i = 0; i < n; i++) //print device list + 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; + } - if (LMS_SetSampleRate(m_lms_dev, GSMRATE, sps) < 0) + delete [] info_list; + + LOG(DEBUG) << "Setting sample rate to " << GSMRATE*sps << " " << sps; + if (LMS_SetSampleRate(m_lms_dev, GSMRATE*sps, 32) < 0) goto out_close; /* FIXME: make this device/model dependent, like UHDDevice:dev_param_map! */ - ts_offset = static_caset(8.9e-5 * GSMRATE); + ts_offset = static_cast(8.9e-5 * GSMRATE); 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_EXTENAL: + case REF_EXTERNAL: + LOG(DEBUG) << "Setting Internal 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; @@ -94,14 +139,18 @@ int LMSDevice::open(const std::string &, int, bool) goto out_close; } + LOG(INFO) << "Init LMS device"; if (LMS_Init(m_lms_dev) < 0) goto out_close; /* Perform Rx and Tx calibration */ - if (LMS_Calibrate(m_lms_dev, LMS_CH_RX, chan, 270000.0, 0) < 0) - goto out_close; - if (LMS_Calibrate(m_lms_dev, LMS_CH_TX, chan, 270000.0, 0) < 0) - goto out_close; + for (i=0; i 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; + } + + LOG(INFO) << "Initial timestamp " << ts_initial << std::endl; + delete[] buffer; + return true; +} + bool LMSDevice::setRxAntenna(const std::string & ant, size_t chan) { int idx; @@ -257,7 +347,7 @@ bool LMSDevice::setRxAntenna(const std::string & ant, size_t chan) return false; } - idx = get_ant_idx(ant, LMS_CH_RX); + idx = get_ant_idx(ant, LMS_CH_RX, chan); if (idx < 0) { LOG(ALERT) << "Invalid Rx Antenna"; return false; @@ -272,6 +362,9 @@ bool LMSDevice::setRxAntenna(const std::string & ant, size_t chan) 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 ""; @@ -283,12 +376,12 @@ std::string LMSDevice::getRxAntenna(size_t chan) return ""; } - if (LMS_GetAntennaList(m_lms_dev, LMS_CH_RX, chan, &list) < idx) { + if (LMS_GetAntennaList(m_lms_dev, LMS_CH_RX, chan, name_list) < idx) { LOG(ALERT) << "Error getting Rx Antenna List"; return ""; } - return list[idx]; + return name_list[idx]; } bool LMSDevice::setTxAntenna(const std::string & ant, size_t chan) @@ -300,7 +393,7 @@ bool LMSDevice::setTxAntenna(const std::string & ant, size_t chan) return false; } - idx = get_ant_idx(ant, LMS_CH_TX); + idx = get_ant_idx(ant, LMS_CH_TX, chan); if (idx < 0) { LOG(ALERT) << "Invalid Rx Antenna"; return false; @@ -315,6 +408,7 @@ bool LMSDevice::setTxAntenna(const std::string & ant, size_t chan) 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()) { @@ -328,49 +422,73 @@ std::string LMSDevice::getTxAntenna(size_t chan) return ""; } - if (LMS_GetAntennaList(m_lms_dev, LMS_CH_TX, chan, &list) < idx) { + if (LMS_GetAntennaList(m_lms_dev, LMS_CH_TX, chan, name_list) < idx) { LOG(ALERT) << "Error getting Tx Antenna List"; return ""; } - return list[idx]; + 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) { - lms_stream_meta_t rx_metadata = { - .flushPartialPacket = false, - .waitForTimestamp = false, - }; - int rc; + 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; + /* Shift read time with respect to transmit clock */ + timestamp += ts_offset; + rx_metadata.timestamp = 0; - if (bufs.size != 1) { + if (bufs.size() != chans) { LOG(ALERT) << "Invalid channel combination " << bufs.size(); return -1; } - /* Shift read time with respect to transmit clock */ - timestamp += ts_offset; - - rc = LMS_RecvStream(&m_lms_stream_rx, bufs[0], len, &rx_metadata, 100); - *overrun = false; *underrun = false; - - if (LMS_GetStreamStatus(&m_lms_stream_rx, &status) == 0) { - if (status.underrun > m_last_rx_underruns) - *underrun = true; - m_last_rx_underruns = status.underrun; - - if (status.overrun > m_last_rx_overruns) - *overrun = true; - m_last_rx_overruns = status.overrun; + for (i = 0; i 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; } @@ -378,35 +496,40 @@ int LMSDevice::writeSamples(std::vector < short *>&bufs, int len, bool * underrun, unsigned long long timestamp, bool isControl) { - lms_stream_status_t status; - lms_stream_meta_t tx_metadata = { - .flushPartialPacket = false, - .waitForTimestamp = true, - .timestamp = timestamp, - }; 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; if (isControl) { LOG(ERR) << "Control packets not supported"; return 0; } - if (bufs.size() != 1) { + if (bufs.size() != chans) { LOG(ALERT) << "Invalid channel combination " << bufs.size(); return -1; } - rc = LMS_Send_Stream(&m_lms_stream_tx, bufs[0], len, &tx_metadata, 100); - if (rc != len) { - LOG(ALERT) << "LMS: Device send timed out "; - } - *underrun = false; - if (LMS_GetStreamStatus(&m_lms_stream_tx, &status) == 0) { - if (status.underrun > m_last_tx_underruns) - *underrun = true; - m_last_tx_underruns = status.underrun; + for (i = 0; i m_last_tx_underruns[i]) + *underrun = true; + m_last_tx_underruns[i] = status.underrun; + } + thread_enable_cancel(true); } samplesWritten += rc; @@ -416,17 +539,7 @@ int LMSDevice::writeSamples(std::vector < short *>&bufs, int len, bool LMSDevice::updateAlignment(TIMESTAMP timestamp) { - short data[] = { 0x00, 0x02, 0x00, 0x00 }; - uint32_t *wordPtr = (uint32_t *) data; - *wordPtr = host_to_usrp_u32(*wordPtr); - bool tmpUnderrun; - - std::vector < short *>buf(1, data); - if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) { - pingTimestamp = timestamp; - return true; - } - return false; + return true; } bool LMSDevice::setTxFreq(double wFreq, size_t chan) @@ -465,5 +578,5 @@ RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, const std::vector < std::string > &tx_paths, const std::vector < std::string > &rx_paths) { - return new LMSDevice(tx_sps); + return new LMSDevice(tx_sps, chans); } diff --git a/Transceiver52M/device/lms/LMSDevice.h b/Transceiver52M/device/lms/LMSDevice.h index 653d159..99eed43 100644 --- a/Transceiver52M/device/lms/LMSDevice.h +++ b/Transceiver52M/device/lms/LMSDevice.h @@ -21,22 +21,33 @@ #include "radioDevice.h" -#include #include #include +#include #include #include +#include + +#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; - lms_stream_t m_lms_Stream_rx; - lms_stream_t m_lms_Stream_tx; + std::vector m_lms_stream_rx; + std::vector m_lms_stream_tx; + + std::vector m_last_rx_underruns; + std::vector m_last_rx_overruns; + std::vector m_last_tx_underruns; + std::vector m_last_tx_overruns; - int sps; + size_t sps, chans; + 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 @@ -44,15 +55,20 @@ private: bool started; ///< flag indicates LMS has started bool skipRx; ///< set if LMS is transmit-only. - TIMESTAMP ts_offset; + 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 sps); + LMSDevice(size_t sps, size_t chans); /** Instantiate the LMS */ - int open(const std::string &, int, bool); + int open(const std::string &args, int ref, bool swap_channels); /** Start the LMS */ bool start(); @@ -62,7 +78,9 @@ public: /** Set priority not supported */ void setPriority(float prio = 0.5) { - } enum TxWindowType getWindowType() { + } + + enum TxWindowType getWindowType() { return TX_WINDOW_LMS1; } @@ -103,22 +121,22 @@ public: /** Returns the starting write Timestamp*/ TIMESTAMP initialWriteTimestamp(void) { - return 20000; + return ts_initial; } /** Returns the starting read Timestamp*/ TIMESTAMP initialReadTimestamp(void) { - return 20000; + return ts_initial; } /** returns the full-scale transmit amplitude **/ double fullScaleInputValue() { - return 13500.0; + return(double) SHRT_MAX * LIMESDR_TX_AMPL; } /** returns the full-scale receive amplitude **/ double fullScaleOutputValue() { - return 9450.0; + return (double) SHRT_MAX; } /** sets the receive chan gain, returns the gain setting **/ @@ -156,6 +174,12 @@ public: /* 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; diff --git a/Transceiver52M/device/radioDevice.h b/Transceiver52M/device/radioDevice.h index 44636d5..c5cd461 100644 --- a/Transceiver52M/device/radioDevice.h +++ b/Transceiver52M/device/radioDevice.h @@ -39,7 +39,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 { -- cgit v1.2.3