blob: 875fe47b518e366b293fc6226d64972835f83d78 [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 Tsou635f1bf2014-02-13 14:34:10 -050035#define B2XX_CLK_RT 26e6
Thomas Tsoufe269fe2013-10-14 23:56:51 -040036#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 Tsoue7882392014-02-13 14:46:23 -050046 B200,
47 B210,
Thomas Tsouc88d8d52013-08-21 17:55:54 -040048 UMTRX,
Thomas Tsou02d88d12013-04-05 15:36:30 -040049 NUM_USRP_TYPES,
50};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000051
Thomas Tsoue3e88142013-04-05 20:42:41 -040052struct uhd_dev_offset {
53 enum uhd_dev_type type;
54 int sps;
55 double offset;
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050056 const std::string desc;
Thomas Tsoue3e88142013-04-05 20:42:41 -040057};
58
kurtis.heimerl965e7572011-11-26 03:16:54 +000059/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040060 * Tx / Rx sample offset values. In a perfect world, there is no group delay
61 * though analog components, and behaviour through digital filters exactly
62 * matches calculated values. In reality, there are unaccounted factors,
63 * which are captured in these empirically measured (using a loopback test)
64 * timing correction values.
65 *
66 * Notes:
67 * USRP1 with timestamps is not supported by UHD.
68 */
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080069static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050070 { USRP1, 1, 0.0, "USRP1 not supported" },
71 { USRP1, 4, 0.0, "USRP1 not supported"},
72 { USRP2, 1, 1.2184e-4, "N2XX 1 SPS" },
73 { USRP2, 4, 8.0230e-5, "N2XX 4 SPS" },
74 { B100, 1, 1.2104e-4, "B100 1 SPS" },
75 { B100, 4, 7.9307e-5, "B100 4 SPS" },
Thomas Tsoue7882392014-02-13 14:46:23 -050076 { B200, 1, 9.9692e-5, "B200 1 SPS" },
77 { B200, 4, 6.9248e-5, "B200 4 SPS" },
78 { B210, 1, 9.9692e-5, "B210 1 SPS" },
79 { B210, 4, 6.9248e-5, "B210 4 SPS" },
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050080 { UMTRX, 1, 9.9692e-5, "UmTRX 1 SPS" },
81 { UMTRX, 4, 7.3846e-5, "UmTRX 4 SPS" },
Thomas Tsoue3e88142013-04-05 20:42:41 -040082};
kurtis.heimerl965e7572011-11-26 03:16:54 +000083
Thomas Tsoue90a42b2013-11-13 23:38:09 -050084/*
85 * Offset handling for special cases. Currently used for UmTRX dual channel
86 * diversity receiver only.
87 */
88static struct uhd_dev_offset special_offsets[] = {
89 { UMTRX, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
90 { UMTRX, 4, 5.2103e-5, "UmTRX diversity, 4 SPS" },
91};
92
93static double get_dev_offset(enum uhd_dev_type type,
94 int sps, bool diversity = false)
Thomas Tsoue3e88142013-04-05 20:42:41 -040095{
Thomas Tsou2e622ff2013-11-15 18:35:04 -050096 struct uhd_dev_offset *offset;
97
Thomas Tsoue90a42b2013-11-13 23:38:09 -050098 /* Reject USRP1 */
Thomas Tsoue3e88142013-04-05 20:42:41 -040099 if (type == USRP1) {
100 LOG(ERR) << "Invalid device type";
101 return 0.0;
102 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000103
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500104 /* Special cases (e.g. diversity receiver) */
105 if (diversity) {
106 if (type != UMTRX) {
107 LOG(ALERT) << "Diversity on UmTRX only";
108 return 0.0;
109 }
110
111 switch (sps) {
112 case 1:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500113 offset = &special_offsets[0];
114 break;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500115 case 4:
116 default:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500117 offset = &special_offsets[1];
118 }
119 } else {
120 /* Normal operation */
121 switch (sps) {
122 case 1:
123 offset = &uhd_offsets[2 * type + 0];
124 break;
125 case 4:
126 default:
127 offset = &uhd_offsets[2 * type + 1];
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500128 }
129 }
130
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500131 std::cout << "-- Setting " << offset->desc << std::endl;
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000132
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500133 return offset->offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -0400134}
kurtis.heimerlce317332011-11-26 03:18:39 +0000135
Thomas Tsoucb69f082013-04-08 14:18:26 -0400136/*
137 * Select sample rate based on device type and requested samples-per-symbol.
138 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
139 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800140 */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500141static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400142{
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500143 if (diversity && (type == UMTRX)) {
144 return GSMRATE * 4;
145 } else if (diversity) {
146 LOG(ALERT) << "Diversity supported on UmTRX only";
147 return -9999.99;
148 }
149
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800150 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400151 return -9999.99;
152
153 switch (type) {
154 case USRP2:
155 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400156 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400157 return B100_BASE_RT * sps;
Thomas Tsoue7882392014-02-13 14:46:23 -0500158 case B200:
159 case B210:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400160 case UMTRX:
161 return GSMRATE * sps;
162 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400163 break;
164 }
165
166 LOG(ALERT) << "Unknown device type " << type;
167 return -9999.99;
168}
169
kurtis.heimerl965e7572011-11-26 03:16:54 +0000170/** Timestamp conversion
171 @param timestamp a UHD or OpenBTS timestamp
172 @param rate sample rate
173 @return the converted timestamp
174*/
175uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
176{
177 double secs = (double) ticks / rate;
178 return uhd::time_spec_t(secs);
179}
180
181TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
182{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +0000183 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000184 return ts.get_tick_count(rate) + ticks;
185}
186
187/*
188 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
189 or UHD style timestamps. Time conversions are handled
190 internally or accessable through the static convert calls.
191*/
192class smpl_buf {
193public:
194 /** Sample buffer constructor
195 @param len number of 32-bit samples the buffer should hold
196 @param rate sample clockrate
197 @param timestamp
198 */
199 smpl_buf(size_t len, double rate);
200 ~smpl_buf();
201
202 /** Query number of samples available for reading
203 @param timestamp time of first sample
204 @return number of available samples or error
205 */
206 ssize_t avail_smpls(TIMESTAMP timestamp) const;
207 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
208
209 /** Read and write
210 @param buf pointer to buffer
211 @param len number of samples desired to read or write
212 @param timestamp time of first stample
213 @return number of actual samples read or written or error
214 */
215 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
216 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
217 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
218 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
219
220 /** Buffer status string
221 @return a formatted string describing internal buffer state
222 */
223 std::string str_status() const;
224
225 /** Formatted error string
226 @param code an error code
227 @return a formatted error string
228 */
229 static std::string str_code(ssize_t code);
230
231 enum err_code {
232 ERROR_TIMESTAMP = -1,
233 ERROR_READ = -2,
234 ERROR_WRITE = -3,
235 ERROR_OVERFLOW = -4
236 };
237
238private:
239 uint32_t *data;
240 size_t buf_len;
241
242 double clk_rt;
243
244 TIMESTAMP time_start;
245 TIMESTAMP time_end;
246
247 size_t data_start;
248 size_t data_end;
249};
250
251/*
252 uhd_device - UHD implementation of the Device interface. Timestamped samples
253 are sent to and received from the device. An intermediate buffer
254 on the receive side collects and aligns packets of samples.
255 Events and errors such as underruns are reported asynchronously
256 by the device and received in a separate thread.
257*/
258class uhd_device : public RadioDevice {
259public:
Thomas Tsou8e17df72014-03-06 14:16:11 -0500260 uhd_device(size_t sps, size_t chans, bool diversity, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000261 ~uhd_device();
262
Thomas Tsou010fff72013-10-16 00:31:18 -0400263 int open(const std::string &args, bool extref);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000264 bool start();
265 bool stop();
kurtis.heimerl68292102011-11-26 03:17:28 +0000266 void restart(uhd::time_spec_t ts);
Thomas Tsou7553aa92013-11-08 12:50:03 -0500267 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400268 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000269
Thomas Tsou204a9f12013-10-29 18:34:16 -0400270 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000271 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
272
Thomas Tsou204a9f12013-10-29 18:34:16 -0400273 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000274 TIMESTAMP timestamp, bool isControl);
275
276 bool updateAlignment(TIMESTAMP timestamp);
277
Thomas Tsou204a9f12013-10-29 18:34:16 -0400278 bool setTxFreq(double wFreq, size_t chan);
279 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000280
Thomas Tsou18d3b832014-02-13 14:55:23 -0500281 inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
282 inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000283
Thomas Tsoue3e88142013-04-05 20:42:41 -0400284 inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000285 inline double fullScaleOutputValue() { return 32000; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000286
Thomas Tsou204a9f12013-10-29 18:34:16 -0400287 double setRxGain(double db, size_t chan);
288 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000289 double maxRxGain(void) { return rx_gain_max; }
290 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000291
Thomas Tsou204a9f12013-10-29 18:34:16 -0400292 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000293 double maxTxGain(void) { return tx_gain_max; }
294 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000295
Thomas Tsou204a9f12013-10-29 18:34:16 -0400296 double getTxFreq(size_t chan);
297 double getRxFreq(size_t chan);
298 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000299
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400300 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000301 inline double numberRead() { return rx_pkt_cnt; }
302 inline double numberWritten() { return 0; }
303
304 /** Receive and process asynchronous message
305 @return true if message received or false on timeout or error
306 */
307 bool recv_async_msg();
308
kurtis.heimerld4be0742011-11-26 03:17:46 +0000309 enum err_code {
310 ERROR_TIMING = -1,
311 ERROR_UNRECOVERABLE = -2,
312 ERROR_UNHANDLED = -3,
313 };
314
kurtis.heimerl965e7572011-11-26 03:16:54 +0000315private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000316 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400317 uhd::tx_streamer::sptr tx_stream;
318 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400319 enum TxWindowType tx_window;
320 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000321
Thomas Tsou204a9f12013-10-29 18:34:16 -0400322 size_t sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400323 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000324
Thomas Tsou204a9f12013-10-29 18:34:16 -0400325 double tx_gain_min, tx_gain_max;
326 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500327 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000328
Thomas Tsou204a9f12013-10-29 18:34:16 -0400329 std::vector<double> tx_gains, rx_gains;
330 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000331 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000332
333 bool started;
334 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000335
336 size_t rx_pkt_cnt;
337 size_t drop_cnt;
338 uhd::time_spec_t prev_ts;
339
Thomas Tsou18d3b832014-02-13 14:55:23 -0500340 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400341 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000342
kurtis.heimerl02d04052011-11-26 03:17:10 +0000343 void init_gains();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400344 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400345 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000346 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000347 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000348 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000349
kurtis.heimerl965e7572011-11-26 03:16:54 +0000350 std::string str_code(uhd::rx_metadata_t metadata);
351 std::string str_code(uhd::async_metadata_t metadata);
352
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500353 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
354 bool set_freq(double freq, size_t chan, bool tx);
355
kurtis.heimerl965e7572011-11-26 03:16:54 +0000356 Thread async_event_thrd;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500357 bool diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000358};
359
360void *async_event_loop(uhd_device *dev)
361{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500362 dev->setPriority(0.43);
363
kurtis.heimerl965e7572011-11-26 03:16:54 +0000364 while (1) {
365 dev->recv_async_msg();
366 pthread_testcancel();
367 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500368
369 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000370}
371
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000372/*
373 Catch and drop underrun 'U' and overrun 'O' messages from stdout
374 since we already report using the logging facility. Direct
375 everything else appropriately.
376 */
377void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
378{
379 switch (type) {
380 case uhd::msg::status:
381 LOG(INFO) << msg;
382 break;
383 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000384 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000385 break;
386 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000387 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000388 break;
389 case uhd::msg::fastpath:
390 break;
391 }
392}
393
Thomas Tsou8e17df72014-03-06 14:16:11 -0500394uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400395 : tx_gain_min(0.0), tx_gain_max(0.0),
396 rx_gain_min(0.0), rx_gain_max(0.0),
397 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000398 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500399 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000400{
Thomas Tsoue3e88142013-04-05 20:42:41 -0400401 this->sps = sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400402 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500403 this->offset = offset;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500404 this->diversity = diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000405}
406
407uhd_device::~uhd_device()
408{
409 stop();
410
Thomas Tsou204a9f12013-10-29 18:34:16 -0400411 for (size_t i = 0; i < rx_buffers.size(); i++)
412 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000413}
414
kurtis.heimerl24481de2011-11-26 03:17:18 +0000415void uhd_device::init_gains()
416{
417 uhd::gain_range_t range;
418
419 range = usrp_dev->get_tx_gain_range();
420 tx_gain_min = range.start();
421 tx_gain_max = range.stop();
422
423 range = usrp_dev->get_rx_gain_range();
424 rx_gain_min = range.start();
425 rx_gain_max = range.stop();
426
Thomas Tsou204a9f12013-10-29 18:34:16 -0400427 for (size_t i = 0; i < tx_gains.size(); i++) {
428 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2, i);
429 tx_gains[i] = usrp_dev->get_tx_gain(i);
430 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000431
Thomas Tsou204a9f12013-10-29 18:34:16 -0400432 for (size_t i = 0; i < rx_gains.size(); i++) {
433 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2, i);
434 rx_gains[i] = usrp_dev->get_rx_gain(i);
435 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000436
437 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400438
kurtis.heimerl24481de2011-11-26 03:17:18 +0000439}
440
Thomas Tsou02d88d12013-04-05 15:36:30 -0400441int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000442{
Thomas Tsou092f7572013-04-04 17:04:39 -0400443 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000444
Thomas Tsou7e068062013-04-08 19:39:37 -0400445 try {
446 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400447 } catch (const std::exception &ex) {
448 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
449 LOG(ALERT) << ex.what();
450 return -1;
451 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000452
Thomas Tsou092f7572013-04-04 17:04:39 -0400453 actual = usrp_dev->get_master_clock_rate();
454 offset = fabs(clk_rate - actual);
455
456 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000457 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400458 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400459 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400460 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000461 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400462
463 return 0;
464}
465
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400466int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400467{
Thomas Tsou092f7572013-04-04 17:04:39 -0400468 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400469 double tx_offset, rx_offset;
470
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400471 // B2XX is the only device where we set FPGA clocking
Thomas Tsoue7882392014-02-13 14:46:23 -0500472 if ((dev_type == B200) || (dev_type == B210)) {
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400473 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400474 return -1;
475 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000476
477 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400478 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400479 usrp_dev->set_tx_rate(tx_rate);
480 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400481 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400482 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400483 LOG(ALERT) << ex.what();
484 return -1;
485 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400486 this->tx_rate = usrp_dev->get_tx_rate();
487 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000488
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400489 tx_offset = fabs(this->tx_rate - tx_rate);
490 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400491 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000492 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400493 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
494 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400495 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000496 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000497
Thomas Tsou02d88d12013-04-05 15:36:30 -0400498 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000499}
500
Thomas Tsou204a9f12013-10-29 18:34:16 -0400501double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000502{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400503 if (chan >= tx_gains.size()) {
504 LOG(ALERT) << "Requested non-existent channel" << chan;
505 return 0.0f;
506 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000507
Thomas Tsou204a9f12013-10-29 18:34:16 -0400508 usrp_dev->set_tx_gain(db, chan);
509 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000510
Thomas Tsou204a9f12013-10-29 18:34:16 -0400511 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
512
513 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000514}
515
Thomas Tsou204a9f12013-10-29 18:34:16 -0400516double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000517{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400518 if (chan >= rx_gains.size()) {
519 LOG(ALERT) << "Requested non-existent channel " << chan;
520 return 0.0f;
521 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000522
Thomas Tsou204a9f12013-10-29 18:34:16 -0400523 usrp_dev->set_rx_gain(db, chan);
524 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000525
Thomas Tsou204a9f12013-10-29 18:34:16 -0400526 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB";
527
528 return rx_gains[chan];
529}
530
531double uhd_device::getRxGain(size_t chan)
532{
533 if (chan >= rx_gains.size()) {
534 LOG(ALERT) << "Requested non-existent channel " << chan;
535 return 0.0f;
536 }
537
538 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000539}
540
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000541/*
542 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400543 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000544 deal with the transport latency. Reject the USRP1 because UHD doesn't
545 support timestamped samples with it.
546 */
547bool uhd_device::parse_dev_type()
548{
549 std::string mboard_str, dev_str;
550 uhd::property_tree::sptr prop_tree;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400551 size_t usrp1_str, usrp2_str, b100_str, b200_str, b210_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000552
553 prop_tree = usrp_dev->get_device()->get_tree();
554 dev_str = prop_tree->access<std::string>("/name").get();
555 mboard_str = usrp_dev->get_mboard_name();
556
557 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400558 usrp2_str = dev_str.find("USRP2");
559 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400560 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400561 b210_str = mboard_str.find("B210");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400562 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000563
564 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000565 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
566 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400567 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000568 return false;
569 }
570
Thomas Tsoucb69f082013-04-08 14:18:26 -0400571 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400572 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400573 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400574 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500575 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500576 dev_type = B200;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400577 } else if (b210_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500578 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500579 dev_type = B210;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400580 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500581 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400582 dev_type = USRP2;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400583 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500584 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400585 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400586 } else {
Thomas Tsou092f7572013-04-04 17:04:39 -0400587 LOG(ALERT) << "Unknown UHD device type " << dev_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400588 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000589 }
590
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500591 if (tx_window == TX_WINDOW_USRP1) {
592 LOG(INFO) << "Using USRP1 type transmit window for "
593 << dev_str << " " << mboard_str;
594 } else {
595 LOG(INFO) << "Using fixed transmit window for "
596 << dev_str << " " << mboard_str;
597 }
598
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000599 return true;
600}
601
Thomas Tsou010fff72013-10-16 00:31:18 -0400602int uhd_device::open(const std::string &args, bool extref)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000603{
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000604 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000605 uhd::device_addr_t addr(args);
606 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000607 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000608 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400609 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000610 }
611
612 // Use the first found device
613 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000614 try {
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000615 usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
kurtis.heimerle380af32011-11-26 03:18:55 +0000616 } catch(...) {
ttsou3b5c0c12012-02-14 17:58:11 +0000617 LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400618 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000619 }
620
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000621 // Check for a valid device type and set bus type
622 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400623 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000624
Thomas Tsou204a9f12013-10-29 18:34:16 -0400625 // Verify and set channels
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500626 if ((dev_type == B210) && (chans == 2)) {
627 } else if ((dev_type == UMTRX) && (chans == 2)) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400628 uhd::usrp::subdev_spec_t subdev_spec("A:0 B:0");
629 usrp_dev->set_tx_subdev_spec(subdev_spec);
630 usrp_dev->set_rx_subdev_spec(subdev_spec);
631 } else if (chans != 1) {
632 LOG(ALERT) << "Invalid channel combination for device";
633 return -1;
634 }
635
636 tx_freqs.resize(chans);
637 rx_freqs.resize(chans);
638 tx_gains.resize(chans);
639 rx_gains.resize(chans);
640 rx_buffers.resize(chans);
641
Thomas Tsou010fff72013-10-16 00:31:18 -0400642 if (extref)
Thomas Tsou0169b312013-10-29 19:25:15 -0400643 usrp_dev->set_clock_source("external");
Thomas Tsou010fff72013-10-16 00:31:18 -0400644
kurtis.heimerl965e7572011-11-26 03:16:54 +0000645 // Set rates
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500646 double _rx_rate;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400647 double _tx_rate = select_rate(dev_type, sps);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500648 if (diversity)
649 _rx_rate = select_rate(dev_type, 1, true);
650 else
651 _rx_rate = _tx_rate / sps;
652
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500653 if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
654 return -1;
655 if (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
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400658 /* Create TX and RX streamers */
659 uhd::stream_args_t stream_args("sc16");
660 for (size_t i = 0; i < chans; i++)
661 stream_args.channels.push_back(i);
662
663 tx_stream = usrp_dev->get_tx_stream(stream_args);
664 rx_stream = usrp_dev->get_rx_stream(stream_args);
665
666 /* Number of samples per over-the-wire packet */
667 tx_spp = tx_stream->get_max_num_samps();
668 rx_spp = rx_stream->get_max_num_samps();
669
kurtis.heimerl965e7572011-11-26 03:16:54 +0000670 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400671 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400672 for (size_t i = 0; i < rx_buffers.size(); i++)
673 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000674
675 // Set receive chain sample offset
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500676 double offset = get_dev_offset(dev_type, sps, diversity);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400677 if (offset == 0.0) {
678 LOG(ERR) << "Unsupported configuration, no correction applied";
679 ts_offset = 0;
680 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400681 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400682 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000683
kurtis.heimerl02d04052011-11-26 03:17:10 +0000684 // Initialize and shadow gain values
685 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000686
kurtis.heimerl965e7572011-11-26 03:16:54 +0000687 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000688 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000689
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500690 if (diversity)
691 return DIVERSITY;
692
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400693 switch (dev_type) {
694 case B100:
695 return RESAMP_64M;
696 case USRP2:
697 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500698 case B200:
699 case B210:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500700 default:
701 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400702 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400703
704 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000705}
706
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000707bool uhd_device::flush_recv(size_t num_pkts)
708{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000709 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000710 size_t num_smpls;
Thomas Tsou18d3b832014-02-13 14:55:23 -0500711 float timeout = 0.1f;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000712
Thomas Tsou18d3b832014-02-13 14:55:23 -0500713 std::vector<std::vector<short> >
714 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000715
Thomas Tsou18d3b832014-02-13 14:55:23 -0500716 std::vector<short *> pkt_ptrs;
717 for (size_t i = 0; i < pkt_bufs.size(); i++)
718 pkt_ptrs.push_back(&pkt_bufs[i].front());
719
720 ts_initial = 0;
721 while (!ts_initial || (num_pkts-- > 0)) {
722 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400723 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000724 if (!num_smpls) {
725 switch (md.error_code) {
726 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000727 default:
728 continue;
729 }
730 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500731
732 ts_initial = convert_time(md.time_spec, rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000733 }
734
Thomas Tsou18d3b832014-02-13 14:55:23 -0500735 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
736
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000737 return true;
738}
739
Thomas Tsou18d3b832014-02-13 14:55:23 -0500740void uhd_device::restart(uhd::time_spec_t)
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000741{
kurtis.heimerl68292102011-11-26 03:17:28 +0000742 aligned = false;
743
Thomas Tsou204a9f12013-10-29 18:34:16 -0400744 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsou18d3b832014-02-13 14:55:23 -0500745 cmd.stream_now = true;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400746
kurtis.heimerl68292102011-11-26 03:17:28 +0000747 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500748
749 flush_recv(1);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000750}
751
kurtis.heimerl965e7572011-11-26 03:16:54 +0000752bool uhd_device::start()
753{
754 LOG(INFO) << "Starting USRP...";
755
756 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000757 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000758 return false;
759 }
760
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400761 // Register msg handler
762 uhd::msg::register_handler(&uhd_msg_handler);
763
kurtis.heimerl965e7572011-11-26 03:16:54 +0000764 // Start asynchronous event (underrun check) loop
765 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
766
767 // Start streaming
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000768 restart(uhd::time_spec_t(0.0));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000769
kurtis.heimerl965e7572011-11-26 03:16:54 +0000770 // Display usrp time
771 double time_now = usrp_dev->get_time_now().get_real_secs();
772 LOG(INFO) << "The current time is " << time_now << " seconds";
773
774 started = true;
775 return true;
776}
777
778bool uhd_device::stop()
779{
Thomas Tsou2c791102013-11-15 23:00:28 -0500780 if (!started)
781 return false;
782
783 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000784 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
785
786 usrp_dev->issue_stream_cmd(stream_cmd);
787
788 started = false;
789 return true;
790}
791
Thomas Tsou7553aa92013-11-08 12:50:03 -0500792void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000793{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500794 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000795 return;
796}
797
kurtis.heimerld4be0742011-11-26 03:17:46 +0000798int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000799{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000800 uhd::time_spec_t ts;
801
kurtis.heimerld4be0742011-11-26 03:17:46 +0000802 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000803 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000804
805 switch (md.error_code) {
806 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000807 LOG(ALERT) << "UHD: Receive timed out";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000808 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
809 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
810 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
811 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
812 default:
813 return ERROR_UNHANDLED;
814 }
815 }
816
kurtis.heimerl965e7572011-11-26 03:16:54 +0000817 // Missing timestamp
818 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000819 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000820 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000821 }
822
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000823 ts = md.time_spec;
824
kurtis.heimerl965e7572011-11-26 03:16:54 +0000825 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000826 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000827 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000828 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
829 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000830 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000831 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000832 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000833 }
834
835 return 0;
836}
837
Thomas Tsou204a9f12013-10-29 18:34:16 -0400838int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
839 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000840{
841 ssize_t rc;
842 uhd::time_spec_t ts;
843 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000844
Thomas Tsou204a9f12013-10-29 18:34:16 -0400845 if (bufs.size() != chans) {
846 LOG(ALERT) << "Invalid channel combination " << bufs.size();
847 return -1;
848 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000849
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000850 *overrun = false;
851 *underrun = false;
852
kurtis.heimerl965e7572011-11-26 03:16:54 +0000853 // Shift read time with respect to transmit clock
854 timestamp += ts_offset;
855
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400856 ts = convert_time(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000857 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000858
859 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400860 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000861 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400862 LOG(ERR) << rx_buffers[0]->str_code(rc);
863 LOG(ERR) << rx_buffers[0]->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000864 return 0;
865 }
866
Thomas Tsou204a9f12013-10-29 18:34:16 -0400867 // Create vector buffer
868 std::vector<std::vector<short> >
869 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
870
871 std::vector<short *> pkt_ptrs;
872 for (size_t i = 0; i < pkt_bufs.size(); i++)
873 pkt_ptrs.push_back(&pkt_bufs[i].front());
874
kurtis.heimerl965e7572011-11-26 03:16:54 +0000875 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400876 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
877 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
878 metadata, 0.1, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000879 rx_pkt_cnt++;
880
kurtis.heimerld4be0742011-11-26 03:17:46 +0000881 // Check for errors
882 rc = check_rx_md_err(metadata, num_smpls);
883 switch (rc) {
884 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000885 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
886 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000887 exit(-1);
888 case ERROR_TIMING:
889 restart(prev_ts);
890 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000891 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000892 }
893
kurtis.heimerl965e7572011-11-26 03:16:54 +0000894 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000895 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000896
Thomas Tsou204a9f12013-10-29 18:34:16 -0400897 for (size_t i = 0; i < rx_buffers.size(); i++) {
898 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
899 num_smpls,
900 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000901
Thomas Tsou204a9f12013-10-29 18:34:16 -0400902 // Continue on local overrun, exit on other errors
903 if ((rc < 0)) {
904 LOG(ERR) << rx_buffers[i]->str_code(rc);
905 LOG(ERR) << rx_buffers[i]->str_status();
906 if (rc != smpl_buf::ERROR_OVERFLOW)
907 return 0;
908 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000909 }
910 }
911
912 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400913 for (size_t i = 0; i < rx_buffers.size(); i++) {
914 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
915 if ((rc < 0) || (rc != len)) {
916 LOG(ERR) << rx_buffers[i]->str_code(rc);
917 LOG(ERR) << rx_buffers[i]->str_status();
918 return 0;
919 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000920 }
921
922 return len;
923}
924
Thomas Tsou204a9f12013-10-29 18:34:16 -0400925int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
926 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000927{
928 uhd::tx_metadata_t metadata;
929 metadata.has_time_spec = true;
930 metadata.start_of_burst = false;
931 metadata.end_of_burst = false;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400932 metadata.time_spec = convert_time(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000933
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000934 *underrun = false;
935
kurtis.heimerl965e7572011-11-26 03:16:54 +0000936 // No control packets
937 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000938 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000939 return 0;
940 }
941
Thomas Tsou204a9f12013-10-29 18:34:16 -0400942 if (bufs.size() != chans) {
943 LOG(ALERT) << "Invalid channel combination " << bufs.size();
944 return -1;
945 }
946
kurtis.heimerl965e7572011-11-26 03:16:54 +0000947 // Drop a fixed number of packets (magic value)
948 if (!aligned) {
949 drop_cnt++;
950
951 if (drop_cnt == 1) {
952 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000953 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000954 metadata.end_of_burst = true;
955 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000956 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000957 return len;
958 } else {
959 LOG(DEBUG) << "Aligning transmitter: start burst";
960 metadata.start_of_burst = true;
961 aligned = true;
962 drop_cnt = 0;
963 }
964 }
965
Thomas Tsou204a9f12013-10-29 18:34:16 -0400966 size_t num_smpls = tx_stream->send(bufs, len, metadata);
ttsoub371ed52012-01-09 18:11:34 +0000967 if (num_smpls != (unsigned) len) {
968 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +0000969 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000970
971 return num_smpls;
972}
973
974bool uhd_device::updateAlignment(TIMESTAMP timestamp)
975{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000976 return true;
977}
978
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500979uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
980{
981 double rf_spread, rf_freq;
982 std::vector<double> freqs;
983 uhd::tune_request_t treq(freq);
984
Thomas Tsou8e17df72014-03-06 14:16:11 -0500985 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) {
986 if (offset == 0.0)
987 return treq;
988
989 return uhd::tune_request_t(freq, offset);
990 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500991 LOG(ALERT) << chans << " channels unsupported";
992 return treq;
993 }
994
995 if (tx)
996 freqs = tx_freqs;
997 else
998 freqs = rx_freqs;
999
1000 /* Tune directly if other channel isn't tuned */
1001 if (freqs[!chan] < 10.0)
1002 return treq;
1003
1004 /* Find center frequency between channels */
1005 rf_spread = fabs(freqs[!chan] - freq);
1006 if (rf_spread > B2XX_CLK_RT) {
1007 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1008 return treq;
1009 }
1010
1011 rf_freq = (freqs[!chan] + freq) / 2.0f;
1012
1013 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1014 treq.target_freq = freq;
1015 treq.rf_freq = rf_freq;
1016
1017 return treq;
1018}
1019
1020bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1021{
1022 std::vector<double> freqs;
1023 uhd::tune_result_t tres;
1024 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1025
1026 if (tx) {
1027 tres = usrp_dev->set_tx_freq(treq, chan);
1028 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1029 } else {
1030 tres = usrp_dev->set_rx_freq(treq, chan);
1031 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1032 }
1033 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1034
Thomas Tsou8e17df72014-03-06 14:16:11 -05001035 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1036 return true;
1037
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001038 /* Manual RF policy means we intentionally tuned with a baseband
1039 * offset for dual-channel purposes. Now retune the other channel
1040 * with the opposite corresponding frequency offset
1041 */
1042 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1043 if (tx) {
1044 treq = select_freq(tx_freqs[!chan], !chan, true);
1045 tres = usrp_dev->set_tx_freq(treq, !chan);
1046 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1047 } else {
1048 treq = select_freq(rx_freqs[!chan], !chan, false);
1049 tres = usrp_dev->set_rx_freq(treq, !chan);
1050 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1051
1052 }
1053 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1054 }
1055
1056 return true;
1057}
1058
Thomas Tsou204a9f12013-10-29 18:34:16 -04001059bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001060{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001061 if (chan >= tx_freqs.size()) {
1062 LOG(ALERT) << "Requested non-existent channel " << chan;
1063 return false;
1064 }
1065
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001066 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001067}
1068
Thomas Tsou204a9f12013-10-29 18:34:16 -04001069bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001070{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001071 if (chan >= rx_freqs.size()) {
1072 LOG(ALERT) << "Requested non-existent channel " << chan;
1073 return false;
1074 }
1075
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001076 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001077}
1078
Thomas Tsou204a9f12013-10-29 18:34:16 -04001079double uhd_device::getTxFreq(size_t chan)
1080{
1081 if (chan >= tx_freqs.size()) {
1082 LOG(ALERT) << "Requested non-existent channel " << chan;
1083 return 0.0;
1084 }
1085
1086 return tx_freqs[chan];
1087}
1088
1089double uhd_device::getRxFreq(size_t chan)
1090{
1091 if (chan >= rx_freqs.size()) {
1092 LOG(ALERT) << "Requested non-existent channel " << chan;
1093 return 0.0;
1094 }
1095
1096 return rx_freqs[chan];
1097}
1098
kurtis.heimerl965e7572011-11-26 03:16:54 +00001099bool uhd_device::recv_async_msg()
1100{
ttsou60dc4c92012-08-08 23:30:23 +00001101 uhd::async_metadata_t md;
1102 if (!usrp_dev->get_device()->recv_async_msg(md))
kurtis.heimerl965e7572011-11-26 03:16:54 +00001103 return false;
1104
1105 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001106 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001107 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001108
1109 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1110 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1111 LOG(ERR) << str_code(md);
1112 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001113 }
1114
1115 return true;
1116}
1117
1118std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1119{
1120 std::ostringstream ost("UHD: ");
1121
1122 switch (metadata.error_code) {
1123 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1124 ost << "No error";
1125 break;
1126 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1127 ost << "No packet received, implementation timed-out";
1128 break;
1129 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1130 ost << "A stream command was issued in the past";
1131 break;
1132 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1133 ost << "Expected another stream command";
1134 break;
1135 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1136 ost << "An internal receive buffer has filled";
1137 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001138 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1139 ost << "Multi-channel alignment failed";
1140 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001141 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1142 ost << "The packet could not be parsed";
1143 break;
1144 default:
1145 ost << "Unknown error " << metadata.error_code;
1146 }
1147
1148 if (metadata.has_time_spec)
1149 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1150
1151 return ost.str();
1152}
1153
1154std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1155{
1156 std::ostringstream ost("UHD: ");
1157
1158 switch (metadata.event_code) {
1159 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1160 ost << "A packet was successfully transmitted";
1161 break;
1162 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1163 ost << "An internal send buffer has emptied";
1164 break;
1165 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1166 ost << "Packet loss between host and device";
1167 break;
1168 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1169 ost << "Packet time was too late or too early";
1170 break;
1171 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1172 ost << "Underflow occurred inside a packet";
1173 break;
1174 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1175 ost << "Packet loss within a burst";
1176 break;
1177 default:
1178 ost << "Unknown error " << metadata.event_code;
1179 }
1180
1181 if (metadata.has_time_spec)
1182 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1183
1184 return ost.str();
1185}
1186
1187smpl_buf::smpl_buf(size_t len, double rate)
1188 : buf_len(len), clk_rt(rate),
1189 time_start(0), time_end(0), data_start(0), data_end(0)
1190{
1191 data = new uint32_t[len];
1192}
1193
1194smpl_buf::~smpl_buf()
1195{
1196 delete[] data;
1197}
1198
1199ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1200{
1201 if (timestamp < time_start)
1202 return ERROR_TIMESTAMP;
1203 else if (timestamp >= time_end)
1204 return 0;
1205 else
1206 return time_end - timestamp;
1207}
1208
1209ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1210{
1211 return avail_smpls(convert_time(timespec, clk_rt));
1212}
1213
1214ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1215{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001216 int type_sz = 2 * sizeof(short);
1217
kurtis.heimerl965e7572011-11-26 03:16:54 +00001218 // Check for valid read
1219 if (timestamp < time_start)
1220 return ERROR_TIMESTAMP;
1221 if (timestamp >= time_end)
1222 return 0;
1223 if (len >= buf_len)
1224 return ERROR_READ;
1225
1226 // How many samples should be copied
1227 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001228 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001229 num_smpls = len;
1230
1231 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001232 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001233
1234 // Read it
1235 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001236 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001237 memcpy(buf, data + read_start, numBytes);
1238 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001239 size_t first_cp = (buf_len - read_start) * type_sz;
1240 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001241
1242 memcpy(buf, data + read_start, first_cp);
1243 memcpy((char*) buf + first_cp, data, second_cp);
1244 }
1245
1246 data_start = (read_start + len) % buf_len;
1247 time_start = timestamp + len;
1248
1249 if (time_start > time_end)
1250 return ERROR_READ;
1251 else
1252 return num_smpls;
1253}
1254
1255ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1256{
1257 return read(buf, len, convert_time(ts, clk_rt));
1258}
1259
1260ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1261{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001262 int type_sz = 2 * sizeof(short);
1263
kurtis.heimerl965e7572011-11-26 03:16:54 +00001264 // Check for valid write
1265 if ((len == 0) || (len >= buf_len))
1266 return ERROR_WRITE;
1267 if ((timestamp + len) <= time_end)
1268 return ERROR_TIMESTAMP;
1269
1270 // Starting index
1271 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1272
1273 // Write it
1274 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001275 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001276 memcpy(data + write_start, buf, numBytes);
1277 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001278 size_t first_cp = (buf_len - write_start) * type_sz;
1279 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001280
1281 memcpy(data + write_start, buf, first_cp);
1282 memcpy(data, (char*) buf + first_cp, second_cp);
1283 }
1284
1285 data_end = (write_start + len) % buf_len;
1286 time_end = timestamp + len;
1287
Thomas Tsou18d3b832014-02-13 14:55:23 -05001288 if (!data_start)
1289 data_start = write_start;
1290
kurtis.heimerl965e7572011-11-26 03:16:54 +00001291 if (((write_start + len) > buf_len) && (data_end > data_start))
1292 return ERROR_OVERFLOW;
1293 else if (time_end <= time_start)
1294 return ERROR_WRITE;
1295 else
1296 return len;
1297}
1298
1299ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1300{
1301 return write(buf, len, convert_time(ts, clk_rt));
1302}
1303
1304std::string smpl_buf::str_status() const
1305{
1306 std::ostringstream ost("Sample buffer: ");
1307
1308 ost << "length = " << buf_len;
1309 ost << ", time_start = " << time_start;
1310 ost << ", time_end = " << time_end;
1311 ost << ", data_start = " << data_start;
1312 ost << ", data_end = " << data_end;
1313
1314 return ost.str();
1315}
1316
1317std::string smpl_buf::str_code(ssize_t code)
1318{
1319 switch (code) {
1320 case ERROR_TIMESTAMP:
1321 return "Sample buffer: Requested timestamp is not valid";
1322 case ERROR_READ:
1323 return "Sample buffer: Read error";
1324 case ERROR_WRITE:
1325 return "Sample buffer: Write error";
1326 case ERROR_OVERFLOW:
1327 return "Sample buffer: Overrun";
1328 default:
1329 return "Sample buffer: Unknown error";
1330 }
1331}
1332
Thomas Tsou8e17df72014-03-06 14:16:11 -05001333RadioDevice *RadioDevice::make(size_t sps, size_t chans,
1334 bool diversity, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001335{
Thomas Tsou8e17df72014-03-06 14:16:11 -05001336 return new uhd_device(sps, chans, diversity, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001337}