diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am index 7bdc1815..6fcef7c6 100644 --- a/Transceiver52M/Makefile.am +++ b/Transceiver52M/Makefile.am @@ -55,15 +55,9 @@ COMMON_SOURCES = \ Transceiver.cpp \ DummyLoad.cpp -if RESAMPLE libtransceiver_la_SOURCES = \ $(COMMON_SOURCES) \ - radioIOResamp.cpp -else -libtransceiver_la_SOURCES = \ - $(COMMON_SOURCES) \ - radioIO.cpp -endif + radioInterfaceResamp.cpp noinst_PROGRAMS = \ USRPping \ diff --git a/Transceiver52M/UHDDevice.cpp b/Transceiver52M/UHDDevice.cpp index 6ce3380b..575cb017 100644 --- a/Transceiver52M/UHDDevice.cpp +++ b/Transceiver52M/UHDDevice.cpp @@ -32,7 +32,9 @@ #include "config.h" #endif -#define B100_CLK_RT 52e6 +#define B100_CLK_RT 52e6 +#define B100_BASE_RT GSMRATE +#define USRP2_BASE_RT 400e3 #define TX_AMPL 0.3 #define SAMPLE_BUF_SZ (1 << 20) @@ -91,6 +93,29 @@ static double get_dev_offset(enum uhd_dev_type type, int sps) return 0.0; } +/* + * Select sample rate based on device type and requested samples-per-symbol. + * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum + * usable channel spacing of 400 kHz. + */ +static double select_rate(uhd_dev_type type, int sps) +{ + if ((sps != 4) && (sps != 2) && (sps != 1)) + return -9999.99; + + switch (type) { + case USRP2: + return USRP2_BASE_RT * sps; + break; + case B100: + return B100_BASE_RT * sps; + break; + } + + LOG(ALERT) << "Unknown device type " << type; + return -9999.99; +} + /** Timestamp conversion @param timestamp a UHD or OpenBTS timestamp @param rate sample rate @@ -181,10 +206,10 @@ private: */ class uhd_device : public RadioDevice { public: - uhd_device(double rate, int sps, bool skip_rx); + uhd_device(int sps, bool skip_rx); ~uhd_device(); - bool open(const std::string &args); + int open(const std::string &args); bool start(); bool stop(); void restart(uhd::time_spec_t ts); @@ -306,9 +331,8 @@ void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg) } } -uhd_device::uhd_device(double rate, int sps, bool skip_rx) - : desired_smpl_rt(rate), actual_smpl_rt(0), - tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0), +uhd_device::uhd_device(int sps, bool skip_rx) + : tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0), rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0), tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0), @@ -374,6 +398,9 @@ int uhd_device::set_master_clk(double clk_rate) int uhd_device::set_rates(double rate) { + double offset_limit = 10.0; + double tx_offset, rx_offset; + // B100 is the only device where we set FPGA clocking if (dev_type == B100) { if (set_master_clk(B100_CLK_RT) < 0) @@ -385,14 +412,14 @@ int uhd_device::set_rates(double rate) usrp_dev->set_rx_rate(rate); actual_smpl_rt = usrp_dev->get_tx_rate(); - if (actual_smpl_rt != rate) { + tx_offset = actual_smpl_rt - rate; + rx_offset = usrp_dev->get_rx_rate() - rate; + if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) { LOG(ALERT) << "Actual sample rate differs from desired rate"; + LOG(ALERT) << "Tx/Rx (" << actual_smpl_rt << "/" + << usrp_dev->get_rx_rate() << ")"; return -1; } - if (usrp_dev->get_rx_rate() != actual_smpl_rt) { - LOG(ALERT) << "Transmit and receive sample rates do not match"; - return -1.0; - } return 0; } @@ -427,15 +454,15 @@ bool uhd_device::parse_dev_type() { std::string mboard_str, dev_str; uhd::property_tree::sptr prop_tree; - size_t usrp1_str, usrp2_str, b100_str1, b100_str2; + size_t usrp1_str, usrp2_str, b100_str; prop_tree = usrp_dev->get_device()->get_tree(); dev_str = prop_tree->access("/name").get(); mboard_str = usrp_dev->get_mboard_name(); usrp1_str = dev_str.find("USRP1"); - b100_str1 = dev_str.find("B-Series"); - b100_str2 = mboard_str.find("B100"); + usrp2_str = dev_str.find("USRP2"); + b100_str = mboard_str.find("B100"); if (usrp1_str != std::string::npos) { LOG(ALERT) << "USRP1 is not supported using the UHD driver"; @@ -444,14 +471,17 @@ bool uhd_device::parse_dev_type() return false; } - if ((b100_str1 != std::string::npos) || (b100_str2 != std::string::npos)) { + if (b100_str != std::string::npos) { tx_window = TX_WINDOW_USRP1; LOG(INFO) << "Using USRP1 type transmit window for " << dev_str << " " << mboard_str; dev_type = B100; return true; - } else { + } else if (usrp2_str != std::string::npos) { dev_type = USRP2; + } else { + LOG(ALERT) << "Unknown UHD device type"; + return false; } tx_window = TX_WINDOW_FIXED; @@ -460,7 +490,7 @@ bool uhd_device::parse_dev_type() return true; } -bool uhd_device::open(const std::string &args) +int uhd_device::open(const std::string &args) { // Register msg handler uhd::msg::register_handler(&uhd_msg_handler); @@ -470,7 +500,7 @@ bool uhd_device::open(const std::string &args) uhd::device_addrs_t dev_addrs = uhd::device::find(addr); if (dev_addrs.size() == 0) { LOG(ALERT) << "No UHD devices found with address '" << args << "'"; - return false; + return -1; } // Use the first found device @@ -479,12 +509,12 @@ bool uhd_device::open(const std::string &args) usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]); } catch(...) { LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string(); - return false; + return -1; } // Check for a valid device type and set bus type if (!parse_dev_type()) - return false; + return -1; #ifdef EXTREF set_ref_clk(true); @@ -499,8 +529,9 @@ bool uhd_device::open(const std::string &args) rx_spp = rx_stream->get_max_num_samps(); // Set rates + desired_smpl_rt = select_rate(dev_type, sps); if (set_rates(desired_smpl_rt) < 0) - return false; + return -1; // Create receive buffer size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t); @@ -521,7 +552,10 @@ bool uhd_device::open(const std::string &args) // Print configuration LOG(INFO) << "\n" << usrp_dev->get_pp_string(); - return true; + if (dev_type == USRP2) + return RESAMP; + + return NORMAL; } bool uhd_device::flush_recv(size_t num_pkts) @@ -1021,7 +1055,7 @@ std::string smpl_buf::str_code(ssize_t code) } } -RadioDevice *RadioDevice::make(double smpl_rt, int sps, bool skip_rx) +RadioDevice *RadioDevice::make(int sps, bool skip_rx) { - return new uhd_device(smpl_rt, sps, skip_rx); + return new uhd_device(sps, skip_rx); } diff --git a/Transceiver52M/USRPDevice.cpp b/Transceiver52M/USRPDevice.cpp index 237c5f1b..5c990038 100644 --- a/Transceiver52M/USRPDevice.cpp +++ b/Transceiver52M/USRPDevice.cpp @@ -59,11 +59,11 @@ const dboardConfigType dboardConfig = TXA_RXB; const double USRPDevice::masterClockRate = 52.0e6; -USRPDevice::USRPDevice (double _desiredSampleRate, bool skipRx) +USRPDevice::USRPDevice(int sps, bool skipRx) : skipRx(skipRx) { LOG(INFO) << "creating USRP device..."; - decimRate = (unsigned int) round(masterClockRate/_desiredSampleRate); + decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps)); actualSampleRate = masterClockRate/decimRate; rxGain = 0; @@ -75,7 +75,7 @@ USRPDevice::USRPDevice (double _desiredSampleRate, bool skipRx) #endif } -bool USRPDevice::open(const std::string &) +int USRPDevice::open(const std::string &) { writeLock.unlock(); @@ -97,7 +97,7 @@ bool USRPDevice::open(const std::string &) catch(...) { LOG(ALERT) << "make failed on Rx"; m_uRx.reset(); - return false; + return -1; } if (m_uRx->fpga_master_clock_freq() != masterClockRate) @@ -105,7 +105,7 @@ bool USRPDevice::open(const std::string &) LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq() << ", desired clock freq = " << masterClockRate; m_uRx.reset(); - return false; + return -1; } } @@ -120,7 +120,7 @@ bool USRPDevice::open(const std::string &) catch(...) { LOG(ALERT) << "make failed on Tx"; m_uTx.reset(); - return false; + return -1; } if (m_uTx->fpga_master_clock_freq() != masterClockRate) @@ -128,7 +128,7 @@ bool USRPDevice::open(const std::string &) LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq() << ", desired clock freq = " << masterClockRate; m_uTx.reset(); - return false; + return -1; } if (!skipRx) m_uRx->stop(); @@ -165,7 +165,7 @@ bool USRPDevice::open(const std::string &) samplesWritten = 0; started = false; - return true; + return NORMAL; } @@ -556,7 +556,7 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;}; bool USRPDevice::setRxFreq(double wFreq) { return true;}; #endif -RadioDevice *RadioDevice::make(double desiredSampleRate, bool skipRx) +RadioDevice *RadioDevice::make(int sps, bool skipRx) { - return new USRPDevice(desiredSampleRate, skipRx); + return new USRPDevice(sps, skipRx); } diff --git a/Transceiver52M/USRPDevice.h b/Transceiver52M/USRPDevice.h index f2a9a6dd..f5bc9395 100644 --- a/Transceiver52M/USRPDevice.h +++ b/Transceiver52M/USRPDevice.h @@ -112,10 +112,10 @@ private: public: /** Object constructor */ - USRPDevice (double _desiredSampleRate, bool skipRx); + USRPDevice(int sps, bool skipRx); /** Instantiate the USRP */ - bool open(const std::string &); + int open(const std::string &); /** Start the USRP */ bool start(); diff --git a/Transceiver52M/radioDevice.h b/Transceiver52M/radioDevice.h index ff3421cc..485d0371 100644 --- a/Transceiver52M/radioDevice.h +++ b/Transceiver52M/radioDevice.h @@ -21,6 +21,8 @@ #include "config.h" #endif +#define GSMRATE 1625e3/6 + /** a 64-bit virtual timestamp for radio data */ typedef unsigned long long TIMESTAMP; @@ -31,10 +33,13 @@ class RadioDevice { /* Available transport bus types */ enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED }; - static RadioDevice *make(double desiredSampleRate, int sps, bool skipRx = false); + /* Radio interface types */ + enum RadioInterfaceType { NORMAL, RESAMP }; + + static RadioDevice *make(int sps, bool skipRx = false); /** Initialize the USRP */ - virtual bool open(const std::string &args)=0; + virtual int open(const std::string &args)=0; /** Start the USRP */ virtual bool start()=0; diff --git a/Transceiver52M/radioIO.cpp b/Transceiver52M/radioIO.cpp deleted file mode 100644 index 9956e871..00000000 --- a/Transceiver52M/radioIO.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Radio device I/O interface - * Written by Thomas Tsou - * - * Copyright 2011 Free Software Foundation, Inc. - * - * 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 . - * See the COPYING file in the main directory for details. - */ - -#include -#include - -/* Device side buffers */ -static short rx_buf[OUTCHUNK * 2 * 2]; -static short tx_buf[INCHUNK * 2 * 2]; - -/* Complex float to short conversion */ -static int float_to_short(short *shrt_out, float *flt_in, int num) -{ - int i; - - for (i = 0; i < num; i++) { - shrt_out[2 * i + 0] = flt_in[2 * i + 0]; - shrt_out[2 * i + 1] = flt_in[2 * i + 1]; - } - - return i; -} - -/* Comlpex short to float conversion */ -static int short_to_float(float *flt_out, short *shrt_in, int num) -{ - int i; - - for (i = 0; i < num; i++) { - flt_out[2 * i + 0] = shrt_in[2 * i + 0]; - flt_out[2 * i + 1] = shrt_in[2 * i + 1]; - } - - return i; -} - -/* Receive a timestamped chunk from the device */ -void RadioInterface::pullBuffer() -{ - bool local_underrun; - - /* Read samples. Fail if we don't get what we want. */ - int num_rd = mRadio->readSamples(rx_buf, OUTCHUNK, &overrun, - readTimestamp, &local_underrun); - - LOG(DEBUG) << "Rx read " << num_rd << " samples from device"; - assert(num_rd == OUTCHUNK); - - underrun |= local_underrun; - readTimestamp += (TIMESTAMP) num_rd; - - short_to_float(rcvBuffer + 2 * rcvCursor, rx_buf, num_rd); - rcvCursor += num_rd; -} - -/* Send timestamped chunk to the device with arbitrary size */ -void RadioInterface::pushBuffer() -{ - if (sendCursor < INCHUNK) - return; - - float_to_short(tx_buf, sendBuffer, sendCursor); - - /* Write samples. Fail if we don't get what we want. */ - int num_smpls = mRadio->writeSamples(tx_buf, - sendCursor, - &underrun, - writeTimestamp); - assert(num_smpls == sendCursor); - - writeTimestamp += (TIMESTAMP) num_smpls; - sendCursor = 0; -} diff --git a/Transceiver52M/radioInterface.cpp b/Transceiver52M/radioInterface.cpp index 8ad72b0e..93d2ff33 100644 --- a/Transceiver52M/radioInterface.cpp +++ b/Transceiver52M/radioInterface.cpp @@ -27,6 +27,28 @@ bool started = false; +/* Device side buffers */ +static short rx_buf[OUTCHUNK * 2 * 2]; +static short tx_buf[INCHUNK * 2 * 2]; + +/* Complex float to short conversion */ +static void floatToShort(short *out, float *in, int num) +{ + for (int i = 0; i < num; i++) { + out[2 * i + 0] = (short) in[2 * i + 0]; + out[2 * i + 1] = (short) in[2 * i + 1]; + } +} + +/* Complex short to float conversion */ +static void shortToFloat(float *out, short *in, int num) +{ + for (int i = 0; i < num; i++) { + out[2 * i + 0] = (float) in[2 * i + 0]; + out[2 * i + 1] = (float) in[2 * i + 1]; + } +} + RadioInterface::RadioInterface(RadioDevice *wRadio, int wReceiveOffset, int wSPS, @@ -236,3 +258,41 @@ double RadioInterface::getRxGain() else return -1; } + +/* Receive a timestamped chunk from the device */ +void RadioInterface::pullBuffer() +{ + bool local_underrun; + + /* Read samples. Fail if we don't get what we want. */ + int num_rd = mRadio->readSamples(rx_buf, OUTCHUNK, &overrun, + readTimestamp, &local_underrun); + + LOG(DEBUG) << "Rx read " << num_rd << " samples from device"; + assert(num_rd == OUTCHUNK); + + underrun |= local_underrun; + readTimestamp += (TIMESTAMP) num_rd; + + shortToFloat(rcvBuffer + 2 * rcvCursor, rx_buf, num_rd); + rcvCursor += num_rd; +} + +/* Send timestamped chunk to the device with arbitrary size */ +void RadioInterface::pushBuffer() +{ + if (sendCursor < INCHUNK) + return; + + floatToShort(tx_buf, sendBuffer, sendCursor); + + /* Write samples. Fail if we don't get what we want. */ + int num_smpls = mRadio->writeSamples(tx_buf, + sendCursor, + &underrun, + writeTimestamp); + assert(num_smpls == sendCursor); + + writeTimestamp += (TIMESTAMP) num_smpls; + sendCursor = 0; +} diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h index 216cf3ec..94d89179 100644 --- a/Transceiver52M/radioInterface.h +++ b/Transceiver52M/radioInterface.h @@ -31,7 +31,7 @@ static const unsigned gSlotLen = 148; ///< number of symbols per slot, not /** class to interface the transceiver with the USRP */ class RadioInterface { -private: +protected: Thread mAlignRadioServiceLoopThread; ///< thread that synchronizes transmit and receive sections @@ -63,6 +63,8 @@ private: int mNumARFCNs; signalVector *finalVec, *finalVec9; +private: + /** format samples to USRP */ int radioifyVector(signalVector &wVector, float *floatVector, @@ -73,10 +75,10 @@ private: int unRadioifyVector(float *floatVector, signalVector &wVector); /** push GSM bursts into the transmit buffer */ - void pushBuffer(void); + virtual void pushBuffer(void); /** pull GSM bursts from the receive buffer */ - void pullBuffer(void); + virtual void pullBuffer(void); public: @@ -154,3 +156,18 @@ protected: /** synchronization thread loop */ void *AlignRadioServiceLoopAdapter(RadioInterface*); #endif + +class RadioInterfaceResamp : public RadioInterface { + +private: + + void pushBuffer(); + void pullBuffer(); + +public: + + RadioInterfaceResamp(RadioDevice* wRadio = NULL, + int receiveOffset = 3, + int wSPS = SAMPSPERSYM, + GSM::Time wStartTime = GSM::Time(0)); +}; diff --git a/Transceiver52M/radioIOResamp.cpp b/Transceiver52M/radioInterfaceResamp.cpp similarity index 96% rename from Transceiver52M/radioIOResamp.cpp rename to Transceiver52M/radioInterfaceResamp.cpp index 8e8ac759..c7f17eac 100644 --- a/Transceiver52M/radioIOResamp.cpp +++ b/Transceiver52M/radioInterfaceResamp.cpp @@ -271,8 +271,16 @@ int tx_resmpl_flt_int(short *smpls_out, float *smpls_in, int num_smpls) return num_resmpl; } +RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio, + int wReceiveOffset, + int wSPS, + GSM::Time wStartTime) + : RadioInterface(wRadio, wReceiveOffset, wSPS, wStartTime) +{ +} + /* Receive a timestamped chunk from the device */ -void RadioInterface::pullBuffer() +void RadioInterfaceResamp::pullBuffer() { int num_cv, num_rd; bool local_underrun; @@ -297,7 +305,7 @@ void RadioInterface::pullBuffer() } /* Send a timestamped chunk to the device */ -void RadioInterface::pushBuffer() +void RadioInterfaceResamp::pushBuffer() { int num_cv, num_wr; diff --git a/Transceiver52M/runTransceiver.cpp b/Transceiver52M/runTransceiver.cpp index db332511..952cf3bd 100644 --- a/Transceiver52M/runTransceiver.cpp +++ b/Transceiver52M/runTransceiver.cpp @@ -36,12 +36,6 @@ #include #include -#ifdef RESAMPLE - #define DEVICERATE 400e3 -#else - #define DEVICERATE 1625e3/6 -#endif - using namespace std; ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db"); @@ -88,13 +82,26 @@ int main(int argc, char *argv[]) srandom(time(NULL)); - RadioDevice *usrp = RadioDevice::make(DEVICERATE * SAMPSPERSYM, SAMPSPERSYM); - if (!usrp->open(deviceArgs)) { + RadioDevice *usrp = RadioDevice::make(SAMPSPERSYM); + int radioType = usrp->open(deviceArgs); + if (radioType < 0) { LOG(ALERT) << "Transceiver exiting..." << std::endl; return EXIT_FAILURE; } - RadioInterface* radio = new RadioInterface(usrp,3,SAMPSPERSYM,false); + RadioInterface* radio; + switch (radioType) { + case RadioDevice::NORMAL: + radio = new RadioInterface(usrp, 3, SAMPSPERSYM, false); + break; + case RadioDevice::RESAMP: + radio = new RadioInterfaceResamp(usrp, 3, SAMPSPERSYM, false); + break; + default: + LOG(ALERT) << "Unsupported configuration"; + return EXIT_FAILURE; + } + Transceiver *trx = new Transceiver(gConfig.getNum("TRX.Port"),gConfig.getStr("TRX.IP").c_str(),SAMPSPERSYM,GSM::Time(3,0),radio); trx->receiveFIFO(radio->receiveFIFO()); /* diff --git a/configure.ac b/configure.ac index 1c96f51e..3cf2783f 100644 --- a/configure.ac +++ b/configure.ac @@ -72,11 +72,6 @@ AC_ARG_WITH(singledb, [ [enable single daughterboard use on USRP1]) ]) -AC_ARG_WITH(resamp, [ - AS_HELP_STRING([--with-resamp], - [enable resampling for non-52MHz devices]) -]) - AC_ARG_WITH(extref, [ AS_HELP_STRING([--with-extref], [enable external reference on UHD devices]) @@ -102,10 +97,6 @@ AS_IF([test "x$with_uhd" = "xyes"],[ AC_DEFINE(USE_UHD, 1, Define to 1 if using UHD) ]) -AS_IF([test "x$with_resamp" = "xyes"], [ - AC_DEFINE(RESAMPLE, 1, Define to 1 for resampling) -]) - AS_IF([test "x$with_extref" = "xyes"], [ AC_DEFINE(EXTREF, 1, Define to 1 for external reference) ]) @@ -114,7 +105,6 @@ AS_IF([test "x$with_singledb" = "xyes"], [ AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard) ]) -AM_CONDITIONAL(RESAMPLE, [test "x$with_resamp" = "xyes"]) AM_CONDITIONAL(UHD, [test "x$with_uhd" = "xyes"]) AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])