Move device specific files to device subdir

Change-Id: Ib42fef14bf4c7b779f44d99711a35c18b32a4c21
diff --git a/Transceiver52M/device/Makefile.am b/Transceiver52M/device/Makefile.am
new file mode 100644
index 0000000..8575328
--- /dev/null
+++ b/Transceiver52M/device/Makefile.am
@@ -0,0 +1,11 @@
+include $(top_srcdir)/Makefile.common
+
+noinst_HEADERS = radioDevice.h
+
+SUBDIRS =
+
+if USRP1
+SUBDIRS += usrp1
+else
+SUBDIRS += uhd
+endif
diff --git a/Transceiver52M/device/radioDevice.h b/Transceiver52M/device/radioDevice.h
new file mode 100644
index 0000000..9913de0
--- /dev/null
+++ b/Transceiver52M/device/radioDevice.h
@@ -0,0 +1,160 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+*/
+
+#ifndef __RADIO_DEVICE_H__
+#define __RADIO_DEVICE_H__
+
+#include <string>
+#include <vector>
+
+extern "C" {
+#include "config_defs.h"
+}
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define GSMRATE       (1625e3/6)
+#define MCBTS_SPACING  800000.0
+
+/** a 64-bit virtual timestamp for radio data */
+typedef unsigned long long TIMESTAMP;
+
+/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */
+class RadioDevice {
+
+  public:
+  /* Available transport bus types */
+  enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
+
+  /* Radio interface types */
+  enum InterfaceType {
+    NORMAL,
+    RESAMP_64M,
+    RESAMP_100M,
+    MULTI_ARFCN,
+  };
+
+  static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
+                           size_t chans = 1, double offset = 0.0,
+                           const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
+                           const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
+
+  /** Initialize the USRP */
+  virtual int open(const std::string &args, int ref, bool swap_channels)=0;
+
+  virtual ~RadioDevice() { }
+
+  /** Start the USRP */
+  virtual bool start()=0;
+
+  /** Stop the USRP */
+  virtual bool stop()=0;
+
+  /** Get the Tx window type */
+  virtual enum TxWindowType getWindowType()=0;
+
+  /** Enable thread priority */
+  virtual void setPriority(float prio = 0.5) = 0;
+
+  /**
+	Read samples from the radio.
+	@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 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,
+                          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, bool isControl = false) = 0;
+
+  /** Update the alignment between the read and write timestamps */
+  virtual bool updateAlignment(TIMESTAMP timestamp)=0;
+
+  /** Set the transmitter frequency */
+  virtual bool setTxFreq(double wFreq, size_t chan = 0) = 0;
+
+  /** Set the receiver frequency */
+  virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0;
+
+  /** Returns the starting write Timestamp*/
+  virtual TIMESTAMP initialWriteTimestamp(void)=0;
+
+  /** Returns the starting read Timestamp*/
+  virtual TIMESTAMP initialReadTimestamp(void)=0;
+
+  /** returns the full-scale transmit amplitude **/
+  virtual double fullScaleInputValue()=0;
+
+  /** returns the full-scale receive amplitude **/
+  virtual double fullScaleOutputValue()=0;
+
+  /** sets the receive chan gain, returns the gain setting **/
+  virtual double setRxGain(double dB, size_t chan = 0) = 0;
+
+  /** gets the current receive gain **/
+  virtual double getRxGain(size_t chan = 0) = 0;
+
+  /** return maximum Rx Gain **/
+  virtual double maxRxGain(void) = 0;
+
+  /** return minimum Rx Gain **/
+  virtual double minRxGain(void) = 0;
+
+  /** sets the transmit chan gain, returns the gain setting **/
+  virtual double setTxGain(double dB, size_t chan = 0) = 0;
+
+  /** return maximum Tx Gain **/
+  virtual double maxTxGain(void) = 0;
+
+  /** return minimum Tx Gain **/
+  virtual double minTxGain(void) = 0;
+
+  /** sets the RX path to use, returns true if successful and false otherwise */
+  virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) = 0;
+
+  /** return the used RX path */
+  virtual std::string getRxAntenna(size_t chan = 0) = 0;
+
+  /** sets the RX path to use, returns true if successful and false otherwise */
+  virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) = 0;
+
+  /** return the used RX path */
+  virtual std::string getTxAntenna(size_t chan = 0) = 0;
+
+  /** Return internal status values */
+  virtual double getTxFreq(size_t chan = 0) = 0;
+  virtual double getRxFreq(size_t chan = 0) = 0;
+  virtual double getSampleRate()=0;
+  virtual double numberRead()=0;
+  virtual double numberWritten()=0;
+
+};
+
+#endif
diff --git a/Transceiver52M/device/uhd/Makefile.am b/Transceiver52M/device/uhd/Makefile.am
new file mode 100644
index 0000000..bb34d2f
--- /dev/null
+++ b/Transceiver52M/device/uhd/Makefile.am
@@ -0,0 +1,8 @@
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
+AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
+
+noinst_LTLIBRARIES = libdevice.la
+
+libdevice_la_SOURCES = UHDDevice.cpp
diff --git a/Transceiver52M/device/uhd/UHDDevice.cpp b/Transceiver52M/device/uhd/UHDDevice.cpp
new file mode 100644
index 0000000..4466da4
--- /dev/null
+++ b/Transceiver52M/device/uhd/UHDDevice.cpp
@@ -0,0 +1,1576 @@
+/*
+ * Device support for Ettus Research UHD driver
+ *
+ * Copyright 2010,2011 Free Software Foundation, Inc.
+ * Copyright (C) 2015 Ettus Research LLC
+ *
+ * Author: Tom Tsou <tom.tsou@ettus.com>
+ *
+ * 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/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+#include <map>
+#include "radioDevice.h"
+#include "Threads.h"
+#include "Logger.h"
+#include <uhd/version.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/usrp/multi_usrp.hpp>
+#include <uhd/utils/thread_priority.hpp>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef USE_UHD_3_11
+#include <uhd/utils/msg.hpp>
+#endif
+
+#define USRP_TX_AMPL     0.3
+#define UMTRX_TX_AMPL    0.7
+#define LIMESDR_TX_AMPL  0.3
+#define SAMPLE_BUF_SZ    (1 << 20)
+
+/*
+ * UHD timeout value on streaming (re)start
+ *
+ * Allow some time for streaming to commence after the start command is issued,
+ * but consider a wait beyond one second to be a definite error condition.
+ */
+#define UHD_RESTART_TIMEOUT     1.0
+
+/*
+ * UmTRX specific settings
+ */
+#define UMTRX_VGA1_DEF   -18
+
+enum uhd_dev_type {
+	USRP1,
+	USRP2,
+	B100,
+	B200,
+	B210,
+	B2XX_MCBTS,
+	E1XX,
+	E3XX,
+	X3XX,
+	UMTRX,
+	LIMESDR,
+};
+
+/*
+ * USRP version dependent device timings
+ */
+#if defined(USE_UHD_3_9) || defined(USE_UHD_3_11)
+#define B2XX_TIMING_1SPS	1.7153e-4
+#define B2XX_TIMING_4SPS	1.1696e-4
+#define B2XX_TIMING_4_4SPS	6.18462e-5
+#define B2XX_TIMING_MCBTS	7e-5
+#else
+#define B2XX_TIMING_1SPS	9.9692e-5
+#define B2XX_TIMING_4SPS	6.9248e-5
+#define B2XX_TIMING_4_4SPS	4.52308e-5
+#define B2XX_TIMING_MCBTS	6.42452e-5
+#endif
+
+/*
+ * Tx / Rx sample offset values. In a perfect world, there is no group delay
+ * though analog components, and behaviour through digital filters exactly
+ * matches calculated values. In reality, there are unaccounted factors,
+ * which are captured in these empirically measured (using a loopback test)
+ * timing correction values.
+ *
+ * Notes:
+ *   USRP1 with timestamps is not supported by UHD.
+ */
+
+/* Device Type, Tx-SPS, Rx-SPS */
+typedef std::tuple<uhd_dev_type, int, int> dev_key;
+
+/* Device parameter descriptor */
+struct dev_desc {
+	unsigned channels;
+	double mcr;
+	double rate;
+	double offset;
+	std::string str;
+};
+
+static const std::map<dev_key, dev_desc> dev_param_map {
+	{ std::make_tuple(USRP2, 1, 1), { 1, 0.0,  390625,  1.2184e-4,  "N2XX 1 SPS"         } },
+	{ std::make_tuple(USRP2, 4, 1), { 1, 0.0,  390625,  7.6547e-5,  "N2XX 4/1 Tx/Rx SPS" } },
+	{ std::make_tuple(USRP2, 4, 4), { 1, 0.0,  390625,  4.6080e-5,  "N2XX 4 SPS"         } },
+	{ std::make_tuple(B100,  1, 1), { 1, 0.0,  400000,  1.2104e-4,  "B100 1 SPS"         } },
+	{ std::make_tuple(B100,  4, 1), { 1, 0.0,  400000,  7.9307e-5,  "B100 4/1 Tx/Rx SPS" } },
+	{ std::make_tuple(B200,  1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS"   } },
+	{ std::make_tuple(B200,  4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } },
+	{ std::make_tuple(B200,  4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
+	{ std::make_tuple(B210,  1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS"    } },
+	{ std::make_tuple(B210,  4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } },
+	{ std::make_tuple(B210,  4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } },
+	{ std::make_tuple(E1XX,  1, 1), { 1, 52e6, GSMRATE, 9.5192e-5,  "E1XX 1 SPS"         } },
+	{ std::make_tuple(E1XX,  4, 1), { 1, 52e6, GSMRATE, 6.5571e-5,  "E1XX 4/1 Tx/Rx SPS" } },
+	{ std::make_tuple(E3XX,  1, 1), { 2, 26e6, GSMRATE, 1.8462e-4,  "E3XX 1 SPS"         } },
+	{ std::make_tuple(E3XX,  4, 1), { 2, 26e6, GSMRATE, 1.2923e-4,  "E3XX 4/1 Tx/Rx SPS" } },
+	{ std::make_tuple(X3XX,  1, 1), { 2, 0.0,  390625,  1.5360e-4,  "X3XX 1 SPS"         } },
+	{ std::make_tuple(X3XX,  4, 1), { 2, 0.0,  390625,  1.1264e-4,  "X3XX 4/1 Tx/Rx SPS" } },
+	{ std::make_tuple(X3XX,  4, 4), { 2, 0.0,  390625,  5.6567e-5,  "X3XX 4 SPS"         } },
+	{ std::make_tuple(UMTRX, 1, 1), { 2, 0.0,  GSMRATE, 9.9692e-5,  "UmTRX 1 SPS"        } },
+	{ std::make_tuple(UMTRX, 4, 1), { 2, 0.0,  GSMRATE, 7.3846e-5,  "UmTRX 4/1 Tx/Rx SPS"} },
+	{ 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" } },
+};
+
+/*
+    Sample Buffer - Allows reading and writing of timed samples using osmo-trx
+                    or UHD style timestamps. Time conversions are handled
+                    internally or accessable through the static convert calls.
+*/
+class smpl_buf {
+public:
+	/** Sample buffer constructor
+	    @param len number of 32-bit samples the buffer should hold
+	    @param rate sample clockrate
+	    @param timestamp
+	*/
+	smpl_buf(size_t len, double rate);
+	~smpl_buf();
+
+	/** Query number of samples available for reading
+	    @param timestamp time of first sample
+	    @return number of available samples or error
+	*/
+	ssize_t avail_smpls(TIMESTAMP timestamp) const;
+	ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
+
+	/** Read and write
+	    @param buf pointer to buffer
+	    @param len number of samples desired to read or write
+	    @param timestamp time of first stample
+	    @return number of actual samples read or written or error
+	*/
+	ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
+	ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
+	ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
+	ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
+
+	/** Buffer status string
+	    @return a formatted string describing internal buffer state
+	*/
+	std::string str_status(size_t ts) const;
+
+	/** Formatted error string
+	    @param code an error code
+	    @return a formatted error string
+	*/
+	static std::string str_code(ssize_t code);
+
+	enum err_code {
+		ERROR_TIMESTAMP = -1,
+		ERROR_READ = -2,
+		ERROR_WRITE = -3,
+		ERROR_OVERFLOW = -4
+	};
+
+private:
+	uint32_t *data;
+	size_t buf_len;
+
+	double clk_rt;
+
+	TIMESTAMP time_start;
+	TIMESTAMP time_end;
+
+	size_t data_start;
+	size_t data_end;
+};
+
+/*
+    uhd_device - UHD implementation of the Device interface. Timestamped samples
+                are sent to and received from the device. An intermediate buffer
+                on the receive side collects and aligns packets of samples.
+                Events and errors such as underruns are reported asynchronously
+                by the device and received in a separate thread.
+*/
+class uhd_device : public RadioDevice {
+public:
+	uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
+		   size_t chans, double offset,
+		   const std::vector<std::string>& tx_paths,
+		   const std::vector<std::string>& rx_paths);
+	~uhd_device();
+
+	int open(const std::string &args, int ref, bool swap_channels);
+	bool start();
+	bool stop();
+	bool restart();
+	void setPriority(float prio);
+	enum TxWindowType getWindowType() { return tx_window; }
+
+	int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
+			TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
+
+	int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
+			 TIMESTAMP timestamp, bool isControl);
+
+	bool updateAlignment(TIMESTAMP timestamp);
+
+	bool setTxFreq(double wFreq, size_t chan);
+	bool setRxFreq(double wFreq, size_t chan);
+
+	TIMESTAMP initialWriteTimestamp();
+	TIMESTAMP initialReadTimestamp();
+
+	double fullScaleInputValue();
+	double fullScaleOutputValue();
+
+	double setRxGain(double db, size_t chan);
+	double getRxGain(size_t chan);
+	double maxRxGain(void) { return rx_gain_max; }
+	double minRxGain(void) { return rx_gain_min; }
+
+	double setTxGain(double db, size_t chan);
+	double maxTxGain(void) { return tx_gain_max; }
+	double minTxGain(void) { return tx_gain_min; }
+
+	double getTxFreq(size_t chan);
+	double getRxFreq(size_t chan);
+	double getRxFreq();
+
+	bool setRxAntenna(const std::string &ant, size_t chan);
+	std::string getRxAntenna(size_t chan);
+	bool setTxAntenna(const std::string &ant, size_t chan);
+	std::string getTxAntenna(size_t chan);
+
+	inline double getSampleRate() { return tx_rate; }
+	inline double numberRead() { return rx_pkt_cnt; }
+	inline double numberWritten() { return 0; }
+
+	/** Receive and process asynchronous message
+	    @return true if message received or false on timeout or error
+	*/
+	bool recv_async_msg();
+
+	enum err_code {
+		ERROR_TIMING = -1,
+		ERROR_TIMEOUT = -2,
+		ERROR_UNRECOVERABLE = -3,
+		ERROR_UNHANDLED = -4,
+	};
+
+private:
+	uhd::usrp::multi_usrp::sptr usrp_dev;
+	uhd::tx_streamer::sptr tx_stream;
+	uhd::rx_streamer::sptr rx_stream;
+	enum TxWindowType tx_window;
+	enum uhd_dev_type dev_type;
+
+	size_t tx_sps, rx_sps, chans;
+	double tx_rate, rx_rate;
+
+	double tx_gain_min, tx_gain_max;
+	double rx_gain_min, rx_gain_max;
+	double offset;
+
+	std::vector<double> tx_gains, rx_gains;
+	std::vector<double> tx_freqs, rx_freqs;
+	std::vector<std::string> tx_paths, rx_paths;
+	size_t tx_spp, rx_spp;
+
+	bool started;
+	bool aligned;
+
+	size_t rx_pkt_cnt;
+	size_t drop_cnt;
+	uhd::time_spec_t prev_ts;
+
+	TIMESTAMP ts_initial, ts_offset;
+	std::vector<smpl_buf *> rx_buffers;
+
+	void init_gains();
+	void set_channels(bool swap);
+	void set_rates();
+	bool set_antennas();
+	bool parse_dev_type();
+	bool flush_recv(size_t num_pkts);
+	int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
+
+	std::string str_code(uhd::rx_metadata_t metadata);
+	std::string str_code(uhd::async_metadata_t metadata);
+
+	uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
+	bool set_freq(double freq, size_t chan, bool tx);
+
+	Thread *async_event_thrd;
+	InterfaceType iface;
+	Mutex tune_lock;
+};
+
+void *async_event_loop(uhd_device *dev)
+{
+	dev->setPriority(0.43);
+
+	while (1) {
+		dev->recv_async_msg();
+		pthread_testcancel();
+	}
+
+	return NULL;
+}
+
+#ifndef USE_UHD_3_11
+/*
+    Catch and drop underrun 'U' and overrun 'O' messages from stdout
+    since we already report using the logging facility. Direct
+    everything else appropriately.
+ */
+void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
+{
+	switch (type) {
+	case uhd::msg::status:
+		LOG(INFO) << msg;
+		break;
+	case uhd::msg::warning:
+		LOG(WARNING) << msg;
+		break;
+	case uhd::msg::error:
+		LOG(ERR) << msg;
+		break;
+	case uhd::msg::fastpath:
+		break;
+	}
+}
+#endif
+
+static void thread_enable_cancel(bool cancel)
+{
+	cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
+		 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+}
+
+uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
+		       InterfaceType iface, size_t chans, double offset,
+		       const std::vector<std::string>& tx_paths,
+		       const std::vector<std::string>& rx_paths)
+	: tx_gain_min(0.0), tx_gain_max(0.0),
+	  rx_gain_min(0.0), rx_gain_max(0.0),
+	  tx_spp(0), rx_spp(0),
+	  started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
+	  prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
+{
+	this->tx_sps = tx_sps;
+	this->rx_sps = rx_sps;
+	this->chans = chans;
+	this->offset = offset;
+	this->iface = iface;
+	this->tx_paths = tx_paths;
+	this->rx_paths = rx_paths;
+}
+
+uhd_device::~uhd_device()
+{
+	stop();
+
+	for (size_t i = 0; i < rx_buffers.size(); i++)
+		delete rx_buffers[i];
+}
+
+void uhd_device::init_gains()
+{
+	uhd::gain_range_t range;
+
+	if (dev_type == UMTRX) {
+		std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
+		if (gain_stages[0] == "VGA") {
+			LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
+		}
+		if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
+			range = usrp_dev->get_tx_gain_range();
+			tx_gain_min = range.start();
+			tx_gain_max = range.stop();
+		} else {
+			range = usrp_dev->get_tx_gain_range("VGA2");
+			tx_gain_min = UMTRX_VGA1_DEF + range.start();
+			tx_gain_max = UMTRX_VGA1_DEF + range.stop();
+		}
+	} else {
+		range = usrp_dev->get_tx_gain_range();
+		tx_gain_min = range.start();
+		tx_gain_max = range.stop();
+	}
+	LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
+
+	range = usrp_dev->get_rx_gain_range();
+	rx_gain_min = range.start();
+	rx_gain_max = range.stop();
+	LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
+
+	for (size_t i = 0; i < tx_gains.size(); i++) {
+		double gain = (tx_gain_min + tx_gain_max) / 2;
+		LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
+		usrp_dev->set_tx_gain(gain, i);
+		tx_gains[i] = usrp_dev->get_tx_gain(i);
+	}
+
+	for (size_t i = 0; i < rx_gains.size(); i++) {
+		double gain = (rx_gain_min + rx_gain_max) / 2;
+		LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
+		usrp_dev->set_rx_gain(gain, i);
+		rx_gains[i] = usrp_dev->get_rx_gain(i);
+	}
+
+	return;
+
+}
+
+void uhd_device::set_rates()
+{
+	dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
+	if (desc.mcr != 0.0)
+		usrp_dev->set_master_clock_rate(desc.mcr);
+
+	tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate;
+	rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate;
+
+	usrp_dev->set_tx_rate(tx_rate);
+	usrp_dev->set_rx_rate(rx_rate);
+	tx_rate = usrp_dev->get_tx_rate();
+	rx_rate = usrp_dev->get_rx_rate();
+
+	ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
+	LOG(INFO) << "Rates configured for " << desc.str;
+}
+
+bool uhd_device::set_antennas()
+{
+	unsigned int i;
+
+	for (i = 0; i < tx_paths.size(); i++) {
+		if (tx_paths[i] == "")
+			continue;
+		LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i];
+		if (!setTxAntenna(tx_paths[i], i)) {
+			LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i];
+			return false;
+		}
+	}
+
+	for (i = 0; i < rx_paths.size(); i++) {
+		if (rx_paths[i] == "")
+			continue;
+		LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i];
+		if (!setRxAntenna(rx_paths[i], i)) {
+			LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i];
+			return false;
+		}
+	}
+	LOG(INFO) << "Antennas configured successfully";
+	return true;
+}
+
+double uhd_device::setTxGain(double db, size_t chan)
+{
+	if (iface == MULTI_ARFCN)
+		chan = 0;
+
+	if (chan >= tx_gains.size()) {
+		LOG(ALERT) << "Requested non-existent channel" << chan;
+		return 0.0f;
+	}
+
+	if (dev_type == UMTRX) {
+		std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
+		if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
+			usrp_dev->set_tx_gain(db, chan);
+		} else {
+			// New UHD versions support split configuration of
+			// Tx gain stages. We utilize this to set the gain
+			// configuration, optimal for the Tx signal quality.
+			// From our measurements, VGA1 must be 18dB plus-minus
+			// one and VGA2 is the best when 23dB or lower.
+			usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
+			usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
+		}
+	} else {
+		usrp_dev->set_tx_gain(db, chan);
+	}
+
+	tx_gains[chan] = usrp_dev->get_tx_gain(chan);
+
+	LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
+
+	return tx_gains[chan];
+}
+
+double uhd_device::setRxGain(double db, size_t chan)
+{
+	if (chan >= rx_gains.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return 0.0f;
+	}
+
+	usrp_dev->set_rx_gain(db, chan);
+	rx_gains[chan] = usrp_dev->get_rx_gain(chan);
+
+	LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
+
+	return rx_gains[chan];
+}
+
+double uhd_device::getRxGain(size_t chan)
+{
+	if (iface == MULTI_ARFCN)
+		chan = 0;
+
+	if (chan >= rx_gains.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return 0.0f;
+	}
+
+	return rx_gains[chan];
+}
+
+/*
+    Parse the UHD device tree and mboard name to find out what device we're
+    dealing with. We need the window type so that the transceiver knows how to
+    deal with the transport latency. Reject the USRP1 because UHD doesn't
+    support timestamped samples with it.
+ */
+bool uhd_device::parse_dev_type()
+{
+	uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
+	std::string devString = prop_tree->access<std::string>("/name").get();
+	std::string mboardString = usrp_dev->get_mboard_name();
+
+	const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
+		{ "B100",     { B100,    TX_WINDOW_USRP1 } },
+		{ "B200",     { B200,    TX_WINDOW_USRP1 } },
+		{ "B200mini", { B200,    TX_WINDOW_USRP1 } },
+		{ "B205mini", { B200,    TX_WINDOW_USRP1 } },
+		{ "B210",     { B210,    TX_WINDOW_USRP1 } },
+		{ "E100",     { E1XX,    TX_WINDOW_FIXED } },
+		{ "E110",     { E1XX,    TX_WINDOW_FIXED } },
+		{ "E310",     { E3XX,    TX_WINDOW_FIXED } },
+		{ "E3XX",     { E3XX,    TX_WINDOW_FIXED } },
+		{ "X300",     { X3XX,    TX_WINDOW_FIXED } },
+		{ "X310",     { X3XX,    TX_WINDOW_FIXED } },
+		{ "USRP2",    { USRP2,   TX_WINDOW_FIXED } },
+		{ "UmTRX",    { UMTRX,   TX_WINDOW_FIXED } },
+		{ "LimeSDR",  { LIMESDR, TX_WINDOW_FIXED } },
+	};
+
+	// Compare UHD motherboard and device strings */
+	auto mapIter = devStringMap.begin();
+	while (mapIter != devStringMap.end()) {
+		if (devString.find(mapIter->first) != std::string::npos ||
+		    mboardString.find(mapIter->first) != std::string::npos) {
+			dev_type = std::get<0>(mapIter->second);
+			tx_window = std::get<1>(mapIter->second);
+			return true;
+		}
+		mapIter++;
+	}
+
+	LOG(ALERT) << "Unsupported device " << devString;
+	return false;
+}
+
+/*
+ * Check for UHD version > 3.9.0 for E3XX support
+ */
+static bool uhd_e3xx_version_chk()
+{
+	std::string ver = uhd::get_version_string();
+	std::string major_str(ver.begin(), ver.begin() + 3);
+	std::string minor_str(ver.begin() + 4, ver.begin() + 7);
+
+	int major_val = atoi(major_str.c_str());
+	int minor_val = atoi(minor_str.c_str());
+
+	if (major_val < 3)
+		return false;
+	if (minor_val < 9)
+		return false;
+
+	return true;
+}
+
+void uhd_device::set_channels(bool swap)
+{
+	if (iface == MULTI_ARFCN) {
+		if (dev_type != B200 && dev_type != B210)
+			throw std::invalid_argument("Device does not support MCBTS");
+		dev_type = B2XX_MCBTS;
+		chans = 1;
+	}
+
+	if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
+		throw std::invalid_argument("Device does not support number of requested channels");
+
+	std::string subdev_string;
+	switch (dev_type) {
+	case B210:
+	case E3XX:
+		if (chans == 1)
+			subdev_string = swap ? "A:B" : "A:A";
+		else if (chans == 2)
+			subdev_string = swap ? "A:B A:A" : "A:A A:B";
+		break;
+	case X3XX:
+	case UMTRX:
+		if (chans == 1)
+			subdev_string = swap ? "B:0" : "A:0";
+		else if (chans == 2)
+			subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
+		break;
+	default:
+		break;
+	}
+
+	if (!subdev_string.empty()) {
+		uhd::usrp::subdev_spec_t spec(subdev_string);
+		usrp_dev->set_tx_subdev_spec(spec);
+		usrp_dev->set_rx_subdev_spec(spec);
+	}
+}
+
+int uhd_device::open(const std::string &args, int ref, bool swap_channels)
+{
+	const char *refstr;
+
+	// Find UHD devices
+	uhd::device_addr_t addr(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 -1;
+	}
+
+	// Use the first found device
+	LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
+	try {
+		usrp_dev = uhd::usrp::multi_usrp::make(addr);
+	} catch(...) {
+		LOG(ALERT) << "UHD make failed, device " << args;
+		return -1;
+	}
+
+	// Check for a valid device type and set bus type
+	if (!parse_dev_type())
+		return -1;
+
+	if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
+		LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater";
+		return -1;
+	}
+
+	try {
+		set_channels(swap_channels);
+        } catch (const std::exception &e) {
+		LOG(ALERT) << "Channel setting failed - " << e.what();
+		return -1;
+	}
+
+	if (!set_antennas()) {
+		LOG(ALERT) << "UHD antenna setting failed";
+		return -1;
+	}
+
+	tx_freqs.resize(chans);
+	rx_freqs.resize(chans);
+	tx_gains.resize(chans);
+	rx_gains.resize(chans);
+	rx_buffers.resize(chans);
+
+	switch (ref) {
+	case REF_INTERNAL:
+		refstr = "internal";
+		break;
+	case REF_EXTERNAL:
+		refstr = "external";
+		break;
+	case REF_GPS:
+		refstr = "gpsdo";
+		break;
+	default:
+		LOG(ALERT) << "Invalid reference type";
+		return -1;
+	}
+
+	usrp_dev->set_clock_source(refstr);
+
+	try {
+		set_rates();
+        } catch (const std::exception &e) {
+		LOG(ALERT) << "UHD rate setting failed - " << e.what();
+		return -1;
+	}
+
+	// Set RF frontend bandwidth
+	if (dev_type == UMTRX) {
+		// Setting LMS6002D LPF to 500kHz gives us the best signal quality
+		for (size_t i = 0; i < chans; i++) {
+			usrp_dev->set_tx_bandwidth(500*1000*2, i);
+			usrp_dev->set_rx_bandwidth(500*1000*2, i);
+		}
+	} else if (dev_type == LIMESDR) {
+		for (size_t i = 0; i < chans; i++) {
+			usrp_dev->set_tx_bandwidth(5e6, i);
+			usrp_dev->set_rx_bandwidth(5e6, i);
+		}
+	}
+
+	/* Create TX and RX streamers */
+	uhd::stream_args_t stream_args("sc16");
+	for (size_t i = 0; i < chans; i++)
+		stream_args.channels.push_back(i);
+
+	tx_stream = usrp_dev->get_tx_stream(stream_args);
+	rx_stream = usrp_dev->get_rx_stream(stream_args);
+
+	/* Number of samples per over-the-wire packet */
+	tx_spp = tx_stream->get_max_num_samps();
+	rx_spp = rx_stream->get_max_num_samps();
+
+	// Create receive buffer
+	size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
+	for (size_t i = 0; i < rx_buffers.size(); i++)
+		rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
+
+	// Initialize and shadow gain values
+	init_gains();
+
+	// Print configuration
+	LOG(INFO) << "\n" << usrp_dev->get_pp_string();
+
+	if (iface == MULTI_ARFCN)
+		return MULTI_ARFCN;
+
+	switch (dev_type) {
+	case B100:
+		return RESAMP_64M;
+	case USRP2:
+	case X3XX:
+		return RESAMP_100M;
+	case B200:
+	case B210:
+	case E1XX:
+	case E3XX:
+	case LIMESDR:
+	default:
+		break;
+	}
+
+	return NORMAL;
+}
+
+bool uhd_device::flush_recv(size_t num_pkts)
+{
+	uhd::rx_metadata_t md;
+	size_t num_smpls;
+	float timeout = UHD_RESTART_TIMEOUT;
+
+	std::vector<std::vector<short> >
+		pkt_bufs(chans, std::vector<short>(2 * rx_spp));
+
+	std::vector<short *> pkt_ptrs;
+	for (size_t i = 0; i < pkt_bufs.size(); i++)
+		pkt_ptrs.push_back(&pkt_bufs[i].front());
+
+	ts_initial = 0;
+	while (!ts_initial || (num_pkts-- > 0)) {
+		num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
+					    timeout, true);
+		if (!num_smpls) {
+			switch (md.error_code) {
+			case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
+				LOG(ALERT) << "Device timed out";
+				return false;
+			default:
+				continue;
+			}
+		}
+
+		ts_initial = md.time_spec.to_ticks(rx_rate);
+	}
+
+	LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
+
+	return true;
+}
+
+bool uhd_device::restart()
+{
+	/* Allow 100 ms delay to align multi-channel streams */
+	double delay = 0.1;
+
+	aligned = false;
+
+	uhd::time_spec_t current = usrp_dev->get_time_now();
+
+	uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
+	cmd.stream_now = false;
+	cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
+
+	usrp_dev->issue_stream_cmd(cmd);
+
+	return flush_recv(10);
+}
+
+bool uhd_device::start()
+{
+	LOG(INFO) << "Starting USRP...";
+
+	if (started) {
+		LOG(ERR) << "Device already started";
+		return false;
+	}
+
+#ifndef USE_UHD_3_11
+	// Register msg handler
+	uhd::msg::register_handler(&uhd_msg_handler);
+#endif
+	// Start asynchronous event (underrun check) loop
+	async_event_thrd = new Thread();
+	async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
+
+	// Start streaming
+	if (!restart())
+		return false;
+
+	// Display usrp time
+	double time_now = usrp_dev->get_time_now().get_real_secs();
+	LOG(INFO) << "The current time is " << time_now << " seconds";
+
+	started = true;
+	return true;
+}
+
+bool uhd_device::stop()
+{
+	if (!started)
+		return false;
+
+	uhd::stream_cmd_t stream_cmd =
+		uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
+
+	usrp_dev->issue_stream_cmd(stream_cmd);
+
+	async_event_thrd->cancel();
+	async_event_thrd->join();
+	delete async_event_thrd;
+
+	started = false;
+	return true;
+}
+
+void uhd_device::setPriority(float prio)
+{
+	uhd::set_thread_priority_safe(prio);
+	return;
+}
+
+int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
+{
+	if (!num_smpls) {
+		LOG(ERR) << str_code(md);
+
+		switch (md.error_code) {
+		case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
+			LOG(ALERT) << "UHD: Receive timed out";
+			return ERROR_TIMEOUT;
+		case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
+		case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
+		case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
+		case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
+		default:
+			return ERROR_UNHANDLED;
+		}
+	}
+
+	// Missing timestamp
+	if (!md.has_time_spec) {
+		LOG(ALERT) << "UHD: Received packet missing timestamp";
+		return ERROR_UNRECOVERABLE;
+	}
+
+	// Monotonicity check
+	if (md.time_spec < prev_ts) {
+		LOG(ALERT) << "UHD: Loss of monotonic time";
+		LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
+			   << "Previous time: " << prev_ts.get_real_secs();
+		return ERROR_TIMING;
+	}
+
+	// Workaround for UHD tick rounding bug
+	TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
+	if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
+		md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
+
+	prev_ts = md.time_spec;
+
+	return 0;
+}
+
+int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
+			    TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
+{
+	ssize_t rc;
+	uhd::time_spec_t ts;
+	uhd::rx_metadata_t metadata;
+
+	if (bufs.size() != chans) {
+		LOG(ALERT) << "Invalid channel combination " << bufs.size();
+		return -1;
+	}
+
+	*overrun = false;
+	*underrun = false;
+
+	// Shift read time with respect to transmit clock
+	timestamp += ts_offset;
+
+	ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
+	LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
+
+	// Check that timestamp is valid
+	rc = rx_buffers[0]->avail_smpls(timestamp);
+	if (rc < 0) {
+		LOG(ERR) << rx_buffers[0]->str_code(rc);
+		LOG(ERR) << rx_buffers[0]->str_status(timestamp);
+		return 0;
+	}
+
+	// Create vector buffer
+	std::vector<std::vector<short> >
+		pkt_bufs(chans, std::vector<short>(2 * rx_spp));
+
+	std::vector<short *> pkt_ptrs;
+	for (size_t i = 0; i < pkt_bufs.size(); i++)
+		pkt_ptrs.push_back(&pkt_bufs[i].front());
+
+	// Receive samples from the usrp until we have enough
+	while (rx_buffers[0]->avail_smpls(timestamp) < len) {
+		thread_enable_cancel(false);
+		size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
+						   metadata, 0.1, true);
+		thread_enable_cancel(true);
+
+		rx_pkt_cnt++;
+
+		// Check for errors
+		rc = check_rx_md_err(metadata, num_smpls);
+		switch (rc) {
+		case ERROR_UNRECOVERABLE:
+			LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
+			LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
+			exit(-1);
+		case ERROR_TIMEOUT:
+			// Assume stopping condition
+			return 0;
+		case ERROR_TIMING:
+			restart();
+		case ERROR_UNHANDLED:
+			continue;
+		}
+
+		ts = metadata.time_spec;
+		LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
+
+		for (size_t i = 0; i < rx_buffers.size(); i++) {
+			rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
+						  num_smpls,
+						  metadata.time_spec);
+
+			// Continue on local overrun, exit on other errors
+			if ((rc < 0)) {
+				LOG(ERR) << rx_buffers[i]->str_code(rc);
+				LOG(ERR) << rx_buffers[i]->str_status(timestamp);
+				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)) {
+			LOG(ERR) << rx_buffers[i]->str_code(rc);
+			LOG(ERR) << rx_buffers[i]->str_status(timestamp);
+			return 0;
+		}
+	}
+
+	return len;
+}
+
+int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
+			     unsigned long long timestamp,bool isControl)
+{
+	uhd::tx_metadata_t metadata;
+	metadata.has_time_spec = true;
+	metadata.start_of_burst = false;
+	metadata.end_of_burst = false;
+	metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
+
+	*underrun = false;
+
+	// No control packets
+	if (isControl) {
+		LOG(ERR) << "Control packets not supported";
+		return 0;
+	}
+
+	if (bufs.size() != chans) {
+		LOG(ALERT) << "Invalid channel combination " << bufs.size();
+		return -1;
+	}
+
+	// Drop a fixed number of packets (magic value)
+	if (!aligned) {
+		drop_cnt++;
+
+		if (drop_cnt == 1) {
+			LOG(DEBUG) << "Aligning transmitter: stop burst";
+			*underrun = true;
+			metadata.end_of_burst = true;
+		} else if (drop_cnt < 30) {
+			LOG(DEBUG) << "Aligning transmitter: packet advance";
+			return len;
+		} else {
+			LOG(DEBUG) << "Aligning transmitter: start burst";
+			metadata.start_of_burst = true;
+			aligned = true;
+			drop_cnt = 0;
+		}
+	}
+
+	thread_enable_cancel(false);
+	size_t num_smpls = tx_stream->send(bufs, len, metadata);
+	thread_enable_cancel(true);
+
+	if (num_smpls != (unsigned) len) {
+		LOG(ALERT) << "UHD: Device send timed out";
+	}
+
+	return num_smpls;
+}
+
+bool uhd_device::updateAlignment(TIMESTAMP timestamp)
+{
+	return true;
+}
+
+uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
+{
+	double rf_spread, rf_freq;
+	std::vector<double> freqs;
+	uhd::tune_request_t treq(freq);
+
+	if (dev_type == UMTRX) {
+		if (offset != 0.0)
+			return uhd::tune_request_t(freq, offset);
+
+		// Don't use DSP tuning, because LMS6002D PLL steps are small enough.
+		// We end up with DSP tuning just for 2-3Hz, which is meaningless and
+		// only distort the signal (because cordic is not ideal).
+		treq.target_freq = freq;
+		treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
+		treq.rf_freq = freq;
+		treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
+		treq.dsp_freq = 0.0;
+		return treq;
+	} else if (chans == 1) {
+		if (offset == 0.0)
+			return treq;
+
+		return uhd::tune_request_t(freq, offset);
+	} else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
+		LOG(ALERT) << chans << " channels unsupported";
+		return treq;
+	}
+
+	if (tx)
+		freqs = tx_freqs;
+	else
+		freqs = rx_freqs;
+
+	/* Tune directly if other channel isn't tuned */
+	if (freqs[!chan] < 10.0)
+		return treq;
+
+	/* Find center frequency between channels */
+	rf_spread = fabs(freqs[!chan] - freq);
+	if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
+		LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
+		return treq;
+	}
+
+	rf_freq = (freqs[!chan] + freq) / 2.0f;
+
+	treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
+	treq.target_freq = freq;
+	treq.rf_freq = rf_freq;
+
+	return treq;
+}
+
+bool uhd_device::set_freq(double freq, size_t chan, bool tx)
+{
+	std::vector<double> freqs;
+	uhd::tune_result_t tres;
+	uhd::tune_request_t treq = select_freq(freq, chan, tx);
+
+	if (tx) {
+		tres = usrp_dev->set_tx_freq(treq, chan);
+		tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
+	} else {
+		tres = usrp_dev->set_rx_freq(treq, chan);
+		rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
+	}
+	LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
+
+	if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
+		return true;
+
+	/* Manual RF policy means we intentionally tuned with a baseband
+	 * offset for dual-channel purposes. Now retune the other channel
+	 * with the opposite corresponding frequency offset
+	 */
+	if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
+		if (tx) {
+			treq = select_freq(tx_freqs[!chan], !chan, true);
+			tres = usrp_dev->set_tx_freq(treq, !chan);
+			tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
+		} else {
+			treq = select_freq(rx_freqs[!chan], !chan, false);
+			tres = usrp_dev->set_rx_freq(treq, !chan);
+			rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
+
+		}
+		LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
+	}
+
+	return true;
+}
+
+bool uhd_device::setTxFreq(double wFreq, size_t chan)
+{
+	if (chan >= tx_freqs.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return false;
+	}
+	ScopedLock lock(tune_lock);
+
+	return set_freq(wFreq, chan, true);
+}
+
+bool uhd_device::setRxFreq(double wFreq, size_t chan)
+{
+	if (chan >= rx_freqs.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return false;
+	}
+	ScopedLock lock(tune_lock);
+
+	return set_freq(wFreq, chan, false);
+}
+
+double uhd_device::getTxFreq(size_t chan)
+{
+	if (chan >= tx_freqs.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return 0.0;
+	}
+
+	return tx_freqs[chan];
+}
+
+double uhd_device::getRxFreq(size_t chan)
+{
+	if (chan >= rx_freqs.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return 0.0;
+	}
+
+	return rx_freqs[chan];
+}
+
+bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
+{
+	std::vector<std::string> avail;
+	if (chan >= rx_paths.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return false;
+	}
+
+	avail = usrp_dev->get_rx_antennas(chan);
+	if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
+		LOG(ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan;
+		LOG(INFO) << "Available Rx antennas: ";
+		for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
+			LOG(INFO) << "- '" << *i << "'";
+		return false;
+	}
+	usrp_dev->set_rx_antenna(ant, chan);
+	rx_paths[chan] = usrp_dev->get_rx_antenna(chan);
+
+	if (ant != rx_paths[chan]) {
+		LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan];
+		return false;
+	}
+
+	return true;
+}
+
+std::string uhd_device::getRxAntenna(size_t chan)
+{
+	if (chan >= rx_paths.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return "";
+	}
+	return usrp_dev->get_rx_antenna(chan);
+}
+
+bool uhd_device::setTxAntenna(const std::string &ant, size_t chan)
+{
+	std::vector<std::string> avail;
+	if (chan >= tx_paths.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return false;
+	}
+
+	avail = usrp_dev->get_tx_antennas(chan);
+	if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
+		LOG(ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan;
+		LOG(INFO) << "Available Tx antennas: ";
+		for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
+			LOG(INFO) << "- '" << *i << "'";
+		return false;
+	}
+	usrp_dev->set_tx_antenna(ant, chan);
+	tx_paths[chan] = usrp_dev->get_tx_antenna(chan);
+
+	if (ant != tx_paths[chan]) {
+		LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan];
+		return false;
+	}
+
+	return true;
+}
+
+std::string uhd_device::getTxAntenna(size_t chan)
+{
+	if (chan >= tx_paths.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return "";
+	}
+	return usrp_dev->get_tx_antenna(chan);
+}
+
+/*
+ * Only allow sampling the Rx path lower than Tx and not vice-versa.
+ * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
+ * combination.
+ */
+TIMESTAMP uhd_device::initialWriteTimestamp()
+{
+	if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
+		return ts_initial;
+	else
+		return ts_initial * tx_sps;
+}
+
+TIMESTAMP uhd_device::initialReadTimestamp()
+{
+	return ts_initial;
+}
+
+double uhd_device::fullScaleInputValue()
+{
+	if (dev_type == LIMESDR)
+		return (double) SHRT_MAX * LIMESDR_TX_AMPL;
+	if (dev_type == UMTRX)
+		return (double) SHRT_MAX * UMTRX_TX_AMPL;
+	else
+		return (double) SHRT_MAX * USRP_TX_AMPL;
+}
+
+double uhd_device::fullScaleOutputValue()
+{
+	return (double) SHRT_MAX;
+}
+
+bool uhd_device::recv_async_msg()
+{
+	uhd::async_metadata_t md;
+
+	thread_enable_cancel(false);
+	bool rc = usrp_dev->get_device()->recv_async_msg(md);
+	thread_enable_cancel(true);
+	if (!rc)
+		return false;
+
+	// Assume that any error requires resynchronization
+	if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
+		aligned = false;
+
+		if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
+		    (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
+			LOG(ERR) << str_code(md);
+		}
+	}
+
+	return true;
+}
+
+std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
+{
+	std::ostringstream ost("UHD: ");
+
+	switch (metadata.error_code) {
+	case uhd::rx_metadata_t::ERROR_CODE_NONE:
+		ost << "No error";
+		break;
+	case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
+		ost << "No packet received, implementation timed-out";
+		break;
+	case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
+		ost << "A stream command was issued in the past";
+		break;
+	case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
+		ost << "Expected another stream command";
+		break;
+	case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
+		ost << "An internal receive buffer has filled";
+		break;
+	case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
+		ost << "Multi-channel alignment failed";
+		break;
+	case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
+		ost << "The packet could not be parsed";
+		break;
+	default:
+		ost << "Unknown error " << metadata.error_code;
+	}
+
+	if (metadata.has_time_spec)
+		ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
+
+	return ost.str();
+}
+
+std::string uhd_device::str_code(uhd::async_metadata_t metadata)
+{
+	std::ostringstream ost("UHD: ");
+
+	switch (metadata.event_code) {
+	case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
+		ost << "A packet was successfully transmitted";
+		break;
+	case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
+		ost << "An internal send buffer has emptied";
+		break;
+	case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
+		ost << "Packet loss between host and device";
+		break;
+	case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
+		ost << "Packet time was too late or too early";
+		break;
+	case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
+		ost << "Underflow occurred inside a packet";
+		break;
+	case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
+		ost << "Packet loss within a burst";
+		break;
+	default:
+		ost << "Unknown error " << metadata.event_code;
+	}
+
+	if (metadata.has_time_spec)
+		ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
+
+	return ost.str();
+}
+
+smpl_buf::smpl_buf(size_t len, double rate)
+	: buf_len(len), clk_rt(rate),
+	  time_start(0), time_end(0), data_start(0), data_end(0)
+{
+	data = new uint32_t[len];
+}
+
+smpl_buf::~smpl_buf()
+{
+	delete[] data;
+}
+
+ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
+{
+	if (timestamp < time_start)
+		return ERROR_TIMESTAMP;
+	else if (timestamp >= time_end)
+		return 0;
+	else
+		return time_end - timestamp;
+}
+
+ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
+{
+	return avail_smpls(timespec.to_ticks(clk_rt));
+}
+
+ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
+{
+	int type_sz = 2 * sizeof(short);
+
+	// Check for valid read
+	if (timestamp < time_start)
+		return ERROR_TIMESTAMP;
+	if (timestamp >= time_end)
+		return 0;
+	if (len >= buf_len)
+		return ERROR_READ;
+
+	// How many samples should be copied
+	size_t num_smpls = time_end - timestamp;
+	if (num_smpls > len)
+		num_smpls = len;
+
+	// Starting index
+	size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
+
+	// Read it
+	if (read_start + num_smpls < buf_len) {
+		size_t numBytes = len * type_sz;
+		memcpy(buf, data + read_start, numBytes);
+	} else {
+		size_t first_cp = (buf_len - read_start) * type_sz;
+		size_t second_cp = len * type_sz - first_cp;
+
+		memcpy(buf, data + read_start, first_cp);
+		memcpy((char*) buf + first_cp, data, second_cp);
+	}
+
+	data_start = (read_start + len) % buf_len;
+	time_start = timestamp + len;
+
+	if (time_start > time_end)
+		return ERROR_READ;
+	else
+		return num_smpls;
+}
+
+ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
+{
+	return read(buf, len, ts.to_ticks(clk_rt));
+}
+
+ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
+{
+	int type_sz = 2 * sizeof(short);
+
+	// Check for valid write
+	if ((len == 0) || (len >= buf_len))
+		return ERROR_WRITE;
+	if ((timestamp + len) <= time_end)
+		return ERROR_TIMESTAMP;
+
+	if (timestamp < time_end) {
+		LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
+		uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
+		LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
+		// Do not return error here, because it's a rounding error and is not fatal
+	}
+	if (timestamp > time_end && time_end != 0) {
+		LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
+		uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
+		LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
+		// Do not return error here, because it's a rounding error and is not fatal
+	}
+
+	// Starting index
+	size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
+
+	// Write it
+	if ((write_start + len) < buf_len) {
+		size_t numBytes = len * type_sz;
+		memcpy(data + write_start, buf, numBytes);
+	} else {
+		size_t first_cp = (buf_len - write_start) * type_sz;
+		size_t second_cp = len * type_sz - first_cp;
+
+		memcpy(data + write_start, buf, first_cp);
+		memcpy(data, (char*) buf + first_cp, second_cp);
+	}
+
+	data_end = (write_start + len) % buf_len;
+	time_end = timestamp + len;
+
+	if (!data_start)
+		data_start = write_start;
+
+	if (((write_start + len) > buf_len) && (data_end > data_start))
+		return ERROR_OVERFLOW;
+	else if (time_end <= time_start)
+		return ERROR_WRITE;
+	else
+		return len;
+}
+
+ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
+{
+	return write(buf, len, ts.to_ticks(clk_rt));
+}
+
+std::string smpl_buf::str_status(size_t ts) const
+{
+	std::ostringstream ost("Sample buffer: ");
+
+	ost << "timestamp = " << ts;
+	ost << ", length = " << buf_len;
+	ost << ", time_start = " << time_start;
+	ost << ", time_end = " << time_end;
+	ost << ", data_start = " << data_start;
+	ost << ", data_end = " << data_end;
+
+	return ost.str();
+}
+
+std::string smpl_buf::str_code(ssize_t code)
+{
+	switch (code) {
+	case ERROR_TIMESTAMP:
+		return "Sample buffer: Requested timestamp is not valid";
+	case ERROR_READ:
+		return "Sample buffer: Read error";
+	case ERROR_WRITE:
+		return "Sample buffer: Write error";
+	case ERROR_OVERFLOW:
+		return "Sample buffer: Overrun";
+	default:
+		return "Sample buffer: Unknown error";
+	}
+}
+
+RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
+			       InterfaceType iface, size_t chans, double offset,
+			       const std::vector<std::string>& tx_paths,
+			       const std::vector<std::string>& rx_paths)
+{
+	return new uhd_device(tx_sps, rx_sps, iface, chans, offset, tx_paths, rx_paths);
+}
diff --git a/Transceiver52M/device/usrp1/Makefile.am b/Transceiver52M/device/usrp1/Makefile.am
new file mode 100644
index 0000000..d99874a
--- /dev/null
+++ b/Transceiver52M/device/usrp1/Makefile.am
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
+AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(USRP_CFLAGS)
+
+noinst_HEADERS = USRPDevice.h
+
+noinst_LTLIBRARIES = libdevice.la
+
+libdevice_la_SOURCES = USRPDevice.cpp
diff --git a/Transceiver52M/device/usrp1/USRPDevice.cpp b/Transceiver52M/device/usrp1/USRPDevice.cpp
new file mode 100644
index 0000000..f7f24e9
--- /dev/null
+++ b/Transceiver52M/device/usrp1/USRPDevice.cpp
@@ -0,0 +1,648 @@
+/*
+* Copyright 2008, 2009 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Affero Public License.
+* See the COPYING file in the main directory for details.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+	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/>.
+
+*/
+
+
+/*
+	Compilation Flags
+
+	SWLOOPBACK	compile for software loopback testing
+*/
+
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include "Logger.h"
+#include "Threads.h"
+#include "USRPDevice.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+using namespace std;
+
+enum dboardConfigType {
+  TXA_RXB,
+  TXB_RXA,
+  TXA_RXA,
+  TXB_RXB
+};
+
+#ifdef SINGLEDB
+const dboardConfigType dboardConfig = TXA_RXA;
+#else
+const dboardConfigType dboardConfig = TXA_RXB;
+#endif
+
+const double USRPDevice::masterClockRate = 52.0e6;
+
+USRPDevice::USRPDevice(size_t sps)
+{
+  LOG(INFO) << "creating USRP device...";
+
+  this->sps = sps;
+  decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps));
+  actualSampleRate = masterClockRate/decimRate;
+  rxGain = 0;
+
+  /*
+   * Undetermined delay b/w ping response timestamp and true
+   * receive timestamp. Values are empirically measured. With
+   * split sample rate Tx/Rx - 4/1 sps we need to need to
+   * compensate for advance rather than delay.
+   */
+  if (sps == 1)
+    pingOffset = 272;
+  else if (sps == 4)
+    pingOffset = 269 - 7500;
+  else
+    pingOffset = 0;
+
+#ifdef SWLOOPBACK
+  samplePeriod = 1.0e6/actualSampleRate;
+  loopbackBufferSize = 0;
+  gettimeofday(&lastReadTime,NULL);
+  firstRead = false;
+#endif
+}
+
+int USRPDevice::open(const std::string &, int, bool)
+{
+  writeLock.unlock();
+
+  LOG(INFO) << "opening USRP device..";
+#ifndef SWLOOPBACK
+  string rbf = "std_inband.rbf";
+  //string rbf = "inband_1rxhb_1tx.rbf";
+  m_uRx.reset();
+  if (!skipRx) {
+  try {
+    m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
+                                        0, decimRate * sps, 1, -1,
+                                        usrp_standard_rx::FPGA_MODE_NORMAL,
+                                        1024, 16 * 8, rbf));
+    m_uRx->set_fpga_master_clock_freq(masterClockRate);
+  }
+
+  catch(...) {
+    LOG(ALERT) << "make failed on Rx";
+    m_uRx.reset();
+    return -1;
+  }
+
+  if (m_uRx->fpga_master_clock_freq() != masterClockRate)
+  {
+    LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
+               << ", desired clock freq = " << masterClockRate;
+    m_uRx.reset();
+    return -1;
+  }
+  }
+
+  try {
+    m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
+                                        0, decimRate * 2, 1, -1,
+                                        1024, 16 * 8, rbf));
+    m_uTx->set_fpga_master_clock_freq(masterClockRate);
+  }
+
+  catch(...) {
+    LOG(ALERT) << "make failed on Tx";
+    m_uTx.reset();
+    return -1;
+  }
+
+  if (m_uTx->fpga_master_clock_freq() != masterClockRate)
+  {
+    LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
+               << ", desired clock freq = " << masterClockRate;
+    m_uTx.reset();
+    return -1;
+  }
+
+  if (!skipRx) m_uRx->stop();
+  m_uTx->stop();
+
+#endif
+
+  switch (dboardConfig) {
+  case TXA_RXB:
+    txSubdevSpec = usrp_subdev_spec(0,0);
+    rxSubdevSpec = usrp_subdev_spec(1,0);
+    break;
+  case TXB_RXA:
+    txSubdevSpec = usrp_subdev_spec(1,0);
+    rxSubdevSpec = usrp_subdev_spec(0,0);
+    break;
+  case TXA_RXA:
+    txSubdevSpec = usrp_subdev_spec(0,0);
+    rxSubdevSpec = usrp_subdev_spec(0,0);
+    break;
+  case TXB_RXB:
+    txSubdevSpec = usrp_subdev_spec(1,0);
+    rxSubdevSpec = usrp_subdev_spec(1,0);
+    break;
+  default:
+    txSubdevSpec = usrp_subdev_spec(0,0);
+    rxSubdevSpec = usrp_subdev_spec(1,0);
+  }
+
+  m_dbTx = m_uTx->selected_subdev(txSubdevSpec);
+  m_dbRx = m_uRx->selected_subdev(rxSubdevSpec);
+
+  samplesRead = 0;
+  samplesWritten = 0;
+  started = false;
+
+  return NORMAL;
+}
+
+
+
+bool USRPDevice::start()
+{
+  LOG(INFO) << "starting USRP...";
+#ifndef SWLOOPBACK
+  if (!m_uRx && !skipRx) return false;
+  if (!m_uTx) return false;
+
+  if (!skipRx) m_uRx->stop();
+  m_uTx->stop();
+
+  writeLock.lock();
+  // power up and configure daughterboards
+  m_dbTx->set_enable(true);
+  m_uTx->set_mux(m_uTx->determine_tx_mux_value(txSubdevSpec));
+  m_uRx->set_mux(m_uRx->determine_rx_mux_value(rxSubdevSpec));
+
+  if (!m_dbRx->select_rx_antenna(1))
+    m_dbRx->select_rx_antenna(0);
+
+  writeLock.unlock();
+
+  // Set gains to midpoint
+  setTxGain((minTxGain() + maxTxGain()) / 2);
+  setRxGain((minRxGain() + maxRxGain()) / 2);
+
+  data = new short[currDataSize];
+  dataStart = 0;
+  dataEnd = 0;
+  timeStart = 0;
+  timeEnd = 0;
+  timestampOffset = 0;
+  latestWriteTimestamp = 0;
+  lastPktTimestamp = 0;
+  hi32Timestamp = 0;
+  isAligned = false;
+
+
+  if (!skipRx)
+  started = (m_uRx->start() && m_uTx->start());
+  else
+  started = m_uTx->start();
+  return started;
+#else
+  gettimeofday(&lastReadTime,NULL);
+  return true;
+#endif
+}
+
+bool USRPDevice::stop()
+{
+#ifndef SWLOOPBACK
+  if (!m_uRx) return false;
+  if (!m_uTx) return false;
+
+  delete[] currData;
+
+  started = !(m_uRx->stop() && m_uTx->stop());
+  return !started;
+#else
+  return true;
+#endif
+}
+
+double USRPDevice::maxTxGain()
+{
+  return m_dbTx->gain_max();
+}
+
+double USRPDevice::minTxGain()
+{
+  return m_dbTx->gain_min();
+}
+
+double USRPDevice::maxRxGain()
+{
+  return m_dbRx->gain_max();
+}
+
+double USRPDevice::minRxGain()
+{
+  return m_dbRx->gain_min();
+}
+
+double USRPDevice::setTxGain(double dB, size_t chan)
+{
+  if (chan) {
+    LOG(ALERT) << "Invalid channel " << chan;
+    return 0.0;
+  }
+
+  writeLock.lock();
+  if (dB > maxTxGain())
+    dB = maxTxGain();
+  if (dB < minTxGain())
+    dB = minTxGain();
+
+  LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
+
+  if (!m_dbTx->set_gain(dB))
+    LOG(ERR) << "Error setting TX gain";
+
+  writeLock.unlock();
+
+  return dB;
+}
+
+
+double USRPDevice::setRxGain(double dB, size_t chan)
+{
+  if (chan) {
+    LOG(ALERT) << "Invalid channel " << chan;
+    return 0.0;
+  }
+
+  dB = 47.0;
+
+  writeLock.lock();
+  if (dB > maxRxGain())
+    dB = maxRxGain();
+  if (dB < minRxGain())
+    dB = minRxGain();
+
+  LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
+
+  if (!m_dbRx->set_gain(dB))
+    LOG(ERR) << "Error setting RX gain";
+
+  writeLock.unlock();
+
+  return dB;
+}
+
+bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
+{
+	if (chan >= rx_paths.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return false;
+	}
+	LOG(ALERT) << "Not implemented";
+	return true;
+}
+
+std::string USRPDevice::getRxAntenna(size_t chan)
+{
+	if (chan >= rx_paths.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return "";
+	}
+	LOG(ALERT) << "Not implemented";
+	return "";
+}
+
+bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan)
+{
+	if (chan >= tx_paths.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return false;
+	}
+	LOG(ALERT) << "Not implemented";
+	return true;
+}
+
+std::string USRPDevice::getTxAntenna(size_t chan)
+{
+	if (chan >= tx_paths.size()) {
+		LOG(ALERT) << "Requested non-existent channel " << chan;
+		return "";
+	}
+	LOG(ALERT) << "Not implemented";
+	return "";
+}
+
+
+// NOTE: Assumes sequential reads
+int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
+                            TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
+{
+#ifndef SWLOOPBACK
+  if (!m_uRx)
+    return 0;
+
+  short *buf = bufs[0];
+
+  timestamp += timestampOffset;
+
+  if (timestamp + len < timeStart) {
+    memset(buf,0,len*2*sizeof(short));
+    return len;
+  }
+
+  if (underrun) *underrun = false;
+
+  uint32_t readBuf[2000];
+
+  while (1) {
+    //guestimate USB read size
+    int readLen=0;
+    {
+      int numSamplesNeeded = timestamp + len - timeEnd;
+      if (numSamplesNeeded <=0) break;
+      readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0));
+      if (readLen > 8000) readLen= (8000/512)*512;
+    }
+
+    // read USRP packets, parse and save A/D data as needed
+    readLen = m_uRx->read((void *)readBuf,readLen,overrun);
+    for(int pktNum = 0; pktNum < (readLen/512); pktNum++) {
+      // tmpBuf points to start of a USB packet
+      uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4);
+      TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]);
+      uint32_t word0 = usrp_to_host_u32(tmpBuf[0]);
+      uint32_t chan = (word0 >> 16) & 0x1f;
+      unsigned payloadSz = word0 & 0x1ff;
+      LOG(DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
+
+      bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp);
+      if (incrementHi32 && (timeStart!=0)) {
+           LOG(DEBUG) << "high 32 increment!!!";
+           hi32Timestamp++;
+      }
+      pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp;
+      lastPktTimestamp = pktTimestamp;
+
+      if (chan == 0x01f) {
+	// control reply, check to see if its ping reply
+        uint32_t word2 = usrp_to_host_u32(tmpBuf[2]);
+	if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
+          timestamp -= timestampOffset;
+	  timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
+	  LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset;
+          timestamp += timestampOffset;
+	  isAligned = true;
+	}
+	continue;
+      }
+      if (chan != 0) {
+	LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
+	continue;
+      }
+      if ((word0 >> 28) & 0x04) {
+	if (underrun) *underrun = true;
+	LOG(DEBUG) << "UNDERRUN in TRX->USRP interface";
+      }
+      if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
+
+      if (!isAligned) continue;
+
+      unsigned cursorStart = pktTimestamp - timeStart + dataStart;
+      while (cursorStart*2 > currDataSize) {
+	cursorStart -= currDataSize/2;
+      }
+      if (cursorStart*2 + payloadSz/2 > currDataSize) {
+	// need to circle around buffer
+	memcpy(data+cursorStart*2,tmpBuf+2,(currDataSize-cursorStart*2)*sizeof(short));
+	memcpy(data,tmpBuf+2+(currDataSize/2-cursorStart),payloadSz-(currDataSize-cursorStart*2)*sizeof(short));
+      }
+      else {
+	memcpy(data+cursorStart*2,tmpBuf+2,payloadSz);
+      }
+      if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
+	timeEnd = pktTimestamp+payloadSz/2/sizeof(short);
+
+      LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
+
+    }
+  }
+
+  // copy desired data to buf
+  unsigned bufStart = dataStart+(timestamp-timeStart);
+  if (bufStart + len < currDataSize/2) {
+    LOG(DEBUG) << "bufStart: " << bufStart;
+    memcpy(buf,data+bufStart*2,len*2*sizeof(short));
+    memset(data+bufStart*2,0,len*2*sizeof(short));
+  }
+  else {
+    LOG(DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
+    unsigned firstLength = (currDataSize/2-bufStart);
+    LOG(DEBUG) << "firstLength: " << firstLength;
+    memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short));
+    memset(data+bufStart*2,0,firstLength*2*sizeof(short));
+    memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short));
+    memset(data,0,(len-firstLength)*2*sizeof(short));
+  }
+  dataStart = (bufStart + len) % (currDataSize/2);
+  timeStart = timestamp + len;
+
+  return len;
+
+#else
+  if (loopbackBufferSize < 2) return 0;
+  int numSamples = 0;
+  struct timeval currTime;
+  gettimeofday(&currTime,NULL);
+  double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
+    (currTime.tv_usec - lastReadTime.tv_usec);
+  if (timeElapsed < samplePeriod) {return 0;}
+  int numSamplesToRead = (int) floor(timeElapsed/samplePeriod);
+  if (numSamplesToRead < len) return 0;
+
+  if (numSamplesToRead > len) numSamplesToRead = len;
+  if (numSamplesToRead > loopbackBufferSize/2) {
+    firstRead =false;
+    numSamplesToRead = loopbackBufferSize/2;
+  }
+  memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead);
+  loopbackBufferSize -= 2*numSamplesToRead;
+  memcpy(loopbackBuffer,loopbackBuffer+2*numSamplesToRead,
+	 sizeof(short)*loopbackBufferSize);
+  numSamples = numSamplesToRead;
+  if (firstRead) {
+    int new_usec = lastReadTime.tv_usec + (int) round((double) numSamplesToRead * samplePeriod);
+    lastReadTime.tv_sec = lastReadTime.tv_sec + new_usec/1000000;
+    lastReadTime.tv_usec = new_usec % 1000000;
+  }
+  else {
+    gettimeofday(&lastReadTime,NULL);
+    firstRead = true;
+  }
+  samplesRead += numSamples;
+
+  return numSamples;
+#endif
+}
+
+int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
+                             bool *underrun, unsigned long long timestamp,
+                             bool isControl)
+{
+  writeLock.lock();
+
+#ifndef SWLOOPBACK
+  if (!m_uTx)
+    return 0;
+
+  short *buf = bufs[0];
+
+  static uint32_t outData[128*20];
+
+  for (int i = 0; i < len*2; i++) {
+	buf[i] = host_to_usrp_short(buf[i]);
+  }
+
+  int numWritten = 0;
+  unsigned isStart = 1;
+  unsigned RSSI = 0;
+  unsigned CHAN = (isControl) ? 0x01f : 0x00;
+  len = len*2*sizeof(short);
+  int numPkts = (int) ceil((float)len/(float)504);
+  unsigned isEnd = (numPkts < 2);
+  uint32_t *outPkt = outData;
+  int pktNum = 0;
+  while (numWritten < len) {
+    // pkt is pointer to start of a USB packet
+    uint32_t *pkt = outPkt + pktNum*128;
+    isEnd = (len - numWritten <= 504);
+    unsigned payloadLen = ((len - numWritten) < 504) ? (len-numWritten) : 504;
+    pkt[0] = (isStart << 12 | isEnd << 11 | (RSSI & 0x3f) << 5 | CHAN) << 16 | payloadLen;
+    pkt[1] = timestamp & 0x0ffffffffll;
+    memcpy(pkt+2,buf+(numWritten/sizeof(short)),payloadLen);
+    numWritten += payloadLen;
+    timestamp += payloadLen/2/sizeof(short);
+    isStart = 0;
+    pkt[0] = host_to_usrp_u32(pkt[0]);
+    pkt[1] = host_to_usrp_u32(pkt[1]);
+    pktNum++;
+  }
+  m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL);
+
+  samplesWritten += len/2/sizeof(short);
+  writeLock.unlock();
+
+  return len/2/sizeof(short);
+#else
+  int retVal = len;
+  memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
+  samplesWritten += retVal;
+  loopbackBufferSize += retVal*2;
+
+  return retVal;
+#endif
+}
+
+bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
+{
+#ifndef SWLOOPBACK
+  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;
+#else
+  return true;
+#endif
+}
+
+#ifndef SWLOOPBACK
+bool USRPDevice::setTxFreq(double wFreq, size_t chan)
+{
+  usrp_tune_result result;
+
+  if (chan) {
+    LOG(ALERT) << "Invalid channel " << chan;
+    return false;
+  }
+
+  if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
+    LOG(INFO) << "set TX: " << wFreq << std::endl
+              << "    baseband freq: " << result.baseband_freq << std::endl
+              << "    DDC freq:      " << result.dxc_freq << std::endl
+              << "    residual freq: " << result.residual_freq;
+    return true;
+  }
+  else {
+    LOG(ALERT) << "set TX: " << wFreq << "failed" << std::endl
+               << "    baseband freq: " << result.baseband_freq << std::endl
+               << "    DDC freq:      " << result.dxc_freq << std::endl
+               << "    residual freq: " << result.residual_freq;
+    return false;
+  }
+}
+
+bool USRPDevice::setRxFreq(double wFreq, size_t chan)
+{
+  usrp_tune_result result;
+
+  if (chan) {
+    LOG(ALERT) << "Invalid channel " << chan;
+    return false;
+  }
+
+  if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
+    LOG(INFO) << "set RX: " << wFreq << std::endl
+              << "    baseband freq: " << result.baseband_freq << std::endl
+              << "    DDC freq:      " << result.dxc_freq << std::endl
+              << "    residual freq: " << result.residual_freq;
+    return true;
+  }
+  else {
+    LOG(ALERT) << "set RX: " << wFreq << "failed" << std::endl
+               << "    baseband freq: " << result.baseband_freq << std::endl
+               << "    DDC freq:      " << result.dxc_freq << std::endl
+               << "    residual freq: " << result.residual_freq;
+    return false;
+  }
+
+}
+
+#else
+bool USRPDevice::setTxFreq(double wFreq) { return true;};
+bool USRPDevice::setRxFreq(double wFreq) { return true;};
+#endif
+
+RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
+			       InterfaceType iface, size_t chans, double offset,
+			       const std::vector<std::string>& tx_paths,
+			       const std::vector<std::string>& rx_paths)
+{
+	return new USRPDevice(tx_sps);
+}
diff --git a/Transceiver52M/device/usrp1/USRPDevice.h b/Transceiver52M/device/usrp1/USRPDevice.h
new file mode 100644
index 0000000..f981643
--- /dev/null
+++ b/Transceiver52M/device/usrp1/USRPDevice.h
@@ -0,0 +1,204 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+*/
+
+#ifndef _USRP_DEVICE_H_
+#define _USRP_DEVICE_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "radioDevice.h"
+
+#include <usrp/usrp_standard.h>
+#include <usrp/usrp_bytesex.h>
+#include <usrp/usrp_prims.h>
+#include <sys/time.h>
+#include <math.h>
+#include <string>
+#include <iostream>
+
+#include <boost/shared_ptr.hpp>
+typedef boost::shared_ptr<usrp_standard_tx> usrp_standard_tx_sptr;
+typedef boost::shared_ptr<usrp_standard_rx> usrp_standard_rx_sptr;
+
+/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */
+class USRPDevice: public RadioDevice {
+
+private:
+
+  static const double masterClockRate; ///< the USRP clock rate
+  double desiredSampleRate; 	///< the desired sampling rate
+  usrp_standard_rx_sptr m_uRx;	///< the USRP receiver
+  usrp_standard_tx_sptr m_uTx;	///< the USRP transmitter
+
+  db_base_sptr m_dbRx;          ///< rx daughterboard
+  db_base_sptr m_dbTx;          ///< tx daughterboard
+  usrp_subdev_spec rxSubdevSpec;
+  usrp_subdev_spec txSubdevSpec;
+
+  int sps;
+  double actualSampleRate;	///< the actual USRP sampling rate
+  unsigned int decimRate;	///< the USRP decimation rate
+
+  unsigned long long samplesRead;	///< number of samples read from USRP
+  unsigned long long samplesWritten;	///< number of samples sent to USRP
+
+  bool started;			///< flag indicates USRP has started
+  bool skipRx;			///< set if USRP is transmit-only.
+
+  static const unsigned int currDataSize_log2 = 21;
+  static const unsigned long currDataSize = (1 << currDataSize_log2);
+  short *data;
+  unsigned long dataStart;
+  unsigned long dataEnd;
+  TIMESTAMP timeStart;
+  TIMESTAMP timeEnd;
+  bool isAligned;
+
+  Mutex writeLock;
+
+  short *currData;		///< internal data buffer when reading from USRP
+  TIMESTAMP currTimestamp;	///< timestamp of internal data buffer
+  unsigned currLen;		///< size of internal data buffer
+
+  TIMESTAMP timestampOffset;       ///< timestamp offset b/w Tx and Rx blocks
+  TIMESTAMP latestWriteTimestamp;  ///< timestamp of most recent ping command
+  TIMESTAMP pingTimestamp;	   ///< timestamp of most recent ping response
+
+  long long  pingOffset;
+  unsigned long hi32Timestamp;
+  unsigned long lastPktTimestamp;
+
+  double rxGain;
+
+#ifdef SWLOOPBACK
+  short loopbackBuffer[1000000];
+  int loopbackBufferSize;
+  double samplePeriod;
+
+  struct timeval startTime;
+  struct timeval lastReadTime;
+  bool   firstRead;
+#endif
+
+ public:
+
+  /** Object constructor */
+  USRPDevice(size_t sps);
+
+  /** Instantiate the USRP */
+  int open(const std::string &, int, bool);
+
+  /** Start the USRP */
+  bool start();
+
+  /** Stop the USRP */
+  bool stop();
+
+  /** Set priority not supported */
+  void setPriority(float prio = 0.5) { }
+
+  enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
+
+  /**
+	Read samples from the USRP.
+	@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 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,
+                  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, bool isControl = false);
+
+  /** Update the alignment between the read and write timestamps */
+  bool updateAlignment(TIMESTAMP timestamp);
+
+  /** Set the transmitter frequency */
+  bool setTxFreq(double wFreq, size_t chan = 0);
+
+  /** Set the receiver frequency */
+  bool setRxFreq(double wFreq, size_t chan = 0);
+
+  /** Returns the starting write Timestamp*/
+  TIMESTAMP initialWriteTimestamp(void) { return 20000;}
+
+  /** Returns the starting read Timestamp*/
+  TIMESTAMP initialReadTimestamp(void) { return 20000;}
+
+  /** returns the full-scale transmit amplitude **/
+  double fullScaleInputValue() {return 13500.0;}
+
+  /** returns the full-scale receive amplitude **/
+  double fullScaleOutputValue() {return 9450.0;}
+
+  /** sets the receive chan gain, returns the gain setting **/
+  double setRxGain(double dB, size_t chan = 0);
+
+  /** get the current receive gain */
+  double getRxGain(size_t chan = 0) { return rxGain; }
+
+  /** return maximum Rx Gain **/
+  double maxRxGain(void);
+
+  /** return minimum Rx Gain **/
+  double minRxGain(void);
+
+  /** sets the transmit chan gain, returns the gain setting **/
+  double setTxGain(double dB, size_t chan = 0);
+
+  /** return maximum Tx Gain **/
+  double maxTxGain(void);
+
+  /** return minimum Rx Gain **/
+  double minTxGain(void);
+
+  /** sets the RX path to use, returns true if successful and false otherwise */
+  bool setRxAntenna(const std::string &ant, size_t chan = 0);
+
+  /* return the used RX path */
+  std::string getRxAntenna(size_t chan = 0);
+
+  /** sets the RX path to use, returns true if successful and false otherwise */
+  bool setTxAntenna(const std::string &ant, size_t chan = 0);
+
+  /* return the used RX path */
+  std::string getTxAntenna(size_t chan = 0);
+
+  /** Return internal status values */
+  inline double getTxFreq(size_t chan = 0) { return 0; }
+  inline double getRxFreq(size_t chan = 0) { return 0; }
+  inline double getSampleRate() { return actualSampleRate; }
+  inline double numberRead() { return samplesRead; }
+  inline double numberWritten() { return samplesWritten; }
+
+  std::vector<std::string> tx_paths, rx_paths;
+};
+
+#endif // _USRP_DEVICE_H_