blob: 4eba136ef7563f73ac7eea69aac1a2837c972dd7 [file] [log] [blame]
kurtis.heimerl965e7572011-11-26 03:16:54 +00001/*
kurtis.heimerl119ca9c2011-11-26 03:18:26 +00002 * Device support for Ettus Research UHD driver
3 * Written by Thomas Tsou <ttsou@vt.edu>
4 *
5 * Copyright 2010,2011 Free Software Foundation, Inc.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * See the COPYING file in the main directory for details.
20 */
kurtis.heimerl965e7572011-11-26 03:16:54 +000021
22#include "radioDevice.h"
23#include "Threads.h"
24#include "Logger.h"
kurtis.heimerle380af32011-11-26 03:18:55 +000025#include <uhd/property_tree.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000026#include <uhd/usrp/single_usrp.hpp>
27#include <uhd/utils/thread_priority.hpp>
kurtis.heimerl0803ad92011-11-26 03:18:51 +000028#include <uhd/utils/msg.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000029
kurtis.heimerlce317332011-11-26 03:18:39 +000030#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif
33
kurtis.heimerl965e7572011-11-26 03:16:54 +000034/*
kurtis.heimerlce317332011-11-26 03:18:39 +000035 master_clk_rt - Master clock frequency - ignored if host resampling is
36 enabled
kurtis.heimerl965e7572011-11-26 03:16:54 +000037
38 rx_smpl_offset - Timing correction in seconds between receive and
39 transmit timestamps. This value corrects for delays on
40 on the RF side of the timestamping point of the device.
kurtis.heimerlbe6abe12011-11-26 03:17:05 +000041 This value is generally empirically measured.
kurtis.heimerl965e7572011-11-26 03:16:54 +000042
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000043 smpl_buf_sz - The receive sample buffer size in bytes.
44
45 tx_ampl - Transmit amplitude must be between 0 and 1.0
kurtis.heimerl965e7572011-11-26 03:16:54 +000046*/
kurtis.heimerl965e7572011-11-26 03:16:54 +000047const double master_clk_rt = 52e6;
kurtis.heimerl965e7572011-11-26 03:16:54 +000048const size_t smpl_buf_sz = (1 << 20);
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000049const float tx_ampl = .3;
kurtis.heimerl965e7572011-11-26 03:16:54 +000050
kurtis.heimerlce317332011-11-26 03:18:39 +000051#ifdef RESAMPLE
52const double rx_smpl_offset = .00005;
53#else
54const double rx_smpl_offset = .0000869;
55#endif
56
kurtis.heimerl965e7572011-11-26 03:16:54 +000057/** Timestamp conversion
58 @param timestamp a UHD or OpenBTS timestamp
59 @param rate sample rate
60 @return the converted timestamp
61*/
62uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
63{
64 double secs = (double) ticks / rate;
65 return uhd::time_spec_t(secs);
66}
67
68TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
69{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +000070 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +000071 return ts.get_tick_count(rate) + ticks;
72}
73
74/*
75 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
76 or UHD style timestamps. Time conversions are handled
77 internally or accessable through the static convert calls.
78*/
79class smpl_buf {
80public:
81 /** Sample buffer constructor
82 @param len number of 32-bit samples the buffer should hold
83 @param rate sample clockrate
84 @param timestamp
85 */
86 smpl_buf(size_t len, double rate);
87 ~smpl_buf();
88
89 /** Query number of samples available for reading
90 @param timestamp time of first sample
91 @return number of available samples or error
92 */
93 ssize_t avail_smpls(TIMESTAMP timestamp) const;
94 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
95
96 /** Read and write
97 @param buf pointer to buffer
98 @param len number of samples desired to read or write
99 @param timestamp time of first stample
100 @return number of actual samples read or written or error
101 */
102 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
103 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
104 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
105 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
106
107 /** Buffer status string
108 @return a formatted string describing internal buffer state
109 */
110 std::string str_status() const;
111
112 /** Formatted error string
113 @param code an error code
114 @return a formatted error string
115 */
116 static std::string str_code(ssize_t code);
117
118 enum err_code {
119 ERROR_TIMESTAMP = -1,
120 ERROR_READ = -2,
121 ERROR_WRITE = -3,
122 ERROR_OVERFLOW = -4
123 };
124
125private:
126 uint32_t *data;
127 size_t buf_len;
128
129 double clk_rt;
130
131 TIMESTAMP time_start;
132 TIMESTAMP time_end;
133
134 size_t data_start;
135 size_t data_end;
136};
137
138/*
139 uhd_device - UHD implementation of the Device interface. Timestamped samples
140 are sent to and received from the device. An intermediate buffer
141 on the receive side collects and aligns packets of samples.
142 Events and errors such as underruns are reported asynchronously
143 by the device and received in a separate thread.
144*/
145class uhd_device : public RadioDevice {
146public:
147 uhd_device(double rate, bool skip_rx);
148 ~uhd_device();
149
150 bool open();
151 bool start();
152 bool stop();
kurtis.heimerl68292102011-11-26 03:17:28 +0000153 void restart(uhd::time_spec_t ts);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000154 void setPriority();
kurtis.heimerle380af32011-11-26 03:18:55 +0000155 enum busType getBus() { return bus; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000156
157 int readSamples(short *buf, int len, bool *overrun,
158 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
159
160 int writeSamples(short *buf, int len, bool *underrun,
161 TIMESTAMP timestamp, bool isControl);
162
163 bool updateAlignment(TIMESTAMP timestamp);
164
165 bool setTxFreq(double wFreq);
166 bool setRxFreq(double wFreq);
167
168 inline TIMESTAMP initialWriteTimestamp() { return 0; }
169 inline TIMESTAMP initialReadTimestamp() { return 0; }
170
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000171 inline double fullScaleInputValue() { return 32000 * tx_ampl; }
172 inline double fullScaleOutputValue() { return 32000; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000173
kurtis.heimerl02d04052011-11-26 03:17:10 +0000174 double setRxGain(double db);
175 double getRxGain(void) { return rx_gain; }
176 double maxRxGain(void) { return rx_gain_max; }
177 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000178
kurtis.heimerl02d04052011-11-26 03:17:10 +0000179 double setTxGain(double db);
180 double maxTxGain(void) { return tx_gain_max; }
181 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000182
kurtis.heimerl02d04052011-11-26 03:17:10 +0000183 double getTxFreq() { return tx_freq; }
184 double getRxFreq() { return rx_freq; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000185
186 inline double getSampleRate() { return actual_smpl_rt; }
187 inline double numberRead() { return rx_pkt_cnt; }
188 inline double numberWritten() { return 0; }
189
190 /** Receive and process asynchronous message
191 @return true if message received or false on timeout or error
192 */
193 bool recv_async_msg();
194
kurtis.heimerld4be0742011-11-26 03:17:46 +0000195 enum err_code {
196 ERROR_TIMING = -1,
197 ERROR_UNRECOVERABLE = -2,
198 ERROR_UNHANDLED = -3,
199 };
200
kurtis.heimerl965e7572011-11-26 03:16:54 +0000201private:
202 uhd::usrp::single_usrp::sptr usrp_dev;
kurtis.heimerle380af32011-11-26 03:18:55 +0000203 enum busType bus;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000204
kurtis.heimerl02d04052011-11-26 03:17:10 +0000205 double desired_smpl_rt, actual_smpl_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000206
kurtis.heimerl02d04052011-11-26 03:17:10 +0000207 double tx_gain, tx_gain_min, tx_gain_max;
208 double rx_gain, rx_gain_min, rx_gain_max;
209
210 double tx_freq, rx_freq;
211 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000212
213 bool started;
214 bool aligned;
215 bool skip_rx;
216
217 size_t rx_pkt_cnt;
218 size_t drop_cnt;
219 uhd::time_spec_t prev_ts;
220
221 TIMESTAMP ts_offset;
222 smpl_buf *rx_smpl_buf;
223
kurtis.heimerl02d04052011-11-26 03:17:10 +0000224 void init_gains();
kurtis.heimerl24481de2011-11-26 03:17:18 +0000225 void set_ref_clk(bool ext_clk);
226 double set_rates(double rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000227 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000228 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000229 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000230
kurtis.heimerl965e7572011-11-26 03:16:54 +0000231 std::string str_code(uhd::rx_metadata_t metadata);
232 std::string str_code(uhd::async_metadata_t metadata);
233
234 Thread async_event_thrd;
235};
236
237void *async_event_loop(uhd_device *dev)
238{
239 while (1) {
240 dev->recv_async_msg();
241 pthread_testcancel();
242 }
243}
244
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000245/*
246 Catch and drop underrun 'U' and overrun 'O' messages from stdout
247 since we already report using the logging facility. Direct
248 everything else appropriately.
249 */
250void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
251{
252 switch (type) {
253 case uhd::msg::status:
254 LOG(INFO) << msg;
255 break;
256 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000257 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000258 break;
259 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000260 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000261 break;
262 case uhd::msg::fastpath:
263 break;
264 }
265}
266
kurtis.heimerl965e7572011-11-26 03:16:54 +0000267uhd_device::uhd_device(double rate, bool skip_rx)
kurtis.heimerl02d04052011-11-26 03:17:10 +0000268 : desired_smpl_rt(rate), actual_smpl_rt(0),
269 tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0),
270 rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0),
271 tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000272 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
kurtis.heimerl965e7572011-11-26 03:16:54 +0000273 prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
274{
275 this->skip_rx = skip_rx;
276}
277
278uhd_device::~uhd_device()
279{
280 stop();
281
282 if (rx_smpl_buf)
283 delete rx_smpl_buf;
284}
285
kurtis.heimerl24481de2011-11-26 03:17:18 +0000286void uhd_device::init_gains()
287{
288 uhd::gain_range_t range;
289
290 range = usrp_dev->get_tx_gain_range();
291 tx_gain_min = range.start();
292 tx_gain_max = range.stop();
293
294 range = usrp_dev->get_rx_gain_range();
295 rx_gain_min = range.start();
296 rx_gain_max = range.stop();
297
298 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
299 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
300
301 tx_gain = usrp_dev->get_tx_gain();
302 rx_gain = usrp_dev->get_rx_gain();
303
304 return;
305}
306
307void uhd_device::set_ref_clk(bool ext_clk)
308{
309 uhd::clock_config_t clk_cfg;
310
311 clk_cfg.pps_source = uhd::clock_config_t::PPS_SMA;
kurtis.heimerl24481de2011-11-26 03:17:18 +0000312
313 if (ext_clk)
314 clk_cfg.ref_source = uhd::clock_config_t::REF_SMA;
315 else
316 clk_cfg.ref_source = uhd::clock_config_t::REF_INT;
317
318 usrp_dev->set_clock_config(clk_cfg);
319
320 return;
321}
322
323double uhd_device::set_rates(double rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000324{
kurtis.heimerld3e25902011-11-26 03:18:13 +0000325 double actual_rt, actual_clk_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000326
kurtis.heimerlce317332011-11-26 03:18:39 +0000327#ifndef RESAMPLE
kurtis.heimerld3e25902011-11-26 03:18:13 +0000328 // Set master clock rate
329 usrp_dev->set_master_clock_rate(master_clk_rt);
330 actual_clk_rt = usrp_dev->get_master_clock_rate();
331
332 if (actual_clk_rt != master_clk_rt) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000333 LOG(ERR) << "Failed to set master clock rate";
kurtis.heimerld3e25902011-11-26 03:18:13 +0000334 return -1.0;
335 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000336#endif
kurtis.heimerld3e25902011-11-26 03:18:13 +0000337
338 // Set sample rates
kurtis.heimerl24481de2011-11-26 03:17:18 +0000339 usrp_dev->set_tx_rate(rate);
340 usrp_dev->set_rx_rate(rate);
kurtis.heimerld3e25902011-11-26 03:18:13 +0000341 actual_rt = usrp_dev->get_tx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000342
kurtis.heimerld3e25902011-11-26 03:18:13 +0000343 if (actual_rt != rate) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000344 LOG(ERR) << "Actual sample rate differs from desired rate";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000345 return -1.0;
346 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000347 if (usrp_dev->get_rx_rate() != actual_rt) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000348 LOG(ERR) << "Transmit and receive sample rates do not match";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000349 return -1.0;
350 }
351
kurtis.heimerld3e25902011-11-26 03:18:13 +0000352 return actual_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000353}
354
kurtis.heimerl02d04052011-11-26 03:17:10 +0000355double uhd_device::setTxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000356{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000357 usrp_dev->set_tx_gain(db);
358 tx_gain = usrp_dev->get_tx_gain();
359
360 LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
361
362 return tx_gain;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000363}
364
kurtis.heimerl02d04052011-11-26 03:17:10 +0000365double uhd_device::setRxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000366{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000367 usrp_dev->set_rx_gain(db);
368 rx_gain = usrp_dev->get_rx_gain();
369
370 LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
371
372 return rx_gain;
373}
374
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000375/*
376 Parse the UHD device tree and mboard name to find out what device we're
377 dealing with. We need the bus type so that the transceiver knows how to
378 deal with the transport latency. Reject the USRP1 because UHD doesn't
379 support timestamped samples with it.
380 */
381bool uhd_device::parse_dev_type()
382{
383 std::string mboard_str, dev_str;
384 uhd::property_tree::sptr prop_tree;
385 size_t usrp1_str, usrp2_str, b100_str1, b100_str2;
386
387 prop_tree = usrp_dev->get_device()->get_tree();
388 dev_str = prop_tree->access<std::string>("/name").get();
389 mboard_str = usrp_dev->get_mboard_name();
390
391 usrp1_str = dev_str.find("USRP1");
392 b100_str1 = dev_str.find("B-Series");
393 b100_str2 = mboard_str.find("B100");
394
395 if (usrp1_str != std::string::npos) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000396 LOG(ERR) << "USRP1 is not supported using UHD driver";
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000397 return false;
398 }
399
400 if ((b100_str1 != std::string::npos) || (b100_str2 != std::string::npos)) {
401 bus = USB;
402 LOG(INFO) << "Using USB bus for " << dev_str;
403 } else {
404 bus = NET;
405 LOG(INFO) << "Using network bus for " << dev_str;
406 }
407
408 return true;
409}
410
kurtis.heimerl965e7572011-11-26 03:16:54 +0000411bool uhd_device::open()
412{
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000413 // Register msg handler
414 uhd::msg::register_handler(&uhd_msg_handler);
415
kurtis.heimerl2b28c692011-11-26 03:18:08 +0000416 // Allow all UHD devices
kurtis.heimerle380af32011-11-26 03:18:55 +0000417 LOG(INFO) << "Creating transceiver with first found UHD device";
kurtis.heimerl2b28c692011-11-26 03:18:08 +0000418 uhd::device_addr_t dev_addr("");
kurtis.heimerl965e7572011-11-26 03:16:54 +0000419 try {
420 usrp_dev = uhd::usrp::single_usrp::make(dev_addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000421 } catch(...) {
kurtis.heimerle417c5a2011-11-26 03:19:25 +0000422 LOG(ALERT) << "UHD make failed";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000423 return false;
424 }
425
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000426#ifdef EXTREF
427 set_ref_clk(true);
428#endif
429
kurtis.heimerl965e7572011-11-26 03:16:54 +0000430 // Number of samples per over-the-wire packet
431 tx_spp = usrp_dev->get_device()->get_max_send_samps_per_packet();
432 rx_spp = usrp_dev->get_device()->get_max_recv_samps_per_packet();
433
434 // Set rates
kurtis.heimerl24481de2011-11-26 03:17:18 +0000435 actual_smpl_rt = set_rates(desired_smpl_rt);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000436 if (actual_smpl_rt < 0)
437 return false;
438
439 // Create receive buffer
440 size_t buf_len = smpl_buf_sz / sizeof(uint32_t);
441 rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
442
443 // Set receive chain sample offset
444 ts_offset = (TIMESTAMP)(rx_smpl_offset * actual_smpl_rt);
445
kurtis.heimerl02d04052011-11-26 03:17:10 +0000446 // Initialize and shadow gain values
447 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000448
kurtis.heimerl965e7572011-11-26 03:16:54 +0000449 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000450 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000451
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000452 // Check for a valid device type and set bus type
453 if (!parse_dev_type())
454 return false;
455
kurtis.heimerl965e7572011-11-26 03:16:54 +0000456 return true;
457}
458
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000459bool uhd_device::flush_recv(size_t num_pkts)
460{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000461 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000462 size_t num_smpls;
463 uint32_t buff[rx_spp];
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000464 float timeout;
465
466 // Use .01 sec instead of the default .1 sec
467 timeout = .01;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000468
469 for (size_t i = 0; i < num_pkts; i++) {
470 num_smpls = usrp_dev->get_device()->recv(
471 buff,
472 rx_spp,
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000473 md,
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000474 uhd::io_type_t::COMPLEX_INT16,
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000475 uhd::device::RECV_MODE_ONE_PACKET,
476 timeout);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000477
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000478 if (!num_smpls) {
479 switch (md.error_code) {
480 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
481 return true;
482 default:
483 continue;
484 }
485 }
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000486 }
487
488 return true;
489}
490
kurtis.heimerl68292102011-11-26 03:17:28 +0000491void uhd_device::restart(uhd::time_spec_t ts)
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000492{
kurtis.heimerl68292102011-11-26 03:17:28 +0000493 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
494 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000495
kurtis.heimerl68292102011-11-26 03:17:28 +0000496 flush_recv(50);
497
498 usrp_dev->set_time_now(ts);
499 aligned = false;
500
501 cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
502 cmd.stream_now = true;
503 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000504}
505
kurtis.heimerl965e7572011-11-26 03:16:54 +0000506bool uhd_device::start()
507{
508 LOG(INFO) << "Starting USRP...";
509
510 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000511 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000512 return false;
513 }
514
515 setPriority();
516
517 // Start asynchronous event (underrun check) loop
518 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
519
520 // Start streaming
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000521 restart(uhd::time_spec_t(0.0));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000522
kurtis.heimerl965e7572011-11-26 03:16:54 +0000523 // Display usrp time
524 double time_now = usrp_dev->get_time_now().get_real_secs();
525 LOG(INFO) << "The current time is " << time_now << " seconds";
526
527 started = true;
528 return true;
529}
530
531bool uhd_device::stop()
532{
533 uhd::stream_cmd_t stream_cmd =
534 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
535
536 usrp_dev->issue_stream_cmd(stream_cmd);
537
538 started = false;
539 return true;
540}
541
542void uhd_device::setPriority()
543{
544 uhd::set_thread_priority_safe();
545 return;
546}
547
kurtis.heimerld4be0742011-11-26 03:17:46 +0000548int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000549{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000550 uhd::time_spec_t ts;
551
kurtis.heimerld4be0742011-11-26 03:17:46 +0000552 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000553 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000554
555 switch (md.error_code) {
556 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
557 return ERROR_UNRECOVERABLE;
558 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
559 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
560 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
561 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
562 default:
563 return ERROR_UNHANDLED;
564 }
565 }
566
kurtis.heimerl965e7572011-11-26 03:16:54 +0000567 // Missing timestamp
568 if (!md.has_time_spec) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000569 LOG(ERR) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000570 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000571 }
572
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000573 ts = md.time_spec;
574
kurtis.heimerl965e7572011-11-26 03:16:54 +0000575 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000576 if (ts < prev_ts) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000577 LOG(ERR) << "UHD: Loss of monotonic: " << ts.get_real_secs();
578 LOG(ERR) << "UHD: Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000579 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000580 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000581 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000582 }
583
584 return 0;
585}
586
587int uhd_device::readSamples(short *buf, int len, bool *overrun,
588 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
589{
590 ssize_t rc;
591 uhd::time_spec_t ts;
592 uhd::rx_metadata_t metadata;
593 uint32_t pkt_buf[rx_spp];
594
595 if (skip_rx)
596 return 0;
597
598 // Shift read time with respect to transmit clock
599 timestamp += ts_offset;
600
601 ts = convert_time(timestamp, actual_smpl_rt);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000602 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000603
604 // Check that timestamp is valid
605 rc = rx_smpl_buf->avail_smpls(timestamp);
606 if (rc < 0) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000607 LOG(ERR) << rx_smpl_buf->str_code(rc);
608 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000609 return 0;
610 }
611
612 // Receive samples from the usrp until we have enough
613 while (rx_smpl_buf->avail_smpls(timestamp) < len) {
614 size_t num_smpls = usrp_dev->get_device()->recv(
615 (void*)pkt_buf,
616 rx_spp,
617 metadata,
618 uhd::io_type_t::COMPLEX_INT16,
619 uhd::device::RECV_MODE_ONE_PACKET);
620
621 rx_pkt_cnt++;
622
kurtis.heimerld4be0742011-11-26 03:17:46 +0000623 // Check for errors
624 rc = check_rx_md_err(metadata, num_smpls);
625 switch (rc) {
626 case ERROR_UNRECOVERABLE:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000627 LOG(ERR) << "UHD: Unrecoverable error, exiting.";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000628 exit(-1);
629 case ERROR_TIMING:
630 restart(prev_ts);
631 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000632 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000633 }
634
kurtis.heimerl965e7572011-11-26 03:16:54 +0000635
636 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000637 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000638
639 rc = rx_smpl_buf->write(pkt_buf,
640 num_smpls,
641 metadata.time_spec);
642
643 // Continue on local overrun, exit on other errors
644 if ((rc < 0)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000645 LOG(ERR) << rx_smpl_buf->str_code(rc);
646 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000647 if (rc != smpl_buf::ERROR_OVERFLOW)
648 return 0;
649 }
650 }
651
652 // We have enough samples
653 rc = rx_smpl_buf->read(buf, len, timestamp);
654 if ((rc < 0) || (rc != len)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000655 LOG(ERR) << rx_smpl_buf->str_code(rc);
656 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000657 return 0;
658 }
659
660 return len;
661}
662
663int uhd_device::writeSamples(short *buf, int len, bool *underrun,
664 unsigned long long timestamp,bool isControl)
665{
666 uhd::tx_metadata_t metadata;
667 metadata.has_time_spec = true;
668 metadata.start_of_burst = false;
669 metadata.end_of_burst = false;
670 metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
671
672 // No control packets
673 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000674 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000675 return 0;
676 }
677
678 // Drop a fixed number of packets (magic value)
679 if (!aligned) {
680 drop_cnt++;
681
682 if (drop_cnt == 1) {
683 LOG(DEBUG) << "Aligning transmitter: stop burst";
684 metadata.end_of_burst = true;
685 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000686 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000687 *underrun = true;
688 return len;
689 } else {
690 LOG(DEBUG) << "Aligning transmitter: start burst";
691 metadata.start_of_burst = true;
692 aligned = true;
693 drop_cnt = 0;
694 }
695 }
696
697 size_t num_smpls = usrp_dev->get_device()->send(buf,
698 len,
699 metadata,
700 uhd::io_type_t::COMPLEX_INT16,
701 uhd::device::SEND_MODE_FULL_BUFF);
702
703 if (num_smpls != (unsigned)len)
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000704 LOG(ERR) << "UHD: Sent fewer samples than requested";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000705
706 return num_smpls;
707}
708
709bool uhd_device::updateAlignment(TIMESTAMP timestamp)
710{
kurtis.heimerl13074c92011-11-26 03:17:43 +0000711 aligned = false;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000712 return true;
713}
714
715bool uhd_device::setTxFreq(double wFreq)
716{
717 uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000718 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000719 tx_freq = usrp_dev->get_tx_freq();
720
kurtis.heimerl965e7572011-11-26 03:16:54 +0000721 return true;
722}
723
724bool uhd_device::setRxFreq(double wFreq)
725{
726 uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000727 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000728 rx_freq = usrp_dev->get_rx_freq();
729
kurtis.heimerl965e7572011-11-26 03:16:54 +0000730 return true;
731}
732
733bool uhd_device::recv_async_msg()
734{
735 uhd::async_metadata_t metadata;
736 if (!usrp_dev->get_device()->recv_async_msg(metadata))
737 return false;
738
739 // Assume that any error requires resynchronization
740 if (metadata.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
741 aligned = false;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000742 LOG(ERR) << str_code(metadata);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000743 }
744
745 return true;
746}
747
748std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
749{
750 std::ostringstream ost("UHD: ");
751
752 switch (metadata.error_code) {
753 case uhd::rx_metadata_t::ERROR_CODE_NONE:
754 ost << "No error";
755 break;
756 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
757 ost << "No packet received, implementation timed-out";
758 break;
759 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
760 ost << "A stream command was issued in the past";
761 break;
762 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
763 ost << "Expected another stream command";
764 break;
765 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
766 ost << "An internal receive buffer has filled";
767 break;
768 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
769 ost << "The packet could not be parsed";
770 break;
771 default:
772 ost << "Unknown error " << metadata.error_code;
773 }
774
775 if (metadata.has_time_spec)
776 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
777
778 return ost.str();
779}
780
781std::string uhd_device::str_code(uhd::async_metadata_t metadata)
782{
783 std::ostringstream ost("UHD: ");
784
785 switch (metadata.event_code) {
786 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
787 ost << "A packet was successfully transmitted";
788 break;
789 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
790 ost << "An internal send buffer has emptied";
791 break;
792 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
793 ost << "Packet loss between host and device";
794 break;
795 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
796 ost << "Packet time was too late or too early";
797 break;
798 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
799 ost << "Underflow occurred inside a packet";
800 break;
801 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
802 ost << "Packet loss within a burst";
803 break;
804 default:
805 ost << "Unknown error " << metadata.event_code;
806 }
807
808 if (metadata.has_time_spec)
809 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
810
811 return ost.str();
812}
813
814smpl_buf::smpl_buf(size_t len, double rate)
815 : buf_len(len), clk_rt(rate),
816 time_start(0), time_end(0), data_start(0), data_end(0)
817{
818 data = new uint32_t[len];
819}
820
821smpl_buf::~smpl_buf()
822{
823 delete[] data;
824}
825
826ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
827{
828 if (timestamp < time_start)
829 return ERROR_TIMESTAMP;
830 else if (timestamp >= time_end)
831 return 0;
832 else
833 return time_end - timestamp;
834}
835
836ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
837{
838 return avail_smpls(convert_time(timespec, clk_rt));
839}
840
841ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
842{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000843 int type_sz = 2 * sizeof(short);
844
kurtis.heimerl965e7572011-11-26 03:16:54 +0000845 // Check for valid read
846 if (timestamp < time_start)
847 return ERROR_TIMESTAMP;
848 if (timestamp >= time_end)
849 return 0;
850 if (len >= buf_len)
851 return ERROR_READ;
852
853 // How many samples should be copied
854 size_t num_smpls = time_end - timestamp;
855 if (num_smpls > len);
856 num_smpls = len;
857
858 // Starting index
859 size_t read_start = data_start + (timestamp - time_start);
860
861 // Read it
862 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000863 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000864 memcpy(buf, data + read_start, numBytes);
865 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000866 size_t first_cp = (buf_len - read_start) * type_sz;
867 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000868
869 memcpy(buf, data + read_start, first_cp);
870 memcpy((char*) buf + first_cp, data, second_cp);
871 }
872
873 data_start = (read_start + len) % buf_len;
874 time_start = timestamp + len;
875
876 if (time_start > time_end)
877 return ERROR_READ;
878 else
879 return num_smpls;
880}
881
882ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
883{
884 return read(buf, len, convert_time(ts, clk_rt));
885}
886
887ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
888{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000889 int type_sz = 2 * sizeof(short);
890
kurtis.heimerl965e7572011-11-26 03:16:54 +0000891 // Check for valid write
892 if ((len == 0) || (len >= buf_len))
893 return ERROR_WRITE;
894 if ((timestamp + len) <= time_end)
895 return ERROR_TIMESTAMP;
896
897 // Starting index
898 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
899
900 // Write it
901 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000902 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000903 memcpy(data + write_start, buf, numBytes);
904 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000905 size_t first_cp = (buf_len - write_start) * type_sz;
906 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000907
908 memcpy(data + write_start, buf, first_cp);
909 memcpy(data, (char*) buf + first_cp, second_cp);
910 }
911
912 data_end = (write_start + len) % buf_len;
913 time_end = timestamp + len;
914
915 if (((write_start + len) > buf_len) && (data_end > data_start))
916 return ERROR_OVERFLOW;
917 else if (time_end <= time_start)
918 return ERROR_WRITE;
919 else
920 return len;
921}
922
923ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
924{
925 return write(buf, len, convert_time(ts, clk_rt));
926}
927
928std::string smpl_buf::str_status() const
929{
930 std::ostringstream ost("Sample buffer: ");
931
932 ost << "length = " << buf_len;
933 ost << ", time_start = " << time_start;
934 ost << ", time_end = " << time_end;
935 ost << ", data_start = " << data_start;
936 ost << ", data_end = " << data_end;
937
938 return ost.str();
939}
940
941std::string smpl_buf::str_code(ssize_t code)
942{
943 switch (code) {
944 case ERROR_TIMESTAMP:
945 return "Sample buffer: Requested timestamp is not valid";
946 case ERROR_READ:
947 return "Sample buffer: Read error";
948 case ERROR_WRITE:
949 return "Sample buffer: Write error";
950 case ERROR_OVERFLOW:
951 return "Sample buffer: Overrun";
952 default:
953 return "Sample buffer: Unknown error";
954 }
955}
956
957RadioDevice *RadioDevice::make(double smpl_rt, bool skip_rx)
958{
959 return new uhd_device(smpl_rt, skip_rx);
960}