blob: 6275cb2d20f659dd4c4c8cc7199cbc041292d21c [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"
ttsoub371ed52012-01-09 18:11:34 +000025#include <uhd/version.hpp>
kurtis.heimerle380af32011-11-26 03:18:55 +000026#include <uhd/property_tree.hpp>
kurtis.heimerl856bbbe2011-12-15 00:50:16 +000027#include <uhd/usrp/multi_usrp.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000028#include <uhd/utils/thread_priority.hpp>
kurtis.heimerl0803ad92011-11-26 03:18:51 +000029#include <uhd/utils/msg.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000030
kurtis.heimerlce317332011-11-26 03:18:39 +000031#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
Thomas Tsoufe269fe2013-10-14 23:56:51 -040035#define B2XX_CLK_RT 52e6
36#define B2XX_BASE_RT GSMRATE
37#define B100_BASE_RT 400000
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040038#define USRP2_BASE_RT 390625
Thomas Tsoue3e88142013-04-05 20:42:41 -040039#define TX_AMPL 0.3
40#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040041
42enum uhd_dev_type {
43 USRP1,
44 USRP2,
45 B100,
Thomas Tsou69d14c92013-10-11 14:27:35 -040046 B2XX,
Thomas Tsouc88d8d52013-08-21 17:55:54 -040047 UMTRX,
Thomas Tsou02d88d12013-04-05 15:36:30 -040048 NUM_USRP_TYPES,
49};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000050
Thomas Tsoue3e88142013-04-05 20:42:41 -040051struct uhd_dev_offset {
52 enum uhd_dev_type type;
53 int sps;
54 double offset;
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050055 const std::string desc;
Thomas Tsoue3e88142013-04-05 20:42:41 -040056};
57
kurtis.heimerl965e7572011-11-26 03:16:54 +000058/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040059 * Tx / Rx sample offset values. In a perfect world, there is no group delay
60 * though analog components, and behaviour through digital filters exactly
61 * matches calculated values. In reality, there are unaccounted factors,
62 * which are captured in these empirically measured (using a loopback test)
63 * timing correction values.
64 *
65 * Notes:
66 * USRP1 with timestamps is not supported by UHD.
67 */
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080068static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050069 { USRP1, 1, 0.0, "USRP1 not supported" },
70 { USRP1, 4, 0.0, "USRP1 not supported"},
71 { USRP2, 1, 1.2184e-4, "N2XX 1 SPS" },
72 { USRP2, 4, 8.0230e-5, "N2XX 4 SPS" },
73 { B100, 1, 1.2104e-4, "B100 1 SPS" },
74 { B100, 4, 7.9307e-5, "B100 4 SPS" },
75 { B2XX, 1, 9.9692e-5, "B2XX 1 SPS" },
76 { B2XX, 4, 6.9248e-5, "B2XX 4 SPS" },
77 { UMTRX, 1, 9.9692e-5, "UmTRX 1 SPS" },
78 { UMTRX, 4, 7.3846e-5, "UmTRX 4 SPS" },
Thomas Tsoue3e88142013-04-05 20:42:41 -040079};
kurtis.heimerl965e7572011-11-26 03:16:54 +000080
Thomas Tsoue90a42b2013-11-13 23:38:09 -050081/*
82 * Offset handling for special cases. Currently used for UmTRX dual channel
83 * diversity receiver only.
84 */
85static struct uhd_dev_offset special_offsets[] = {
86 { UMTRX, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
87 { UMTRX, 4, 5.2103e-5, "UmTRX diversity, 4 SPS" },
88};
89
90static double get_dev_offset(enum uhd_dev_type type,
91 int sps, bool diversity = false)
Thomas Tsoue3e88142013-04-05 20:42:41 -040092{
Thomas Tsou2e622ff2013-11-15 18:35:04 -050093 struct uhd_dev_offset *offset;
94
Thomas Tsoue90a42b2013-11-13 23:38:09 -050095 /* Reject USRP1 */
Thomas Tsoue3e88142013-04-05 20:42:41 -040096 if (type == USRP1) {
97 LOG(ERR) << "Invalid device type";
98 return 0.0;
99 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000100
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500101 /* Special cases (e.g. diversity receiver) */
102 if (diversity) {
103 if (type != UMTRX) {
104 LOG(ALERT) << "Diversity on UmTRX only";
105 return 0.0;
106 }
107
108 switch (sps) {
109 case 1:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500110 offset = &special_offsets[0];
111 break;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500112 case 4:
113 default:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500114 offset = &special_offsets[1];
115 }
116 } else {
117 /* Normal operation */
118 switch (sps) {
119 case 1:
120 offset = &uhd_offsets[2 * type + 0];
121 break;
122 case 4:
123 default:
124 offset = &uhd_offsets[2 * type + 1];
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500125 }
126 }
127
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500128 std::cout << "-- Setting " << offset->desc << std::endl;
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000129
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500130 return offset->offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -0400131}
kurtis.heimerlce317332011-11-26 03:18:39 +0000132
Thomas Tsoucb69f082013-04-08 14:18:26 -0400133/*
134 * Select sample rate based on device type and requested samples-per-symbol.
135 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
136 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800137 */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500138static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400139{
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500140 if (diversity && (type == UMTRX)) {
141 return GSMRATE * 4;
142 } else if (diversity) {
143 LOG(ALERT) << "Diversity supported on UmTRX only";
144 return -9999.99;
145 }
146
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800147 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400148 return -9999.99;
149
150 switch (type) {
151 case USRP2:
152 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400153 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400154 return B100_BASE_RT * sps;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400155 case B2XX:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400156 case UMTRX:
157 return GSMRATE * sps;
158 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400159 break;
160 }
161
162 LOG(ALERT) << "Unknown device type " << type;
163 return -9999.99;
164}
165
kurtis.heimerl965e7572011-11-26 03:16:54 +0000166/** Timestamp conversion
167 @param timestamp a UHD or OpenBTS timestamp
168 @param rate sample rate
169 @return the converted timestamp
170*/
171uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
172{
173 double secs = (double) ticks / rate;
174 return uhd::time_spec_t(secs);
175}
176
177TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
178{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +0000179 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000180 return ts.get_tick_count(rate) + ticks;
181}
182
183/*
184 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
185 or UHD style timestamps. Time conversions are handled
186 internally or accessable through the static convert calls.
187*/
188class smpl_buf {
189public:
190 /** Sample buffer constructor
191 @param len number of 32-bit samples the buffer should hold
192 @param rate sample clockrate
193 @param timestamp
194 */
195 smpl_buf(size_t len, double rate);
196 ~smpl_buf();
197
198 /** Query number of samples available for reading
199 @param timestamp time of first sample
200 @return number of available samples or error
201 */
202 ssize_t avail_smpls(TIMESTAMP timestamp) const;
203 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
204
205 /** Read and write
206 @param buf pointer to buffer
207 @param len number of samples desired to read or write
208 @param timestamp time of first stample
209 @return number of actual samples read or written or error
210 */
211 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
212 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
213 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
214 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
215
216 /** Buffer status string
217 @return a formatted string describing internal buffer state
218 */
219 std::string str_status() const;
220
221 /** Formatted error string
222 @param code an error code
223 @return a formatted error string
224 */
225 static std::string str_code(ssize_t code);
226
227 enum err_code {
228 ERROR_TIMESTAMP = -1,
229 ERROR_READ = -2,
230 ERROR_WRITE = -3,
231 ERROR_OVERFLOW = -4
232 };
233
234private:
235 uint32_t *data;
236 size_t buf_len;
237
238 double clk_rt;
239
240 TIMESTAMP time_start;
241 TIMESTAMP time_end;
242
243 size_t data_start;
244 size_t data_end;
245};
246
247/*
248 uhd_device - UHD implementation of the Device interface. Timestamped samples
249 are sent to and received from the device. An intermediate buffer
250 on the receive side collects and aligns packets of samples.
251 Events and errors such as underruns are reported asynchronously
252 by the device and received in a separate thread.
253*/
254class uhd_device : public RadioDevice {
255public:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500256 uhd_device(size_t sps, size_t chans, bool diversity);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000257 ~uhd_device();
258
Thomas Tsou010fff72013-10-16 00:31:18 -0400259 int open(const std::string &args, bool extref);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000260 bool start();
261 bool stop();
kurtis.heimerl68292102011-11-26 03:17:28 +0000262 void restart(uhd::time_spec_t ts);
Thomas Tsou7553aa92013-11-08 12:50:03 -0500263 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400264 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000265
Thomas Tsou204a9f12013-10-29 18:34:16 -0400266 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000267 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
268
Thomas Tsou204a9f12013-10-29 18:34:16 -0400269 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000270 TIMESTAMP timestamp, bool isControl);
271
272 bool updateAlignment(TIMESTAMP timestamp);
273
Thomas Tsou204a9f12013-10-29 18:34:16 -0400274 bool setTxFreq(double wFreq, size_t chan);
275 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000276
277 inline TIMESTAMP initialWriteTimestamp() { return 0; }
278 inline TIMESTAMP initialReadTimestamp() { return 0; }
279
Thomas Tsoue3e88142013-04-05 20:42:41 -0400280 inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000281 inline double fullScaleOutputValue() { return 32000; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000282
Thomas Tsou204a9f12013-10-29 18:34:16 -0400283 double setRxGain(double db, size_t chan);
284 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000285 double maxRxGain(void) { return rx_gain_max; }
286 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000287
Thomas Tsou204a9f12013-10-29 18:34:16 -0400288 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000289 double maxTxGain(void) { return tx_gain_max; }
290 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000291
Thomas Tsou204a9f12013-10-29 18:34:16 -0400292 double getTxFreq(size_t chan);
293 double getRxFreq(size_t chan);
294 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000295
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400296 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000297 inline double numberRead() { return rx_pkt_cnt; }
298 inline double numberWritten() { return 0; }
299
300 /** Receive and process asynchronous message
301 @return true if message received or false on timeout or error
302 */
303 bool recv_async_msg();
304
kurtis.heimerld4be0742011-11-26 03:17:46 +0000305 enum err_code {
306 ERROR_TIMING = -1,
307 ERROR_UNRECOVERABLE = -2,
308 ERROR_UNHANDLED = -3,
309 };
310
kurtis.heimerl965e7572011-11-26 03:16:54 +0000311private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000312 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400313 uhd::tx_streamer::sptr tx_stream;
314 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400315 enum TxWindowType tx_window;
316 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000317
Thomas Tsou204a9f12013-10-29 18:34:16 -0400318 size_t sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400319 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000320
Thomas Tsou204a9f12013-10-29 18:34:16 -0400321 double tx_gain_min, tx_gain_max;
322 double rx_gain_min, rx_gain_max;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000323
Thomas Tsou204a9f12013-10-29 18:34:16 -0400324 std::vector<double> tx_gains, rx_gains;
325 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000326 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000327
328 bool started;
329 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000330
331 size_t rx_pkt_cnt;
332 size_t drop_cnt;
333 uhd::time_spec_t prev_ts;
334
335 TIMESTAMP ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400336 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000337
kurtis.heimerl02d04052011-11-26 03:17:10 +0000338 void init_gains();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400339 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400340 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000341 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000342 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000343 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000344
kurtis.heimerl965e7572011-11-26 03:16:54 +0000345 std::string str_code(uhd::rx_metadata_t metadata);
346 std::string str_code(uhd::async_metadata_t metadata);
347
348 Thread async_event_thrd;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500349 bool diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000350};
351
352void *async_event_loop(uhd_device *dev)
353{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500354 dev->setPriority(0.43);
355
kurtis.heimerl965e7572011-11-26 03:16:54 +0000356 while (1) {
357 dev->recv_async_msg();
358 pthread_testcancel();
359 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500360
361 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000362}
363
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000364/*
365 Catch and drop underrun 'U' and overrun 'O' messages from stdout
366 since we already report using the logging facility. Direct
367 everything else appropriately.
368 */
369void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
370{
371 switch (type) {
372 case uhd::msg::status:
373 LOG(INFO) << msg;
374 break;
375 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000376 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000377 break;
378 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000379 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000380 break;
381 case uhd::msg::fastpath:
382 break;
383 }
384}
385
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500386uhd_device::uhd_device(size_t sps, size_t chans, bool diversity)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400387 : tx_gain_min(0.0), tx_gain_max(0.0),
388 rx_gain_min(0.0), rx_gain_max(0.0),
389 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000390 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou204a9f12013-10-29 18:34:16 -0400391 prev_ts(0,0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000392{
Thomas Tsoue3e88142013-04-05 20:42:41 -0400393 this->sps = sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400394 this->chans = chans;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500395 this->diversity = diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000396}
397
398uhd_device::~uhd_device()
399{
400 stop();
401
Thomas Tsou204a9f12013-10-29 18:34:16 -0400402 for (size_t i = 0; i < rx_buffers.size(); i++)
403 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000404}
405
kurtis.heimerl24481de2011-11-26 03:17:18 +0000406void uhd_device::init_gains()
407{
408 uhd::gain_range_t range;
409
410 range = usrp_dev->get_tx_gain_range();
411 tx_gain_min = range.start();
412 tx_gain_max = range.stop();
413
414 range = usrp_dev->get_rx_gain_range();
415 rx_gain_min = range.start();
416 rx_gain_max = range.stop();
417
Thomas Tsou204a9f12013-10-29 18:34:16 -0400418 for (size_t i = 0; i < tx_gains.size(); i++) {
419 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2, i);
420 tx_gains[i] = usrp_dev->get_tx_gain(i);
421 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000422
Thomas Tsou204a9f12013-10-29 18:34:16 -0400423 for (size_t i = 0; i < rx_gains.size(); i++) {
424 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2, i);
425 rx_gains[i] = usrp_dev->get_rx_gain(i);
426 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000427
428 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400429
kurtis.heimerl24481de2011-11-26 03:17:18 +0000430}
431
Thomas Tsou02d88d12013-04-05 15:36:30 -0400432int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000433{
Thomas Tsou092f7572013-04-04 17:04:39 -0400434 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000435
Thomas Tsou7e068062013-04-08 19:39:37 -0400436 try {
437 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400438 } catch (const std::exception &ex) {
439 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
440 LOG(ALERT) << ex.what();
441 return -1;
442 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000443
Thomas Tsou092f7572013-04-04 17:04:39 -0400444 actual = usrp_dev->get_master_clock_rate();
445 offset = fabs(clk_rate - actual);
446
447 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000448 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400449 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400450 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400451 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000452 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400453
454 return 0;
455}
456
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400457int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400458{
Thomas Tsou092f7572013-04-04 17:04:39 -0400459 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400460 double tx_offset, rx_offset;
461
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400462 // B2XX is the only device where we set FPGA clocking
463 if (dev_type == B2XX) {
464 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400465 return -1;
466 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000467
468 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400469 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400470 usrp_dev->set_tx_rate(tx_rate);
471 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400472 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400473 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400474 LOG(ALERT) << ex.what();
475 return -1;
476 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400477 this->tx_rate = usrp_dev->get_tx_rate();
478 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000479
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400480 tx_offset = fabs(this->tx_rate - tx_rate);
481 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400482 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000483 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400484 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
485 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400486 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000487 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000488
Thomas Tsou02d88d12013-04-05 15:36:30 -0400489 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000490}
491
Thomas Tsou204a9f12013-10-29 18:34:16 -0400492double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000493{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400494 if (chan >= tx_gains.size()) {
495 LOG(ALERT) << "Requested non-existent channel" << chan;
496 return 0.0f;
497 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000498
Thomas Tsou204a9f12013-10-29 18:34:16 -0400499 usrp_dev->set_tx_gain(db, chan);
500 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000501
Thomas Tsou204a9f12013-10-29 18:34:16 -0400502 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
503
504 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000505}
506
Thomas Tsou204a9f12013-10-29 18:34:16 -0400507double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000508{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400509 if (chan >= rx_gains.size()) {
510 LOG(ALERT) << "Requested non-existent channel " << chan;
511 return 0.0f;
512 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000513
Thomas Tsou204a9f12013-10-29 18:34:16 -0400514 usrp_dev->set_rx_gain(db, chan);
515 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000516
Thomas Tsou204a9f12013-10-29 18:34:16 -0400517 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB";
518
519 return rx_gains[chan];
520}
521
522double uhd_device::getRxGain(size_t chan)
523{
524 if (chan >= rx_gains.size()) {
525 LOG(ALERT) << "Requested non-existent channel " << chan;
526 return 0.0f;
527 }
528
529 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000530}
531
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000532/*
533 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400534 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000535 deal with the transport latency. Reject the USRP1 because UHD doesn't
536 support timestamped samples with it.
537 */
538bool uhd_device::parse_dev_type()
539{
540 std::string mboard_str, dev_str;
541 uhd::property_tree::sptr prop_tree;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400542 size_t usrp1_str, usrp2_str, b100_str, b200_str, b210_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000543
544 prop_tree = usrp_dev->get_device()->get_tree();
545 dev_str = prop_tree->access<std::string>("/name").get();
546 mboard_str = usrp_dev->get_mboard_name();
547
548 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400549 usrp2_str = dev_str.find("USRP2");
550 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400551 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400552 b210_str = mboard_str.find("B210");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400553 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000554
555 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000556 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
557 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400558 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000559 return false;
560 }
561
Thomas Tsoucb69f082013-04-08 14:18:26 -0400562 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400563 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400564 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400565 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500566 tx_window = TX_WINDOW_USRP1;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400567 dev_type = B2XX;
568 } else if (b210_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500569 tx_window = TX_WINDOW_USRP1;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400570 dev_type = B2XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400571 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500572 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400573 dev_type = USRP2;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400574 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500575 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400576 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400577 } else {
Thomas Tsou092f7572013-04-04 17:04:39 -0400578 LOG(ALERT) << "Unknown UHD device type " << dev_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400579 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000580 }
581
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500582 if (tx_window == TX_WINDOW_USRP1) {
583 LOG(INFO) << "Using USRP1 type transmit window for "
584 << dev_str << " " << mboard_str;
585 } else {
586 LOG(INFO) << "Using fixed transmit window for "
587 << dev_str << " " << mboard_str;
588 }
589
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000590 return true;
591}
592
Thomas Tsou010fff72013-10-16 00:31:18 -0400593int uhd_device::open(const std::string &args, bool extref)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000594{
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000595 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000596 uhd::device_addr_t addr(args);
597 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000598 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000599 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400600 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000601 }
602
603 // Use the first found device
604 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000605 try {
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000606 usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
kurtis.heimerle380af32011-11-26 03:18:55 +0000607 } catch(...) {
ttsou3b5c0c12012-02-14 17:58:11 +0000608 LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400609 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000610 }
611
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000612 // Check for a valid device type and set bus type
613 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400614 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000615
Thomas Tsou204a9f12013-10-29 18:34:16 -0400616 // Verify and set channels
617 if ((dev_type == UMTRX) && (chans == 2)) {
618 uhd::usrp::subdev_spec_t subdev_spec("A:0 B:0");
619 usrp_dev->set_tx_subdev_spec(subdev_spec);
620 usrp_dev->set_rx_subdev_spec(subdev_spec);
621 } else if (chans != 1) {
622 LOG(ALERT) << "Invalid channel combination for device";
623 return -1;
624 }
625
626 tx_freqs.resize(chans);
627 rx_freqs.resize(chans);
628 tx_gains.resize(chans);
629 rx_gains.resize(chans);
630 rx_buffers.resize(chans);
631
Thomas Tsou010fff72013-10-16 00:31:18 -0400632 if (extref)
Thomas Tsou0169b312013-10-29 19:25:15 -0400633 usrp_dev->set_clock_source("external");
Thomas Tsou010fff72013-10-16 00:31:18 -0400634
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400635 // Create TX and RX streamers
636 uhd::stream_args_t stream_args("sc16");
Thomas Tsou204a9f12013-10-29 18:34:16 -0400637 for (size_t i = 0; i < chans; i++)
638 stream_args.channels.push_back(i);
639
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400640 tx_stream = usrp_dev->get_tx_stream(stream_args);
641 rx_stream = usrp_dev->get_rx_stream(stream_args);
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000642
kurtis.heimerl965e7572011-11-26 03:16:54 +0000643 // Number of samples per over-the-wire packet
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400644 tx_spp = tx_stream->get_max_num_samps();
645 rx_spp = rx_stream->get_max_num_samps();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000646
647 // Set rates
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500648 double _rx_rate;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400649 double _tx_rate = select_rate(dev_type, sps);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500650 if (diversity)
651 _rx_rate = select_rate(dev_type, 1, true);
652 else
653 _rx_rate = _tx_rate / sps;
654
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400655 if ((_tx_rate > 0.0) && (set_rates(_tx_rate, _rx_rate) < 0))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400656 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000657
658 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400659 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400660 for (size_t i = 0; i < rx_buffers.size(); i++)
661 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000662
663 // Set receive chain sample offset
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500664 double offset = get_dev_offset(dev_type, sps, diversity);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400665 if (offset == 0.0) {
666 LOG(ERR) << "Unsupported configuration, no correction applied";
667 ts_offset = 0;
668 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400669 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400670 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000671
kurtis.heimerl02d04052011-11-26 03:17:10 +0000672 // Initialize and shadow gain values
673 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000674
kurtis.heimerl965e7572011-11-26 03:16:54 +0000675 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000676 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000677
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500678 if (diversity)
679 return DIVERSITY;
680
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400681 switch (dev_type) {
682 case B100:
683 return RESAMP_64M;
684 case USRP2:
685 return RESAMP_100M;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500686 default:
687 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400688 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400689
690 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000691}
692
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000693bool uhd_device::flush_recv(size_t num_pkts)
694{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000695 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000696 size_t num_smpls;
697 uint32_t buff[rx_spp];
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000698 float timeout;
699
700 // Use .01 sec instead of the default .1 sec
701 timeout = .01;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000702
703 for (size_t i = 0; i < num_pkts; i++) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400704 num_smpls = rx_stream->recv(buff, rx_spp, md,
705 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000706 if (!num_smpls) {
707 switch (md.error_code) {
708 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
709 return true;
710 default:
711 continue;
712 }
713 }
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000714 }
715
716 return true;
717}
718
kurtis.heimerl68292102011-11-26 03:17:28 +0000719void uhd_device::restart(uhd::time_spec_t ts)
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000720{
kurtis.heimerl68292102011-11-26 03:17:28 +0000721 usrp_dev->set_time_now(ts);
722 aligned = false;
723
Thomas Tsou204a9f12013-10-29 18:34:16 -0400724 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
725 cmd.time_spec = uhd::time_spec_t(0.1);
726 cmd.stream_now = false;
727
kurtis.heimerl68292102011-11-26 03:17:28 +0000728 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000729}
730
kurtis.heimerl965e7572011-11-26 03:16:54 +0000731bool uhd_device::start()
732{
733 LOG(INFO) << "Starting USRP...";
734
735 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000736 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000737 return false;
738 }
739
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400740 // Register msg handler
741 uhd::msg::register_handler(&uhd_msg_handler);
742
kurtis.heimerl965e7572011-11-26 03:16:54 +0000743 // Start asynchronous event (underrun check) loop
744 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
745
746 // Start streaming
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000747 restart(uhd::time_spec_t(0.0));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000748
kurtis.heimerl965e7572011-11-26 03:16:54 +0000749 // Display usrp time
750 double time_now = usrp_dev->get_time_now().get_real_secs();
751 LOG(INFO) << "The current time is " << time_now << " seconds";
752
753 started = true;
754 return true;
755}
756
757bool uhd_device::stop()
758{
759 uhd::stream_cmd_t stream_cmd =
760 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
761
762 usrp_dev->issue_stream_cmd(stream_cmd);
763
764 started = false;
765 return true;
766}
767
Thomas Tsou7553aa92013-11-08 12:50:03 -0500768void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000769{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500770 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000771 return;
772}
773
kurtis.heimerld4be0742011-11-26 03:17:46 +0000774int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000775{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000776 uhd::time_spec_t ts;
777
kurtis.heimerld4be0742011-11-26 03:17:46 +0000778 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000779 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000780
781 switch (md.error_code) {
782 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000783 LOG(ALERT) << "UHD: Receive timed out";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000784 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
785 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
786 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
787 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
788 default:
789 return ERROR_UNHANDLED;
790 }
791 }
792
kurtis.heimerl965e7572011-11-26 03:16:54 +0000793 // Missing timestamp
794 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000795 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000796 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000797 }
798
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000799 ts = md.time_spec;
800
kurtis.heimerl965e7572011-11-26 03:16:54 +0000801 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000802 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000803 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000804 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
805 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000806 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000807 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000808 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000809 }
810
811 return 0;
812}
813
Thomas Tsou204a9f12013-10-29 18:34:16 -0400814int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
815 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000816{
817 ssize_t rc;
818 uhd::time_spec_t ts;
819 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000820
Thomas Tsou204a9f12013-10-29 18:34:16 -0400821 if (bufs.size() != chans) {
822 LOG(ALERT) << "Invalid channel combination " << bufs.size();
823 return -1;
824 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000825
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000826 *overrun = false;
827 *underrun = false;
828
kurtis.heimerl965e7572011-11-26 03:16:54 +0000829 // Shift read time with respect to transmit clock
830 timestamp += ts_offset;
831
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400832 ts = convert_time(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000833 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000834
835 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400836 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000837 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400838 LOG(ERR) << rx_buffers[0]->str_code(rc);
839 LOG(ERR) << rx_buffers[0]->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000840 return 0;
841 }
842
Thomas Tsou204a9f12013-10-29 18:34:16 -0400843 // Create vector buffer
844 std::vector<std::vector<short> >
845 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
846
847 std::vector<short *> pkt_ptrs;
848 for (size_t i = 0; i < pkt_bufs.size(); i++)
849 pkt_ptrs.push_back(&pkt_bufs[i].front());
850
kurtis.heimerl965e7572011-11-26 03:16:54 +0000851 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400852 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
853 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
854 metadata, 0.1, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000855 rx_pkt_cnt++;
856
kurtis.heimerld4be0742011-11-26 03:17:46 +0000857 // Check for errors
858 rc = check_rx_md_err(metadata, num_smpls);
859 switch (rc) {
860 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000861 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
862 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000863 exit(-1);
864 case ERROR_TIMING:
865 restart(prev_ts);
866 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000867 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000868 }
869
kurtis.heimerl965e7572011-11-26 03:16:54 +0000870 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000871 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000872
Thomas Tsou204a9f12013-10-29 18:34:16 -0400873 for (size_t i = 0; i < rx_buffers.size(); i++) {
874 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
875 num_smpls,
876 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000877
Thomas Tsou204a9f12013-10-29 18:34:16 -0400878 // Continue on local overrun, exit on other errors
879 if ((rc < 0)) {
880 LOG(ERR) << rx_buffers[i]->str_code(rc);
881 LOG(ERR) << rx_buffers[i]->str_status();
882 if (rc != smpl_buf::ERROR_OVERFLOW)
883 return 0;
884 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000885 }
886 }
887
888 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400889 for (size_t i = 0; i < rx_buffers.size(); i++) {
890 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
891 if ((rc < 0) || (rc != len)) {
892 LOG(ERR) << rx_buffers[i]->str_code(rc);
893 LOG(ERR) << rx_buffers[i]->str_status();
894 return 0;
895 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000896 }
897
898 return len;
899}
900
Thomas Tsou204a9f12013-10-29 18:34:16 -0400901int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
902 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000903{
904 uhd::tx_metadata_t metadata;
905 metadata.has_time_spec = true;
906 metadata.start_of_burst = false;
907 metadata.end_of_burst = false;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400908 metadata.time_spec = convert_time(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000909
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000910 *underrun = false;
911
kurtis.heimerl965e7572011-11-26 03:16:54 +0000912 // No control packets
913 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000914 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000915 return 0;
916 }
917
Thomas Tsou204a9f12013-10-29 18:34:16 -0400918 if (bufs.size() != chans) {
919 LOG(ALERT) << "Invalid channel combination " << bufs.size();
920 return -1;
921 }
922
kurtis.heimerl965e7572011-11-26 03:16:54 +0000923 // Drop a fixed number of packets (magic value)
924 if (!aligned) {
925 drop_cnt++;
926
927 if (drop_cnt == 1) {
928 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000929 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000930 metadata.end_of_burst = true;
931 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000932 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000933 return len;
934 } else {
935 LOG(DEBUG) << "Aligning transmitter: start burst";
936 metadata.start_of_burst = true;
937 aligned = true;
938 drop_cnt = 0;
939 }
940 }
941
Thomas Tsou204a9f12013-10-29 18:34:16 -0400942 size_t num_smpls = tx_stream->send(bufs, len, metadata);
ttsoub371ed52012-01-09 18:11:34 +0000943 if (num_smpls != (unsigned) len) {
944 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +0000945 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000946
947 return num_smpls;
948}
949
950bool uhd_device::updateAlignment(TIMESTAMP timestamp)
951{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000952 return true;
953}
954
Thomas Tsou204a9f12013-10-29 18:34:16 -0400955bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000956{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400957 if (chan >= tx_freqs.size()) {
958 LOG(ALERT) << "Requested non-existent channel " << chan;
959 return false;
960 }
961
962 uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq, chan);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000963 LOG(INFO) << "\n" << tr.to_pp_string();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400964 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000965
kurtis.heimerl965e7572011-11-26 03:16:54 +0000966 return true;
967}
968
Thomas Tsou204a9f12013-10-29 18:34:16 -0400969bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000970{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400971 if (chan >= rx_freqs.size()) {
972 LOG(ALERT) << "Requested non-existent channel " << chan;
973 return false;
974 }
975
976 uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq, chan);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000977 LOG(INFO) << "\n" << tr.to_pp_string();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400978 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000979
kurtis.heimerl965e7572011-11-26 03:16:54 +0000980 return true;
981}
982
Thomas Tsou204a9f12013-10-29 18:34:16 -0400983double uhd_device::getTxFreq(size_t chan)
984{
985 if (chan >= tx_freqs.size()) {
986 LOG(ALERT) << "Requested non-existent channel " << chan;
987 return 0.0;
988 }
989
990 return tx_freqs[chan];
991}
992
993double uhd_device::getRxFreq(size_t chan)
994{
995 if (chan >= rx_freqs.size()) {
996 LOG(ALERT) << "Requested non-existent channel " << chan;
997 return 0.0;
998 }
999
1000 return rx_freqs[chan];
1001}
1002
kurtis.heimerl965e7572011-11-26 03:16:54 +00001003bool uhd_device::recv_async_msg()
1004{
ttsou60dc4c92012-08-08 23:30:23 +00001005 uhd::async_metadata_t md;
1006 if (!usrp_dev->get_device()->recv_async_msg(md))
kurtis.heimerl965e7572011-11-26 03:16:54 +00001007 return false;
1008
1009 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001010 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001011 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001012
1013 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1014 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1015 LOG(ERR) << str_code(md);
1016 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001017 }
1018
1019 return true;
1020}
1021
1022std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1023{
1024 std::ostringstream ost("UHD: ");
1025
1026 switch (metadata.error_code) {
1027 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1028 ost << "No error";
1029 break;
1030 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1031 ost << "No packet received, implementation timed-out";
1032 break;
1033 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1034 ost << "A stream command was issued in the past";
1035 break;
1036 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1037 ost << "Expected another stream command";
1038 break;
1039 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1040 ost << "An internal receive buffer has filled";
1041 break;
1042 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1043 ost << "The packet could not be parsed";
1044 break;
1045 default:
1046 ost << "Unknown error " << metadata.error_code;
1047 }
1048
1049 if (metadata.has_time_spec)
1050 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1051
1052 return ost.str();
1053}
1054
1055std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1056{
1057 std::ostringstream ost("UHD: ");
1058
1059 switch (metadata.event_code) {
1060 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1061 ost << "A packet was successfully transmitted";
1062 break;
1063 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1064 ost << "An internal send buffer has emptied";
1065 break;
1066 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1067 ost << "Packet loss between host and device";
1068 break;
1069 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1070 ost << "Packet time was too late or too early";
1071 break;
1072 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1073 ost << "Underflow occurred inside a packet";
1074 break;
1075 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1076 ost << "Packet loss within a burst";
1077 break;
1078 default:
1079 ost << "Unknown error " << metadata.event_code;
1080 }
1081
1082 if (metadata.has_time_spec)
1083 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1084
1085 return ost.str();
1086}
1087
1088smpl_buf::smpl_buf(size_t len, double rate)
1089 : buf_len(len), clk_rt(rate),
1090 time_start(0), time_end(0), data_start(0), data_end(0)
1091{
1092 data = new uint32_t[len];
1093}
1094
1095smpl_buf::~smpl_buf()
1096{
1097 delete[] data;
1098}
1099
1100ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1101{
1102 if (timestamp < time_start)
1103 return ERROR_TIMESTAMP;
1104 else if (timestamp >= time_end)
1105 return 0;
1106 else
1107 return time_end - timestamp;
1108}
1109
1110ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1111{
1112 return avail_smpls(convert_time(timespec, clk_rt));
1113}
1114
1115ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1116{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001117 int type_sz = 2 * sizeof(short);
1118
kurtis.heimerl965e7572011-11-26 03:16:54 +00001119 // Check for valid read
1120 if (timestamp < time_start)
1121 return ERROR_TIMESTAMP;
1122 if (timestamp >= time_end)
1123 return 0;
1124 if (len >= buf_len)
1125 return ERROR_READ;
1126
1127 // How many samples should be copied
1128 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001129 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001130 num_smpls = len;
1131
1132 // Starting index
1133 size_t read_start = data_start + (timestamp - time_start);
1134
1135 // Read it
1136 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001137 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001138 memcpy(buf, data + read_start, numBytes);
1139 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001140 size_t first_cp = (buf_len - read_start) * type_sz;
1141 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001142
1143 memcpy(buf, data + read_start, first_cp);
1144 memcpy((char*) buf + first_cp, data, second_cp);
1145 }
1146
1147 data_start = (read_start + len) % buf_len;
1148 time_start = timestamp + len;
1149
1150 if (time_start > time_end)
1151 return ERROR_READ;
1152 else
1153 return num_smpls;
1154}
1155
1156ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1157{
1158 return read(buf, len, convert_time(ts, clk_rt));
1159}
1160
1161ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1162{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001163 int type_sz = 2 * sizeof(short);
1164
kurtis.heimerl965e7572011-11-26 03:16:54 +00001165 // Check for valid write
1166 if ((len == 0) || (len >= buf_len))
1167 return ERROR_WRITE;
1168 if ((timestamp + len) <= time_end)
1169 return ERROR_TIMESTAMP;
1170
1171 // Starting index
1172 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1173
1174 // Write it
1175 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001176 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001177 memcpy(data + write_start, buf, numBytes);
1178 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001179 size_t first_cp = (buf_len - write_start) * type_sz;
1180 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001181
1182 memcpy(data + write_start, buf, first_cp);
1183 memcpy(data, (char*) buf + first_cp, second_cp);
1184 }
1185
1186 data_end = (write_start + len) % buf_len;
1187 time_end = timestamp + len;
1188
1189 if (((write_start + len) > buf_len) && (data_end > data_start))
1190 return ERROR_OVERFLOW;
1191 else if (time_end <= time_start)
1192 return ERROR_WRITE;
1193 else
1194 return len;
1195}
1196
1197ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1198{
1199 return write(buf, len, convert_time(ts, clk_rt));
1200}
1201
1202std::string smpl_buf::str_status() const
1203{
1204 std::ostringstream ost("Sample buffer: ");
1205
1206 ost << "length = " << buf_len;
1207 ost << ", time_start = " << time_start;
1208 ost << ", time_end = " << time_end;
1209 ost << ", data_start = " << data_start;
1210 ost << ", data_end = " << data_end;
1211
1212 return ost.str();
1213}
1214
1215std::string smpl_buf::str_code(ssize_t code)
1216{
1217 switch (code) {
1218 case ERROR_TIMESTAMP:
1219 return "Sample buffer: Requested timestamp is not valid";
1220 case ERROR_READ:
1221 return "Sample buffer: Read error";
1222 case ERROR_WRITE:
1223 return "Sample buffer: Write error";
1224 case ERROR_OVERFLOW:
1225 return "Sample buffer: Overrun";
1226 default:
1227 return "Sample buffer: Unknown error";
1228 }
1229}
1230
Thomas Tsoue90a42b2013-11-13 23:38:09 -05001231RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001232{
Thomas Tsoue90a42b2013-11-13 23:38:09 -05001233 return new uhd_device(sps, chans, diversity);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001234}