mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-10-31 11:53:33 +00:00 
			
		
		
		
	Shadow all gains and frequencies, which minimizes device access. This allows the transceiver to variably control the device settings. Signed-off-by: Thomas Tsou <ttsou@vt.edu> git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@2640 19bc5d8c-e614-43d4-8b26-e1612bc8e597
		
			
				
	
	
		
			826 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			826 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| * Copyright 2008, 2009, 2010 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/>.
 | |
| 
 | |
| */
 | |
| 
 | |
| #include "radioDevice.h"
 | |
| #include "Threads.h"
 | |
| #include "Logger.h"
 | |
| #include <uhd/usrp/single_usrp.hpp>
 | |
| #include <uhd/utils/thread_priority.hpp>
 | |
| 
 | |
| /*
 | |
|     use_ext_ref       - Enable external 10MHz clock reference
 | |
| 
 | |
|     master_clk_rt     - Master clock frequency
 | |
| 
 | |
|     rx_smpl_offset    - Timing correction in seconds between receive and
 | |
|                         transmit timestamps. This value corrects for delays on
 | |
|                         on the RF side of the timestamping point of the device.
 | |
|                         This value is generally empirically measured.
 | |
| 
 | |
|     smpl_buf_sz       - The receive sample buffer size in bytes. 
 | |
| */
 | |
| const bool use_ext_ref = false;
 | |
| const double master_clk_rt = 52e6;
 | |
| const double rx_smpl_offset = .0000869;
 | |
| const size_t smpl_buf_sz = (1 << 20);
 | |
| 
 | |
| /** Timestamp conversion
 | |
|     @param timestamp a UHD or OpenBTS timestamp
 | |
|     @param rate sample rate
 | |
|     @return the converted timestamp
 | |
| */
 | |
| uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
 | |
| {
 | |
| 	double secs = (double) ticks / rate;
 | |
| 	return uhd::time_spec_t(secs);
 | |
| }
 | |
| 
 | |
| TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
 | |
| {
 | |
| 	size_t ticks = ts.get_full_secs() * rate;
 | |
| 	return ts.get_tick_count(rate) + ticks;
 | |
| }
 | |
| 
 | |
| /*
 | |
|     Sample Buffer - Allows reading and writing of timed samples using OpenBTS
 | |
|                     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() 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(double rate, bool skip_rx);
 | |
| 	~uhd_device();
 | |
| 
 | |
| 	bool open();
 | |
| 	bool start();
 | |
| 	bool stop();
 | |
| 	void setPriority();
 | |
| 
 | |
| 	int readSamples(short *buf, int len, bool *overrun, 
 | |
| 			TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
 | |
| 
 | |
| 	int writeSamples(short *buf, int len, bool *underrun, 
 | |
| 			 TIMESTAMP timestamp, bool isControl);
 | |
| 
 | |
| 	bool updateAlignment(TIMESTAMP timestamp);
 | |
| 
 | |
| 	bool setTxFreq(double wFreq);
 | |
| 	bool setRxFreq(double wFreq);
 | |
| 
 | |
| 	inline TIMESTAMP initialWriteTimestamp() { return 0; }
 | |
| 	inline TIMESTAMP initialReadTimestamp() { return 0; }
 | |
| 
 | |
| 	inline double fullScaleInputValue() { return 13500.0; }
 | |
| 	inline double fullScaleOutputValue() { return 9450.0; }
 | |
| 
 | |
| 	double setRxGain(double db);
 | |
| 	double getRxGain(void) { return rx_gain; }
 | |
| 	double maxRxGain(void) { return rx_gain_max; }
 | |
| 	double minRxGain(void) { return rx_gain_min; }
 | |
| 
 | |
| 	double setTxGain(double db);
 | |
| 	double maxTxGain(void) { return tx_gain_max; }
 | |
| 	double minTxGain(void) { return tx_gain_min; }
 | |
| 
 | |
| 	double getTxFreq() { return tx_freq; }
 | |
| 	double getRxFreq() { return rx_freq; }
 | |
| 
 | |
| 	inline double getSampleRate() { return actual_smpl_rt; }
 | |
| 	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();
 | |
| 
 | |
| private:
 | |
| 	uhd::usrp::single_usrp::sptr usrp_dev;
 | |
| 
 | |
| 	double desired_smpl_rt, actual_smpl_rt;
 | |
| 
 | |
| 	double tx_gain, tx_gain_min, tx_gain_max;
 | |
| 	double rx_gain, rx_gain_min, rx_gain_max;
 | |
| 
 | |
| 	double tx_freq, rx_freq;
 | |
| 	size_t tx_spp, rx_spp;
 | |
| 
 | |
| 	bool started;
 | |
| 	bool aligned;
 | |
| 	bool skip_rx;
 | |
| 
 | |
| 	size_t rx_pkt_cnt;
 | |
| 	size_t drop_cnt;
 | |
| 	uhd::time_spec_t prev_ts;
 | |
| 
 | |
| 	TIMESTAMP ts_offset;
 | |
| 	smpl_buf *rx_smpl_buf;
 | |
| 
 | |
| 	void init_gains();
 | |
| 	bool flush_recv(size_t num_pkts);
 | |
| 	std::string str_code(uhd::rx_metadata_t metadata);
 | |
| 	std::string str_code(uhd::async_metadata_t metadata);
 | |
| 
 | |
| 	Thread async_event_thrd;
 | |
| };
 | |
| 
 | |
| void *async_event_loop(uhd_device *dev)
 | |
| {
 | |
| 	while (1) {
 | |
| 		dev->recv_async_msg();
 | |
| 		pthread_testcancel();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| uhd_device::uhd_device(double rate, 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),
 | |
| 	  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(true), rx_pkt_cnt(0), drop_cnt(0),
 | |
| 	  prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
 | |
| {
 | |
| 	this->skip_rx = skip_rx;
 | |
| }
 | |
| 
 | |
| uhd_device::~uhd_device()
 | |
| {
 | |
| 	stop();
 | |
| 
 | |
| 	if (rx_smpl_buf)
 | |
| 		delete rx_smpl_buf;
 | |
| }
 | |
| 
 | |
| static double set_usrp_rates(uhd::usrp::single_usrp::sptr dev, double rate)
 | |
| {
 | |
| 	double actual_rate;
 | |
| 
 | |
| 	dev->set_tx_rate(rate);
 | |
| 	dev->set_rx_rate(rate);
 | |
| 	actual_rate = dev->get_tx_rate();
 | |
| 
 | |
| 	if (actual_rate != rate) {
 | |
| 		LOG(ERROR) << "Actual sample rate differs from desired rate";
 | |
| 		return -1.0;
 | |
| 	}
 | |
| 	if (dev->get_rx_rate() != actual_rate) {
 | |
| 		LOG(ERROR) << "Transmit and receive sample rates do not match";
 | |
| 		return -1.0;
 | |
| 	}
 | |
| 
 | |
| 	return actual_rate;
 | |
| }
 | |
| 
 | |
| double uhd_device::setTxGain(double db)
 | |
| {
 | |
| 	usrp_dev->set_tx_gain(db);
 | |
| 	tx_gain = usrp_dev->get_tx_gain();
 | |
| 
 | |
| 	LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
 | |
| 
 | |
| 	return tx_gain;
 | |
| }
 | |
| 
 | |
| double uhd_device::setRxGain(double db)
 | |
| {
 | |
| 	usrp_dev->set_rx_gain(db);
 | |
| 	rx_gain = usrp_dev->get_rx_gain();
 | |
| 
 | |
| 	LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
 | |
| 
 | |
| 	return rx_gain;
 | |
| }
 | |
| 
 | |
| void uhd_device::init_gains()
 | |
| {
 | |
| 	uhd::gain_range_t range;
 | |
| 
 | |
| 	range = usrp_dev->get_tx_gain_range();
 | |
| 	tx_gain_min = range.start();
 | |
| 	tx_gain_max = range.stop();
 | |
| 
 | |
| 	range = usrp_dev->get_rx_gain_range();
 | |
| 	rx_gain_min = range.start();
 | |
| 	rx_gain_max = range.stop();
 | |
| 
 | |
| 	usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
 | |
| 	usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
 | |
| 
 | |
| 	tx_gain = usrp_dev->get_tx_gain();
 | |
| 	rx_gain = usrp_dev->get_rx_gain();
 | |
| 
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| static void set_usrp_ref_clk(uhd::usrp::single_usrp::sptr dev, bool ext_clk)
 | |
| {
 | |
| 	uhd::clock_config_t clk_cfg;
 | |
| 
 | |
| 	clk_cfg.pps_source = uhd::clock_config_t::PPS_SMA;
 | |
| 	clk_cfg.pps_polarity = uhd::clock_config_t::PPS_NEG;
 | |
| 
 | |
| 	if (ext_clk)
 | |
| 		clk_cfg.ref_source = uhd::clock_config_t::REF_SMA;
 | |
| 	else
 | |
| 		clk_cfg.ref_source = uhd::clock_config_t::REF_INT;
 | |
| 
 | |
| 	dev->set_clock_config(clk_cfg);
 | |
| }
 | |
| 
 | |
| bool uhd_device::open()
 | |
| {
 | |
| 	LOG(INFO) << "creating USRP device...";
 | |
| 
 | |
| 	// Use the first available USRP E100
 | |
| 	uhd::device_addr_t dev_addr("type=usrp-e");
 | |
| 	try {
 | |
| 		usrp_dev = uhd::usrp::single_usrp::make(dev_addr);
 | |
| 	}
 | |
| 	
 | |
| 	catch(...) {
 | |
| 		LOG(ERROR) << "USRP make failed";
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	// Set master clock rate
 | |
| 	usrp_dev->set_master_clock_rate(master_clk_rt);
 | |
| 
 | |
| 	// Number of samples per over-the-wire packet
 | |
| 	tx_spp = usrp_dev->get_device()->get_max_send_samps_per_packet();
 | |
| 	rx_spp = usrp_dev->get_device()->get_max_recv_samps_per_packet();
 | |
| 
 | |
| 	// Set rates
 | |
| 	actual_smpl_rt = set_usrp_rates(usrp_dev, desired_smpl_rt);
 | |
| 	if (actual_smpl_rt < 0)
 | |
| 		return false;
 | |
| 
 | |
| 	// Create receive buffer
 | |
| 	size_t buf_len = smpl_buf_sz / sizeof(uint32_t);
 | |
| 	rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
 | |
| 
 | |
| 	// Set receive chain sample offset 
 | |
| 	ts_offset = (TIMESTAMP)(rx_smpl_offset * actual_smpl_rt);
 | |
| 
 | |
| 	// Initialize and shadow gain values 
 | |
| 	init_gains();
 | |
| 
 | |
| 	// Set reference clock
 | |
| 	set_usrp_ref_clk(usrp_dev, use_ext_ref);
 | |
| 
 | |
| 	// Print configuration
 | |
| 	LOG(INFO) << usrp_dev->get_pp_string();
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool uhd_device::flush_recv(size_t num_pkts)
 | |
| {
 | |
| 	uhd::rx_metadata_t metadata;
 | |
| 	size_t num_smpls;
 | |
| 	uint32_t buff[rx_spp];
 | |
| 
 | |
| 	for (size_t i = 0; i < num_pkts; i++) {
 | |
| 		num_smpls = usrp_dev->get_device()->recv(
 | |
| 					buff,
 | |
| 					rx_spp,
 | |
| 					metadata,
 | |
| 					uhd::io_type_t::COMPLEX_INT16,
 | |
| 					uhd::device::RECV_MODE_ONE_PACKET);
 | |
| 
 | |
| 		if (!num_smpls)
 | |
| 			return false;
 | |
| 	}
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool uhd_device::start()
 | |
| {
 | |
| 	LOG(INFO) << "Starting USRP...";
 | |
| 
 | |
| 	if (started) {
 | |
| 		LOG(ERROR) << "Device already started";
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	setPriority();
 | |
| 
 | |
| 	// Start asynchronous event (underrun check) loop
 | |
| 	async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
 | |
| 
 | |
| 	// Start streaming
 | |
| 	uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
 | |
| 	cmd.stream_now = true;
 | |
| 	usrp_dev->set_time_now(uhd::time_spec_t(0.0));
 | |
| 
 | |
| 	if (!skip_rx)
 | |
| 		usrp_dev->issue_stream_cmd(cmd);
 | |
| 
 | |
| 	// Flush out any early garbage
 | |
| 	if (!flush_recv(20))
 | |
| 		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()
 | |
| {
 | |
| 	uhd::stream_cmd_t stream_cmd = 
 | |
| 		uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
 | |
| 
 | |
| 	usrp_dev->issue_stream_cmd(stream_cmd);
 | |
| 
 | |
| 	started = false;
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| void uhd_device::setPriority()
 | |
| {
 | |
| 	uhd::set_thread_priority_safe();
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| static int check_rx_md_err(uhd::rx_metadata_t &md, uhd::time_spec_t &prev_ts)
 | |
| {
 | |
| 	uhd::time_spec_t ts;
 | |
| 
 | |
| 	// Missing timestamp
 | |
| 	if (!md.has_time_spec) {
 | |
| 		LOG(ERROR) << "UHD: Received packet missing timestamp";
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	ts = md.time_spec;
 | |
| 
 | |
| 	// Monotonicity check
 | |
| 	if (ts < prev_ts) {
 | |
| 		LOG(ERROR) << "UHD: Loss of monotonic: " << ts.get_real_secs();
 | |
| 		LOG(ERROR) << "UHD: Previous time: " << prev_ts.get_real_secs();
 | |
| 		return -1;
 | |
| 	} else {
 | |
| 		prev_ts = ts;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int uhd_device::readSamples(short *buf, int len, bool *overrun,
 | |
| 			TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
 | |
| {
 | |
| 	ssize_t rc;
 | |
| 	uhd::time_spec_t ts;
 | |
| 	uhd::rx_metadata_t metadata;
 | |
| 	uint32_t pkt_buf[rx_spp];
 | |
| 
 | |
| 	if (skip_rx)
 | |
| 		return 0;
 | |
| 
 | |
| 	// Shift read time with respect to transmit clock
 | |
| 	timestamp += ts_offset;
 | |
| 
 | |
| 	ts = convert_time(timestamp, actual_smpl_rt);
 | |
| 	LOG(DEEPDEBUG) << "Requested timestamp = " << ts.get_real_secs();
 | |
| 
 | |
| 	// Check that timestamp is valid
 | |
| 	rc = rx_smpl_buf->avail_smpls(timestamp);
 | |
| 	if (rc < 0) {
 | |
| 		LOG(ERROR) << rx_smpl_buf->str_code(rc);
 | |
| 		LOG(ERROR) << rx_smpl_buf->str_status();
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	// Receive samples from the usrp until we have enough
 | |
| 	while (rx_smpl_buf->avail_smpls(timestamp) < len) {
 | |
| 		size_t num_smpls = usrp_dev->get_device()->recv(
 | |
| 					(void*)pkt_buf,
 | |
| 					rx_spp,
 | |
| 					metadata,
 | |
| 					uhd::io_type_t::COMPLEX_INT16,
 | |
| 					uhd::device::RECV_MODE_ONE_PACKET);
 | |
| 
 | |
| 		rx_pkt_cnt++;
 | |
| 
 | |
| 		// Recv error in UHD
 | |
| 		if (!num_smpls) {
 | |
| 			LOG(ERROR) << str_code(metadata);
 | |
| 			return 0;
 | |
| 		}
 | |
| 
 | |
| 		// Other metadata timing checks
 | |
| 		if (check_rx_md_err(metadata, prev_ts) < 0)
 | |
| 			return 0;
 | |
| 
 | |
| 		ts = metadata.time_spec;
 | |
| 		LOG(DEEPDEBUG) << "Received timestamp = " << ts.get_real_secs();
 | |
| 
 | |
| 		rc = rx_smpl_buf->write(pkt_buf,
 | |
| 					num_smpls,
 | |
| 					metadata.time_spec);
 | |
| 
 | |
| 		// Continue on local overrun, exit on other errors
 | |
| 		if ((rc < 0)) {
 | |
| 			LOG(ERROR) << rx_smpl_buf->str_code(rc);
 | |
| 			LOG(ERROR) << rx_smpl_buf->str_status();
 | |
| 			if (rc != smpl_buf::ERROR_OVERFLOW)
 | |
| 				return 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// We have enough samples
 | |
| 	rc = rx_smpl_buf->read(buf, len, timestamp);
 | |
| 	if ((rc < 0) || (rc != len)) {
 | |
| 		LOG(ERROR) << rx_smpl_buf->str_code(rc);
 | |
| 		LOG(ERROR) << rx_smpl_buf->str_status();
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	return len;
 | |
| }
 | |
| 
 | |
| int uhd_device::writeSamples(short *buf, 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 = convert_time(timestamp, actual_smpl_rt);
 | |
| 
 | |
| 	// No control packets
 | |
| 	if (isControl) {
 | |
| 		LOG(ERROR) << "Control packets not supported";
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	// Drop a fixed number of packets (magic value)
 | |
| 	if (!aligned) {
 | |
| 		drop_cnt++;
 | |
| 
 | |
| 		if (drop_cnt == 1) {
 | |
| 			LOG(DEBUG) << "Aligning transmitter: stop burst";
 | |
| 			metadata.end_of_burst = true;
 | |
| 		} else if (drop_cnt < 30) {
 | |
| 			LOG(DEEPDEBUG) << "Aligning transmitter: packet advance";
 | |
| 			*underrun = true;
 | |
| 			return len;
 | |
| 		} else {
 | |
| 			LOG(DEBUG) << "Aligning transmitter: start burst";
 | |
| 			metadata.start_of_burst = true;
 | |
| 			aligned = true;
 | |
| 			drop_cnt = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	size_t num_smpls = usrp_dev->get_device()->send(buf,
 | |
| 					len,
 | |
| 					metadata,
 | |
| 					uhd::io_type_t::COMPLEX_INT16,
 | |
| 					uhd::device::SEND_MODE_FULL_BUFF);
 | |
| 
 | |
| 	if (num_smpls != (unsigned)len)
 | |
| 		LOG(ERROR) << "UHD: Sent fewer samples than requested";
 | |
| 
 | |
| 	return num_smpls;
 | |
| }
 | |
| 
 | |
| bool uhd_device::updateAlignment(TIMESTAMP timestamp)
 | |
| {
 | |
| 	/* NOP */
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool uhd_device::setTxFreq(double wFreq)
 | |
| {
 | |
| 	uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
 | |
| 	LOG(INFO) << tr.to_pp_string();
 | |
| 	tx_freq = usrp_dev->get_tx_freq();
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool uhd_device::setRxFreq(double wFreq)
 | |
| {
 | |
| 	uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
 | |
| 	LOG(INFO) << tr.to_pp_string();
 | |
| 	rx_freq = usrp_dev->get_rx_freq();
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool uhd_device::recv_async_msg()
 | |
| {
 | |
| 	uhd::async_metadata_t metadata;
 | |
| 	if (!usrp_dev->get_device()->recv_async_msg(metadata))
 | |
| 		return false;
 | |
| 
 | |
| 	// Assume that any error requires resynchronization
 | |
| 	if (metadata.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
 | |
| 		aligned = false;
 | |
| 		LOG(INFO) << str_code(metadata);
 | |
| 	}
 | |
| 
 | |
| 	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_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(convert_time(timespec, clk_rt));
 | |
| }
 | |
| 
 | |
| ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
 | |
| {
 | |
| 	// 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);
 | |
| 
 | |
| 	// Read it
 | |
| 	if (read_start + num_smpls < buf_len) {
 | |
| 		size_t numBytes = len * 2 * sizeof(short);
 | |
| 		memcpy(buf, data + read_start, numBytes);
 | |
| 	} else {
 | |
| 		size_t first_cp = (buf_len - read_start) * 2 * sizeof(short);
 | |
| 		size_t second_cp = len * 2 * sizeof(short) - 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, convert_time(ts, clk_rt));
 | |
| }
 | |
| 
 | |
| ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
 | |
| {
 | |
| 	// Check for valid write
 | |
| 	if ((len == 0) || (len >= buf_len))
 | |
| 		return ERROR_WRITE;
 | |
| 	if ((timestamp + len) <= time_end)
 | |
| 		return ERROR_TIMESTAMP;
 | |
| 
 | |
| 	// 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 * 2 * sizeof(short);
 | |
| 		memcpy(data + write_start, buf, numBytes);
 | |
| 	} else {
 | |
| 		size_t first_cp = (buf_len - write_start) * 2 * sizeof(short);
 | |
| 		size_t second_cp = len * 2 * sizeof(short) - 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 (((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, convert_time(ts, clk_rt));
 | |
| }
 | |
| 
 | |
| std::string smpl_buf::str_status() const
 | |
| {
 | |
| 	std::ostringstream ost("Sample buffer: ");
 | |
| 
 | |
| 	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(double smpl_rt, bool skip_rx)
 | |
| {
 | |
| 	return new uhd_device(smpl_rt, skip_rx);
 | |
| }
 |