blob: 72cad4db138495e5ffb2c61be5f6e7a93d54989a [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 Tsoua5c83ae2014-04-02 03:31:44 +010036#define E1XX_CLK_RT 52e6
Thomas Tsoufe269fe2013-10-14 23:56:51 -040037#define B2XX_BASE_RT GSMRATE
38#define B100_BASE_RT 400000
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040039#define USRP2_BASE_RT 390625
Thomas Tsoue3e88142013-04-05 20:42:41 -040040#define TX_AMPL 0.3
41#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040042
43enum uhd_dev_type {
44 USRP1,
45 USRP2,
46 B100,
Thomas Tsoue7882392014-02-13 14:46:23 -050047 B200,
48 B210,
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010049 E1XX,
Thomas Tsouc88d8d52013-08-21 17:55:54 -040050 UMTRX,
Thomas Tsou02d88d12013-04-05 15:36:30 -040051 NUM_USRP_TYPES,
52};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000053
Thomas Tsoue3e88142013-04-05 20:42:41 -040054struct uhd_dev_offset {
55 enum uhd_dev_type type;
56 int sps;
57 double offset;
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050058 const std::string desc;
Thomas Tsoue3e88142013-04-05 20:42:41 -040059};
60
kurtis.heimerl965e7572011-11-26 03:16:54 +000061/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040062 * Tx / Rx sample offset values. In a perfect world, there is no group delay
63 * though analog components, and behaviour through digital filters exactly
64 * matches calculated values. In reality, there are unaccounted factors,
65 * which are captured in these empirically measured (using a loopback test)
66 * timing correction values.
67 *
68 * Notes:
69 * USRP1 with timestamps is not supported by UHD.
70 */
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080071static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050072 { USRP1, 1, 0.0, "USRP1 not supported" },
73 { USRP1, 4, 0.0, "USRP1 not supported"},
74 { USRP2, 1, 1.2184e-4, "N2XX 1 SPS" },
75 { USRP2, 4, 8.0230e-5, "N2XX 4 SPS" },
76 { B100, 1, 1.2104e-4, "B100 1 SPS" },
77 { B100, 4, 7.9307e-5, "B100 4 SPS" },
Thomas Tsoue7882392014-02-13 14:46:23 -050078 { B200, 1, 9.9692e-5, "B200 1 SPS" },
79 { B200, 4, 6.9248e-5, "B200 4 SPS" },
80 { B210, 1, 9.9692e-5, "B210 1 SPS" },
81 { B210, 4, 6.9248e-5, "B210 4 SPS" },
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010082 { E1XX, 1, 9.5192e-5, "E1XX 1 SPS" },
83 { E1XX, 4, 6.5571e-5, "E1XX 4 SPS" },
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050084 { UMTRX, 1, 9.9692e-5, "UmTRX 1 SPS" },
85 { UMTRX, 4, 7.3846e-5, "UmTRX 4 SPS" },
Thomas Tsoue3e88142013-04-05 20:42:41 -040086};
kurtis.heimerl965e7572011-11-26 03:16:54 +000087
Thomas Tsoue90a42b2013-11-13 23:38:09 -050088/*
89 * Offset handling for special cases. Currently used for UmTRX dual channel
90 * diversity receiver only.
91 */
92static struct uhd_dev_offset special_offsets[] = {
93 { UMTRX, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
94 { UMTRX, 4, 5.2103e-5, "UmTRX diversity, 4 SPS" },
95};
96
97static double get_dev_offset(enum uhd_dev_type type,
98 int sps, bool diversity = false)
Thomas Tsoue3e88142013-04-05 20:42:41 -040099{
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500100 struct uhd_dev_offset *offset;
101
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500102 /* Reject USRP1 */
Thomas Tsoue3e88142013-04-05 20:42:41 -0400103 if (type == USRP1) {
104 LOG(ERR) << "Invalid device type";
105 return 0.0;
106 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000107
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500108 /* Special cases (e.g. diversity receiver) */
109 if (diversity) {
110 if (type != UMTRX) {
111 LOG(ALERT) << "Diversity on UmTRX only";
112 return 0.0;
113 }
114
115 switch (sps) {
116 case 1:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500117 offset = &special_offsets[0];
118 break;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500119 case 4:
120 default:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500121 offset = &special_offsets[1];
122 }
123 } else {
124 /* Normal operation */
125 switch (sps) {
126 case 1:
127 offset = &uhd_offsets[2 * type + 0];
128 break;
129 case 4:
130 default:
131 offset = &uhd_offsets[2 * type + 1];
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500132 }
133 }
134
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500135 std::cout << "-- Setting " << offset->desc << std::endl;
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000136
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500137 return offset->offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -0400138}
kurtis.heimerlce317332011-11-26 03:18:39 +0000139
Thomas Tsoucb69f082013-04-08 14:18:26 -0400140/*
141 * Select sample rate based on device type and requested samples-per-symbol.
142 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
143 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800144 */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500145static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400146{
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500147 if (diversity && (type == UMTRX)) {
148 return GSMRATE * 4;
149 } else if (diversity) {
150 LOG(ALERT) << "Diversity supported on UmTRX only";
151 return -9999.99;
152 }
153
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800154 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400155 return -9999.99;
156
157 switch (type) {
158 case USRP2:
159 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400160 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400161 return B100_BASE_RT * sps;
Thomas Tsoue7882392014-02-13 14:46:23 -0500162 case B200:
163 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100164 case E1XX:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400165 case UMTRX:
166 return GSMRATE * sps;
167 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400168 break;
169 }
170
171 LOG(ALERT) << "Unknown device type " << type;
172 return -9999.99;
173}
174
kurtis.heimerl965e7572011-11-26 03:16:54 +0000175/** Timestamp conversion
176 @param timestamp a UHD or OpenBTS timestamp
177 @param rate sample rate
178 @return the converted timestamp
179*/
180uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
181{
182 double secs = (double) ticks / rate;
183 return uhd::time_spec_t(secs);
184}
185
186TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
187{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +0000188 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000189 return ts.get_tick_count(rate) + ticks;
190}
191
192/*
193 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
194 or UHD style timestamps. Time conversions are handled
195 internally or accessable through the static convert calls.
196*/
197class smpl_buf {
198public:
199 /** Sample buffer constructor
200 @param len number of 32-bit samples the buffer should hold
201 @param rate sample clockrate
202 @param timestamp
203 */
204 smpl_buf(size_t len, double rate);
205 ~smpl_buf();
206
207 /** Query number of samples available for reading
208 @param timestamp time of first sample
209 @return number of available samples or error
210 */
211 ssize_t avail_smpls(TIMESTAMP timestamp) const;
212 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
213
214 /** Read and write
215 @param buf pointer to buffer
216 @param len number of samples desired to read or write
217 @param timestamp time of first stample
218 @return number of actual samples read or written or error
219 */
220 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
221 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
222 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
223 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
224
225 /** Buffer status string
226 @return a formatted string describing internal buffer state
227 */
228 std::string str_status() const;
229
230 /** Formatted error string
231 @param code an error code
232 @return a formatted error string
233 */
234 static std::string str_code(ssize_t code);
235
236 enum err_code {
237 ERROR_TIMESTAMP = -1,
238 ERROR_READ = -2,
239 ERROR_WRITE = -3,
240 ERROR_OVERFLOW = -4
241 };
242
243private:
244 uint32_t *data;
245 size_t buf_len;
246
247 double clk_rt;
248
249 TIMESTAMP time_start;
250 TIMESTAMP time_end;
251
252 size_t data_start;
253 size_t data_end;
254};
255
256/*
257 uhd_device - UHD implementation of the Device interface. Timestamped samples
258 are sent to and received from the device. An intermediate buffer
259 on the receive side collects and aligns packets of samples.
260 Events and errors such as underruns are reported asynchronously
261 by the device and received in a separate thread.
262*/
263class uhd_device : public RadioDevice {
264public:
Thomas Tsou8e17df72014-03-06 14:16:11 -0500265 uhd_device(size_t sps, size_t chans, bool diversity, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000266 ~uhd_device();
267
Thomas Tsou010fff72013-10-16 00:31:18 -0400268 int open(const std::string &args, bool extref);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000269 bool start();
270 bool stop();
Thomas Tsouc2839162014-03-27 13:52:58 -0400271 void restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500272 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400273 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000274
Thomas Tsou204a9f12013-10-29 18:34:16 -0400275 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000276 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
277
Thomas Tsou204a9f12013-10-29 18:34:16 -0400278 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000279 TIMESTAMP timestamp, bool isControl);
280
281 bool updateAlignment(TIMESTAMP timestamp);
282
Thomas Tsou204a9f12013-10-29 18:34:16 -0400283 bool setTxFreq(double wFreq, size_t chan);
284 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000285
Thomas Tsou18d3b832014-02-13 14:55:23 -0500286 inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
287 inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000288
Thomas Tsoue3e88142013-04-05 20:42:41 -0400289 inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000290 inline double fullScaleOutputValue() { return 32000; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000291
Thomas Tsou204a9f12013-10-29 18:34:16 -0400292 double setRxGain(double db, size_t chan);
293 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000294 double maxRxGain(void) { return rx_gain_max; }
295 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000296
Thomas Tsou204a9f12013-10-29 18:34:16 -0400297 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000298 double maxTxGain(void) { return tx_gain_max; }
299 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000300
Thomas Tsou204a9f12013-10-29 18:34:16 -0400301 double getTxFreq(size_t chan);
302 double getRxFreq(size_t chan);
303 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000304
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400305 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000306 inline double numberRead() { return rx_pkt_cnt; }
307 inline double numberWritten() { return 0; }
308
309 /** Receive and process asynchronous message
310 @return true if message received or false on timeout or error
311 */
312 bool recv_async_msg();
313
kurtis.heimerld4be0742011-11-26 03:17:46 +0000314 enum err_code {
315 ERROR_TIMING = -1,
316 ERROR_UNRECOVERABLE = -2,
317 ERROR_UNHANDLED = -3,
318 };
319
kurtis.heimerl965e7572011-11-26 03:16:54 +0000320private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000321 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400322 uhd::tx_streamer::sptr tx_stream;
323 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400324 enum TxWindowType tx_window;
325 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000326
Thomas Tsou204a9f12013-10-29 18:34:16 -0400327 size_t sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400328 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000329
Thomas Tsou204a9f12013-10-29 18:34:16 -0400330 double tx_gain_min, tx_gain_max;
331 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500332 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000333
Thomas Tsou204a9f12013-10-29 18:34:16 -0400334 std::vector<double> tx_gains, rx_gains;
335 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000336 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000337
338 bool started;
339 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000340
341 size_t rx_pkt_cnt;
342 size_t drop_cnt;
343 uhd::time_spec_t prev_ts;
344
Thomas Tsou18d3b832014-02-13 14:55:23 -0500345 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400346 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000347
kurtis.heimerl02d04052011-11-26 03:17:10 +0000348 void init_gains();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400349 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400350 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000351 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000352 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000353 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000354
kurtis.heimerl965e7572011-11-26 03:16:54 +0000355 std::string str_code(uhd::rx_metadata_t metadata);
356 std::string str_code(uhd::async_metadata_t metadata);
357
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500358 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
359 bool set_freq(double freq, size_t chan, bool tx);
360
kurtis.heimerl965e7572011-11-26 03:16:54 +0000361 Thread async_event_thrd;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500362 bool diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000363};
364
365void *async_event_loop(uhd_device *dev)
366{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500367 dev->setPriority(0.43);
368
kurtis.heimerl965e7572011-11-26 03:16:54 +0000369 while (1) {
370 dev->recv_async_msg();
371 pthread_testcancel();
372 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500373
374 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000375}
376
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000377/*
378 Catch and drop underrun 'U' and overrun 'O' messages from stdout
379 since we already report using the logging facility. Direct
380 everything else appropriately.
381 */
382void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
383{
384 switch (type) {
385 case uhd::msg::status:
386 LOG(INFO) << msg;
387 break;
388 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000389 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000390 break;
391 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000392 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000393 break;
394 case uhd::msg::fastpath:
395 break;
396 }
397}
398
Thomas Tsou8e17df72014-03-06 14:16:11 -0500399uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400400 : tx_gain_min(0.0), tx_gain_max(0.0),
401 rx_gain_min(0.0), rx_gain_max(0.0),
402 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000403 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500404 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000405{
Thomas Tsoue3e88142013-04-05 20:42:41 -0400406 this->sps = sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400407 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500408 this->offset = offset;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500409 this->diversity = diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000410}
411
412uhd_device::~uhd_device()
413{
414 stop();
415
Thomas Tsou204a9f12013-10-29 18:34:16 -0400416 for (size_t i = 0; i < rx_buffers.size(); i++)
417 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000418}
419
kurtis.heimerl24481de2011-11-26 03:17:18 +0000420void uhd_device::init_gains()
421{
422 uhd::gain_range_t range;
423
424 range = usrp_dev->get_tx_gain_range();
425 tx_gain_min = range.start();
426 tx_gain_max = range.stop();
427
428 range = usrp_dev->get_rx_gain_range();
429 rx_gain_min = range.start();
430 rx_gain_max = range.stop();
431
Thomas Tsou204a9f12013-10-29 18:34:16 -0400432 for (size_t i = 0; i < tx_gains.size(); i++) {
433 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2, i);
434 tx_gains[i] = usrp_dev->get_tx_gain(i);
435 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000436
Thomas Tsou204a9f12013-10-29 18:34:16 -0400437 for (size_t i = 0; i < rx_gains.size(); i++) {
438 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2, i);
439 rx_gains[i] = usrp_dev->get_rx_gain(i);
440 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000441
442 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400443
kurtis.heimerl24481de2011-11-26 03:17:18 +0000444}
445
Thomas Tsou02d88d12013-04-05 15:36:30 -0400446int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000447{
Thomas Tsou092f7572013-04-04 17:04:39 -0400448 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000449
Thomas Tsou7e068062013-04-08 19:39:37 -0400450 try {
451 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400452 } catch (const std::exception &ex) {
453 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
454 LOG(ALERT) << ex.what();
455 return -1;
456 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000457
Thomas Tsou092f7572013-04-04 17:04:39 -0400458 actual = usrp_dev->get_master_clock_rate();
459 offset = fabs(clk_rate - actual);
460
461 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000462 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400463 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400464 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400465 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000466 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400467
468 return 0;
469}
470
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400471int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400472{
Thomas Tsou092f7572013-04-04 17:04:39 -0400473 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400474 double tx_offset, rx_offset;
475
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100476 /* B2XX and E1xx are the only device where we set FPGA clocking */
Thomas Tsoue7882392014-02-13 14:46:23 -0500477 if ((dev_type == B200) || (dev_type == B210)) {
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400478 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400479 return -1;
480 }
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100481 else if (dev_type == E1XX) {
482 if (set_master_clk(E1XX_CLK_RT) < 0)
483 return -1;
484 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000485
486 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400487 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400488 usrp_dev->set_tx_rate(tx_rate);
489 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400490 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400491 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400492 LOG(ALERT) << ex.what();
493 return -1;
494 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400495 this->tx_rate = usrp_dev->get_tx_rate();
496 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000497
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400498 tx_offset = fabs(this->tx_rate - tx_rate);
499 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400500 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000501 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400502 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
503 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400504 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000505 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000506
Thomas Tsou02d88d12013-04-05 15:36:30 -0400507 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000508}
509
Thomas Tsou204a9f12013-10-29 18:34:16 -0400510double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000511{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400512 if (chan >= tx_gains.size()) {
513 LOG(ALERT) << "Requested non-existent channel" << chan;
514 return 0.0f;
515 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000516
Thomas Tsou204a9f12013-10-29 18:34:16 -0400517 usrp_dev->set_tx_gain(db, chan);
518 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000519
Thomas Tsou204a9f12013-10-29 18:34:16 -0400520 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
521
522 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000523}
524
Thomas Tsou204a9f12013-10-29 18:34:16 -0400525double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000526{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400527 if (chan >= rx_gains.size()) {
528 LOG(ALERT) << "Requested non-existent channel " << chan;
529 return 0.0f;
530 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000531
Thomas Tsou204a9f12013-10-29 18:34:16 -0400532 usrp_dev->set_rx_gain(db, chan);
533 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000534
Thomas Tsou204a9f12013-10-29 18:34:16 -0400535 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB";
536
537 return rx_gains[chan];
538}
539
540double uhd_device::getRxGain(size_t chan)
541{
542 if (chan >= rx_gains.size()) {
543 LOG(ALERT) << "Requested non-existent channel " << chan;
544 return 0.0f;
545 }
546
547 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000548}
549
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000550/*
551 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400552 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000553 deal with the transport latency. Reject the USRP1 because UHD doesn't
554 support timestamped samples with it.
555 */
556bool uhd_device::parse_dev_type()
557{
558 std::string mboard_str, dev_str;
559 uhd::property_tree::sptr prop_tree;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100560 size_t usrp1_str, usrp2_str, e100_str, e110_str,
561 b100_str, b200_str, b210_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000562
563 prop_tree = usrp_dev->get_device()->get_tree();
564 dev_str = prop_tree->access<std::string>("/name").get();
565 mboard_str = usrp_dev->get_mboard_name();
566
567 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400568 usrp2_str = dev_str.find("USRP2");
569 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400570 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400571 b210_str = mboard_str.find("B210");
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100572 e100_str = mboard_str.find("E100");
573 e110_str = mboard_str.find("E110");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400574 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000575
576 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000577 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
578 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400579 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000580 return false;
581 }
582
Thomas Tsoucb69f082013-04-08 14:18:26 -0400583 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400584 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400585 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400586 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500587 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500588 dev_type = B200;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400589 } else if (b210_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500590 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500591 dev_type = B210;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100592 } else if (e100_str != std::string::npos) {
593 tx_window = TX_WINDOW_FIXED;
594 dev_type = E1XX;
595 } else if (e110_str != std::string::npos) {
596 tx_window = TX_WINDOW_FIXED;
597 dev_type = E1XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400598 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500599 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400600 dev_type = USRP2;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400601 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500602 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400603 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400604 } else {
Thomas Tsou092f7572013-04-04 17:04:39 -0400605 LOG(ALERT) << "Unknown UHD device type " << dev_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400606 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000607 }
608
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500609 if (tx_window == TX_WINDOW_USRP1) {
610 LOG(INFO) << "Using USRP1 type transmit window for "
611 << dev_str << " " << mboard_str;
612 } else {
613 LOG(INFO) << "Using fixed transmit window for "
614 << dev_str << " " << mboard_str;
615 }
616
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000617 return true;
618}
619
Thomas Tsou010fff72013-10-16 00:31:18 -0400620int uhd_device::open(const std::string &args, bool extref)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000621{
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000622 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000623 uhd::device_addr_t addr(args);
624 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000625 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000626 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400627 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000628 }
629
630 // Use the first found device
631 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000632 try {
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000633 usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
kurtis.heimerle380af32011-11-26 03:18:55 +0000634 } catch(...) {
ttsou3b5c0c12012-02-14 17:58:11 +0000635 LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400636 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000637 }
638
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000639 // Check for a valid device type and set bus type
640 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400641 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000642
Thomas Tsou204a9f12013-10-29 18:34:16 -0400643 // Verify and set channels
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500644 if ((dev_type == B210) && (chans == 2)) {
645 } else if ((dev_type == UMTRX) && (chans == 2)) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400646 uhd::usrp::subdev_spec_t subdev_spec("A:0 B:0");
647 usrp_dev->set_tx_subdev_spec(subdev_spec);
648 usrp_dev->set_rx_subdev_spec(subdev_spec);
649 } else if (chans != 1) {
650 LOG(ALERT) << "Invalid channel combination for device";
651 return -1;
652 }
653
654 tx_freqs.resize(chans);
655 rx_freqs.resize(chans);
656 tx_gains.resize(chans);
657 rx_gains.resize(chans);
658 rx_buffers.resize(chans);
659
Thomas Tsou010fff72013-10-16 00:31:18 -0400660 if (extref)
Thomas Tsou0169b312013-10-29 19:25:15 -0400661 usrp_dev->set_clock_source("external");
Thomas Tsou010fff72013-10-16 00:31:18 -0400662
kurtis.heimerl965e7572011-11-26 03:16:54 +0000663 // Set rates
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500664 double _rx_rate;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400665 double _tx_rate = select_rate(dev_type, sps);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500666 if (diversity)
667 _rx_rate = select_rate(dev_type, 1, true);
668 else
669 _rx_rate = _tx_rate / sps;
670
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500671 if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
672 return -1;
673 if (set_rates(_tx_rate, _rx_rate) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400674 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000675
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400676 /* Create TX and RX streamers */
677 uhd::stream_args_t stream_args("sc16");
678 for (size_t i = 0; i < chans; i++)
679 stream_args.channels.push_back(i);
680
681 tx_stream = usrp_dev->get_tx_stream(stream_args);
682 rx_stream = usrp_dev->get_rx_stream(stream_args);
683
684 /* Number of samples per over-the-wire packet */
685 tx_spp = tx_stream->get_max_num_samps();
686 rx_spp = rx_stream->get_max_num_samps();
687
kurtis.heimerl965e7572011-11-26 03:16:54 +0000688 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400689 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400690 for (size_t i = 0; i < rx_buffers.size(); i++)
691 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000692
693 // Set receive chain sample offset
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500694 double offset = get_dev_offset(dev_type, sps, diversity);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400695 if (offset == 0.0) {
696 LOG(ERR) << "Unsupported configuration, no correction applied";
697 ts_offset = 0;
698 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400699 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400700 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000701
kurtis.heimerl02d04052011-11-26 03:17:10 +0000702 // Initialize and shadow gain values
703 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000704
kurtis.heimerl965e7572011-11-26 03:16:54 +0000705 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000706 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000707
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500708 if (diversity)
709 return DIVERSITY;
710
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400711 switch (dev_type) {
712 case B100:
713 return RESAMP_64M;
714 case USRP2:
715 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500716 case B200:
717 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100718 case E1XX:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500719 default:
720 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400721 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400722
723 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000724}
725
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000726bool uhd_device::flush_recv(size_t num_pkts)
727{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000728 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000729 size_t num_smpls;
Thomas Tsou18d3b832014-02-13 14:55:23 -0500730 float timeout = 0.1f;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000731
Thomas Tsou18d3b832014-02-13 14:55:23 -0500732 std::vector<std::vector<short> >
733 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000734
Thomas Tsou18d3b832014-02-13 14:55:23 -0500735 std::vector<short *> pkt_ptrs;
736 for (size_t i = 0; i < pkt_bufs.size(); i++)
737 pkt_ptrs.push_back(&pkt_bufs[i].front());
738
739 ts_initial = 0;
740 while (!ts_initial || (num_pkts-- > 0)) {
741 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400742 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000743 if (!num_smpls) {
744 switch (md.error_code) {
745 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000746 default:
747 continue;
748 }
749 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500750
751 ts_initial = convert_time(md.time_spec, rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000752 }
753
Thomas Tsou18d3b832014-02-13 14:55:23 -0500754 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
755
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000756 return true;
757}
758
Thomas Tsouc2839162014-03-27 13:52:58 -0400759void uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000760{
Thomas Tsouc2839162014-03-27 13:52:58 -0400761 /* Allow 100 ms delay to align multi-channel streams */
762 double delay = 0.1;
763
kurtis.heimerl68292102011-11-26 03:17:28 +0000764 aligned = false;
765
Thomas Tsouc2839162014-03-27 13:52:58 -0400766 uhd::time_spec_t current = usrp_dev->get_time_now();
767
Thomas Tsou204a9f12013-10-29 18:34:16 -0400768 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400769 cmd.stream_now = false;
770 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400771
kurtis.heimerl68292102011-11-26 03:17:28 +0000772 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500773
774 flush_recv(1);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000775}
776
kurtis.heimerl965e7572011-11-26 03:16:54 +0000777bool uhd_device::start()
778{
779 LOG(INFO) << "Starting USRP...";
780
781 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000782 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000783 return false;
784 }
785
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400786 // Register msg handler
787 uhd::msg::register_handler(&uhd_msg_handler);
788
kurtis.heimerl965e7572011-11-26 03:16:54 +0000789 // Start asynchronous event (underrun check) loop
790 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
791
792 // Start streaming
Thomas Tsouc2839162014-03-27 13:52:58 -0400793 restart();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000794
kurtis.heimerl965e7572011-11-26 03:16:54 +0000795 // Display usrp time
796 double time_now = usrp_dev->get_time_now().get_real_secs();
797 LOG(INFO) << "The current time is " << time_now << " seconds";
798
799 started = true;
800 return true;
801}
802
803bool uhd_device::stop()
804{
Thomas Tsou2c791102013-11-15 23:00:28 -0500805 if (!started)
806 return false;
807
808 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000809 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
810
811 usrp_dev->issue_stream_cmd(stream_cmd);
812
813 started = false;
814 return true;
815}
816
Thomas Tsou7553aa92013-11-08 12:50:03 -0500817void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000818{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500819 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000820 return;
821}
822
kurtis.heimerld4be0742011-11-26 03:17:46 +0000823int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000824{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000825 uhd::time_spec_t ts;
Ivan Kluchnikov36058722014-06-30 20:36:51 +0400826 static int err_count = 0;
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000827
kurtis.heimerld4be0742011-11-26 03:17:46 +0000828 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000829 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000830
831 switch (md.error_code) {
832 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000833 LOG(ALERT) << "UHD: Receive timed out";
Ivan Kluchnikov36058722014-06-30 20:36:51 +0400834 if (err_count > 100) {
835 err_count = 0;
836 return ERROR_UNRECOVERABLE;
837 }
838 err_count++;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000839 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
840 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
841 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
842 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
843 default:
844 return ERROR_UNHANDLED;
845 }
846 }
847
kurtis.heimerl965e7572011-11-26 03:16:54 +0000848 // Missing timestamp
849 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000850 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000851 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000852 }
853
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000854 ts = md.time_spec;
855
kurtis.heimerl965e7572011-11-26 03:16:54 +0000856 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000857 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000858 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000859 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
860 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000861 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000862 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000863 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000864 }
865
866 return 0;
867}
868
Thomas Tsou204a9f12013-10-29 18:34:16 -0400869int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
870 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000871{
872 ssize_t rc;
873 uhd::time_spec_t ts;
874 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000875
Thomas Tsou204a9f12013-10-29 18:34:16 -0400876 if (bufs.size() != chans) {
877 LOG(ALERT) << "Invalid channel combination " << bufs.size();
878 return -1;
879 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000880
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000881 *overrun = false;
882 *underrun = false;
883
kurtis.heimerl965e7572011-11-26 03:16:54 +0000884 // Shift read time with respect to transmit clock
885 timestamp += ts_offset;
886
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400887 ts = convert_time(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000888 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000889
890 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400891 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000892 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400893 LOG(ERR) << rx_buffers[0]->str_code(rc);
894 LOG(ERR) << rx_buffers[0]->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000895 return 0;
896 }
897
Thomas Tsou204a9f12013-10-29 18:34:16 -0400898 // Create vector buffer
899 std::vector<std::vector<short> >
900 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
901
902 std::vector<short *> pkt_ptrs;
903 for (size_t i = 0; i < pkt_bufs.size(); i++)
904 pkt_ptrs.push_back(&pkt_bufs[i].front());
905
kurtis.heimerl965e7572011-11-26 03:16:54 +0000906 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400907 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
908 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
909 metadata, 0.1, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000910 rx_pkt_cnt++;
911
kurtis.heimerld4be0742011-11-26 03:17:46 +0000912 // Check for errors
913 rc = check_rx_md_err(metadata, num_smpls);
914 switch (rc) {
915 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000916 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
917 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000918 exit(-1);
919 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400920 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000921 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000922 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000923 }
924
kurtis.heimerl965e7572011-11-26 03:16:54 +0000925 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000926 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000927
Thomas Tsou204a9f12013-10-29 18:34:16 -0400928 for (size_t i = 0; i < rx_buffers.size(); i++) {
929 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
930 num_smpls,
931 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000932
Thomas Tsou204a9f12013-10-29 18:34:16 -0400933 // Continue on local overrun, exit on other errors
934 if ((rc < 0)) {
935 LOG(ERR) << rx_buffers[i]->str_code(rc);
936 LOG(ERR) << rx_buffers[i]->str_status();
937 if (rc != smpl_buf::ERROR_OVERFLOW)
938 return 0;
939 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000940 }
941 }
942
943 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400944 for (size_t i = 0; i < rx_buffers.size(); i++) {
945 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
946 if ((rc < 0) || (rc != len)) {
947 LOG(ERR) << rx_buffers[i]->str_code(rc);
948 LOG(ERR) << rx_buffers[i]->str_status();
949 return 0;
950 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000951 }
952
953 return len;
954}
955
Thomas Tsou204a9f12013-10-29 18:34:16 -0400956int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
957 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000958{
959 uhd::tx_metadata_t metadata;
960 metadata.has_time_spec = true;
961 metadata.start_of_burst = false;
962 metadata.end_of_burst = false;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400963 metadata.time_spec = convert_time(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000964
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000965 *underrun = false;
966
kurtis.heimerl965e7572011-11-26 03:16:54 +0000967 // No control packets
968 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000969 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000970 return 0;
971 }
972
Thomas Tsou204a9f12013-10-29 18:34:16 -0400973 if (bufs.size() != chans) {
974 LOG(ALERT) << "Invalid channel combination " << bufs.size();
975 return -1;
976 }
977
kurtis.heimerl965e7572011-11-26 03:16:54 +0000978 // Drop a fixed number of packets (magic value)
979 if (!aligned) {
980 drop_cnt++;
981
982 if (drop_cnt == 1) {
983 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000984 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000985 metadata.end_of_burst = true;
986 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000987 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000988 return len;
989 } else {
990 LOG(DEBUG) << "Aligning transmitter: start burst";
991 metadata.start_of_burst = true;
992 aligned = true;
993 drop_cnt = 0;
994 }
995 }
996
Thomas Tsou204a9f12013-10-29 18:34:16 -0400997 size_t num_smpls = tx_stream->send(bufs, len, metadata);
ttsoub371ed52012-01-09 18:11:34 +0000998 if (num_smpls != (unsigned) len) {
999 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001000 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001001
1002 return num_smpls;
1003}
1004
1005bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1006{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001007 return true;
1008}
1009
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001010uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1011{
1012 double rf_spread, rf_freq;
1013 std::vector<double> freqs;
1014 uhd::tune_request_t treq(freq);
1015
Thomas Tsou8e17df72014-03-06 14:16:11 -05001016 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) {
1017 if (offset == 0.0)
1018 return treq;
1019
1020 return uhd::tune_request_t(freq, offset);
1021 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001022 LOG(ALERT) << chans << " channels unsupported";
1023 return treq;
1024 }
1025
1026 if (tx)
1027 freqs = tx_freqs;
1028 else
1029 freqs = rx_freqs;
1030
1031 /* Tune directly if other channel isn't tuned */
1032 if (freqs[!chan] < 10.0)
1033 return treq;
1034
1035 /* Find center frequency between channels */
1036 rf_spread = fabs(freqs[!chan] - freq);
1037 if (rf_spread > B2XX_CLK_RT) {
1038 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1039 return treq;
1040 }
1041
1042 rf_freq = (freqs[!chan] + freq) / 2.0f;
1043
1044 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1045 treq.target_freq = freq;
1046 treq.rf_freq = rf_freq;
1047
1048 return treq;
1049}
1050
1051bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1052{
1053 std::vector<double> freqs;
1054 uhd::tune_result_t tres;
1055 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1056
1057 if (tx) {
1058 tres = usrp_dev->set_tx_freq(treq, chan);
1059 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1060 } else {
1061 tres = usrp_dev->set_rx_freq(treq, chan);
1062 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1063 }
1064 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1065
Thomas Tsou8e17df72014-03-06 14:16:11 -05001066 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1067 return true;
1068
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001069 /* Manual RF policy means we intentionally tuned with a baseband
1070 * offset for dual-channel purposes. Now retune the other channel
1071 * with the opposite corresponding frequency offset
1072 */
1073 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1074 if (tx) {
1075 treq = select_freq(tx_freqs[!chan], !chan, true);
1076 tres = usrp_dev->set_tx_freq(treq, !chan);
1077 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1078 } else {
1079 treq = select_freq(rx_freqs[!chan], !chan, false);
1080 tres = usrp_dev->set_rx_freq(treq, !chan);
1081 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1082
1083 }
1084 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1085 }
1086
1087 return true;
1088}
1089
Thomas Tsou204a9f12013-10-29 18:34:16 -04001090bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001091{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001092 if (chan >= tx_freqs.size()) {
1093 LOG(ALERT) << "Requested non-existent channel " << chan;
1094 return false;
1095 }
1096
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001097 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001098}
1099
Thomas Tsou204a9f12013-10-29 18:34:16 -04001100bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001101{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001102 if (chan >= rx_freqs.size()) {
1103 LOG(ALERT) << "Requested non-existent channel " << chan;
1104 return false;
1105 }
1106
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001107 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001108}
1109
Thomas Tsou204a9f12013-10-29 18:34:16 -04001110double uhd_device::getTxFreq(size_t chan)
1111{
1112 if (chan >= tx_freqs.size()) {
1113 LOG(ALERT) << "Requested non-existent channel " << chan;
1114 return 0.0;
1115 }
1116
1117 return tx_freqs[chan];
1118}
1119
1120double uhd_device::getRxFreq(size_t chan)
1121{
1122 if (chan >= rx_freqs.size()) {
1123 LOG(ALERT) << "Requested non-existent channel " << chan;
1124 return 0.0;
1125 }
1126
1127 return rx_freqs[chan];
1128}
1129
kurtis.heimerl965e7572011-11-26 03:16:54 +00001130bool uhd_device::recv_async_msg()
1131{
ttsou60dc4c92012-08-08 23:30:23 +00001132 uhd::async_metadata_t md;
1133 if (!usrp_dev->get_device()->recv_async_msg(md))
kurtis.heimerl965e7572011-11-26 03:16:54 +00001134 return false;
1135
1136 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001137 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001138 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001139
1140 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1141 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1142 LOG(ERR) << str_code(md);
1143 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001144 }
1145
1146 return true;
1147}
1148
1149std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1150{
1151 std::ostringstream ost("UHD: ");
1152
1153 switch (metadata.error_code) {
1154 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1155 ost << "No error";
1156 break;
1157 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1158 ost << "No packet received, implementation timed-out";
1159 break;
1160 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1161 ost << "A stream command was issued in the past";
1162 break;
1163 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1164 ost << "Expected another stream command";
1165 break;
1166 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1167 ost << "An internal receive buffer has filled";
1168 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001169 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1170 ost << "Multi-channel alignment failed";
1171 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001172 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1173 ost << "The packet could not be parsed";
1174 break;
1175 default:
1176 ost << "Unknown error " << metadata.error_code;
1177 }
1178
1179 if (metadata.has_time_spec)
1180 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1181
1182 return ost.str();
1183}
1184
1185std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1186{
1187 std::ostringstream ost("UHD: ");
1188
1189 switch (metadata.event_code) {
1190 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1191 ost << "A packet was successfully transmitted";
1192 break;
1193 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1194 ost << "An internal send buffer has emptied";
1195 break;
1196 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1197 ost << "Packet loss between host and device";
1198 break;
1199 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1200 ost << "Packet time was too late or too early";
1201 break;
1202 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1203 ost << "Underflow occurred inside a packet";
1204 break;
1205 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1206 ost << "Packet loss within a burst";
1207 break;
1208 default:
1209 ost << "Unknown error " << metadata.event_code;
1210 }
1211
1212 if (metadata.has_time_spec)
1213 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1214
1215 return ost.str();
1216}
1217
1218smpl_buf::smpl_buf(size_t len, double rate)
1219 : buf_len(len), clk_rt(rate),
1220 time_start(0), time_end(0), data_start(0), data_end(0)
1221{
1222 data = new uint32_t[len];
1223}
1224
1225smpl_buf::~smpl_buf()
1226{
1227 delete[] data;
1228}
1229
1230ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1231{
1232 if (timestamp < time_start)
1233 return ERROR_TIMESTAMP;
1234 else if (timestamp >= time_end)
1235 return 0;
1236 else
1237 return time_end - timestamp;
1238}
1239
1240ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1241{
1242 return avail_smpls(convert_time(timespec, clk_rt));
1243}
1244
1245ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1246{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001247 int type_sz = 2 * sizeof(short);
1248
kurtis.heimerl965e7572011-11-26 03:16:54 +00001249 // Check for valid read
1250 if (timestamp < time_start)
1251 return ERROR_TIMESTAMP;
1252 if (timestamp >= time_end)
1253 return 0;
1254 if (len >= buf_len)
1255 return ERROR_READ;
1256
1257 // How many samples should be copied
1258 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001259 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001260 num_smpls = len;
1261
1262 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001263 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001264
1265 // Read it
1266 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001267 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001268 memcpy(buf, data + read_start, numBytes);
1269 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001270 size_t first_cp = (buf_len - read_start) * type_sz;
1271 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001272
1273 memcpy(buf, data + read_start, first_cp);
1274 memcpy((char*) buf + first_cp, data, second_cp);
1275 }
1276
1277 data_start = (read_start + len) % buf_len;
1278 time_start = timestamp + len;
1279
1280 if (time_start > time_end)
1281 return ERROR_READ;
1282 else
1283 return num_smpls;
1284}
1285
1286ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1287{
1288 return read(buf, len, convert_time(ts, clk_rt));
1289}
1290
1291ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1292{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001293 int type_sz = 2 * sizeof(short);
1294
kurtis.heimerl965e7572011-11-26 03:16:54 +00001295 // Check for valid write
1296 if ((len == 0) || (len >= buf_len))
1297 return ERROR_WRITE;
1298 if ((timestamp + len) <= time_end)
1299 return ERROR_TIMESTAMP;
1300
1301 // Starting index
1302 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1303
1304 // Write it
1305 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001306 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001307 memcpy(data + write_start, buf, numBytes);
1308 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001309 size_t first_cp = (buf_len - write_start) * type_sz;
1310 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001311
1312 memcpy(data + write_start, buf, first_cp);
1313 memcpy(data, (char*) buf + first_cp, second_cp);
1314 }
1315
1316 data_end = (write_start + len) % buf_len;
1317 time_end = timestamp + len;
1318
Thomas Tsou18d3b832014-02-13 14:55:23 -05001319 if (!data_start)
1320 data_start = write_start;
1321
kurtis.heimerl965e7572011-11-26 03:16:54 +00001322 if (((write_start + len) > buf_len) && (data_end > data_start))
1323 return ERROR_OVERFLOW;
1324 else if (time_end <= time_start)
1325 return ERROR_WRITE;
1326 else
1327 return len;
1328}
1329
1330ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1331{
1332 return write(buf, len, convert_time(ts, clk_rt));
1333}
1334
1335std::string smpl_buf::str_status() const
1336{
1337 std::ostringstream ost("Sample buffer: ");
1338
1339 ost << "length = " << buf_len;
1340 ost << ", time_start = " << time_start;
1341 ost << ", time_end = " << time_end;
1342 ost << ", data_start = " << data_start;
1343 ost << ", data_end = " << data_end;
1344
1345 return ost.str();
1346}
1347
1348std::string smpl_buf::str_code(ssize_t code)
1349{
1350 switch (code) {
1351 case ERROR_TIMESTAMP:
1352 return "Sample buffer: Requested timestamp is not valid";
1353 case ERROR_READ:
1354 return "Sample buffer: Read error";
1355 case ERROR_WRITE:
1356 return "Sample buffer: Write error";
1357 case ERROR_OVERFLOW:
1358 return "Sample buffer: Overrun";
1359 default:
1360 return "Sample buffer: Unknown error";
1361 }
1362}
1363
Thomas Tsou8e17df72014-03-06 14:16:11 -05001364RadioDevice *RadioDevice::make(size_t sps, size_t chans,
1365 bool diversity, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001366{
Thomas Tsou8e17df72014-03-06 14:16:11 -05001367 return new uhd_device(sps, chans, diversity, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001368}