blob: c914868392902a6f7b98699755981139e3e36a94 [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 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800228 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000229
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;
826
kurtis.heimerld4be0742011-11-26 03:17:46 +0000827 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000828 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000829
830 switch (md.error_code) {
831 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000832 LOG(ALERT) << "UHD: Receive timed out";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000833 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
834 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
835 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
836 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
837 default:
838 return ERROR_UNHANDLED;
839 }
840 }
841
kurtis.heimerl965e7572011-11-26 03:16:54 +0000842 // Missing timestamp
843 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000844 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000845 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000846 }
847
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000848 ts = md.time_spec;
849
kurtis.heimerl965e7572011-11-26 03:16:54 +0000850 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000851 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000852 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000853 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
854 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000855 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000856 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000857 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000858 }
859
860 return 0;
861}
862
Thomas Tsou204a9f12013-10-29 18:34:16 -0400863int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
864 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000865{
866 ssize_t rc;
867 uhd::time_spec_t ts;
868 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000869
Thomas Tsou204a9f12013-10-29 18:34:16 -0400870 if (bufs.size() != chans) {
871 LOG(ALERT) << "Invalid channel combination " << bufs.size();
872 return -1;
873 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000874
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000875 *overrun = false;
876 *underrun = false;
877
kurtis.heimerl965e7572011-11-26 03:16:54 +0000878 // Shift read time with respect to transmit clock
879 timestamp += ts_offset;
880
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400881 ts = convert_time(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000882 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000883
884 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400885 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000886 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400887 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800888 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000889 return 0;
890 }
891
Thomas Tsou204a9f12013-10-29 18:34:16 -0400892 // Create vector buffer
893 std::vector<std::vector<short> >
894 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
895
896 std::vector<short *> pkt_ptrs;
897 for (size_t i = 0; i < pkt_bufs.size(); i++)
898 pkt_ptrs.push_back(&pkt_bufs[i].front());
899
kurtis.heimerl965e7572011-11-26 03:16:54 +0000900 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400901 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
902 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
903 metadata, 0.1, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000904 rx_pkt_cnt++;
905
kurtis.heimerld4be0742011-11-26 03:17:46 +0000906 // Check for errors
907 rc = check_rx_md_err(metadata, num_smpls);
908 switch (rc) {
909 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000910 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
911 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000912 exit(-1);
913 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400914 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000915 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000916 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000917 }
918
kurtis.heimerl965e7572011-11-26 03:16:54 +0000919 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000920 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000921
Thomas Tsou204a9f12013-10-29 18:34:16 -0400922 for (size_t i = 0; i < rx_buffers.size(); i++) {
923 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
924 num_smpls,
925 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000926
Thomas Tsou204a9f12013-10-29 18:34:16 -0400927 // Continue on local overrun, exit on other errors
928 if ((rc < 0)) {
929 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800930 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400931 if (rc != smpl_buf::ERROR_OVERFLOW)
932 return 0;
933 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000934 }
935 }
936
937 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400938 for (size_t i = 0; i < rx_buffers.size(); i++) {
939 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
940 if ((rc < 0) || (rc != len)) {
941 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800942 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400943 return 0;
944 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000945 }
946
947 return len;
948}
949
Thomas Tsou204a9f12013-10-29 18:34:16 -0400950int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
951 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000952{
953 uhd::tx_metadata_t metadata;
954 metadata.has_time_spec = true;
955 metadata.start_of_burst = false;
956 metadata.end_of_burst = false;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400957 metadata.time_spec = convert_time(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000958
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000959 *underrun = false;
960
kurtis.heimerl965e7572011-11-26 03:16:54 +0000961 // No control packets
962 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000963 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000964 return 0;
965 }
966
Thomas Tsou204a9f12013-10-29 18:34:16 -0400967 if (bufs.size() != chans) {
968 LOG(ALERT) << "Invalid channel combination " << bufs.size();
969 return -1;
970 }
971
kurtis.heimerl965e7572011-11-26 03:16:54 +0000972 // Drop a fixed number of packets (magic value)
973 if (!aligned) {
974 drop_cnt++;
975
976 if (drop_cnt == 1) {
977 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000978 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000979 metadata.end_of_burst = true;
980 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000981 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000982 return len;
983 } else {
984 LOG(DEBUG) << "Aligning transmitter: start burst";
985 metadata.start_of_burst = true;
986 aligned = true;
987 drop_cnt = 0;
988 }
989 }
990
Thomas Tsou204a9f12013-10-29 18:34:16 -0400991 size_t num_smpls = tx_stream->send(bufs, len, metadata);
ttsoub371ed52012-01-09 18:11:34 +0000992 if (num_smpls != (unsigned) len) {
993 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +0000994 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000995
996 return num_smpls;
997}
998
999bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1000{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001001 return true;
1002}
1003
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001004uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1005{
1006 double rf_spread, rf_freq;
1007 std::vector<double> freqs;
1008 uhd::tune_request_t treq(freq);
1009
Thomas Tsou8e17df72014-03-06 14:16:11 -05001010 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) {
1011 if (offset == 0.0)
1012 return treq;
1013
1014 return uhd::tune_request_t(freq, offset);
1015 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001016 LOG(ALERT) << chans << " channels unsupported";
1017 return treq;
1018 }
1019
1020 if (tx)
1021 freqs = tx_freqs;
1022 else
1023 freqs = rx_freqs;
1024
1025 /* Tune directly if other channel isn't tuned */
1026 if (freqs[!chan] < 10.0)
1027 return treq;
1028
1029 /* Find center frequency between channels */
1030 rf_spread = fabs(freqs[!chan] - freq);
1031 if (rf_spread > B2XX_CLK_RT) {
1032 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1033 return treq;
1034 }
1035
1036 rf_freq = (freqs[!chan] + freq) / 2.0f;
1037
1038 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1039 treq.target_freq = freq;
1040 treq.rf_freq = rf_freq;
1041
1042 return treq;
1043}
1044
1045bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1046{
1047 std::vector<double> freqs;
1048 uhd::tune_result_t tres;
1049 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1050
1051 if (tx) {
1052 tres = usrp_dev->set_tx_freq(treq, chan);
1053 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1054 } else {
1055 tres = usrp_dev->set_rx_freq(treq, chan);
1056 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1057 }
1058 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1059
Thomas Tsou8e17df72014-03-06 14:16:11 -05001060 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1061 return true;
1062
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001063 /* Manual RF policy means we intentionally tuned with a baseband
1064 * offset for dual-channel purposes. Now retune the other channel
1065 * with the opposite corresponding frequency offset
1066 */
1067 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1068 if (tx) {
1069 treq = select_freq(tx_freqs[!chan], !chan, true);
1070 tres = usrp_dev->set_tx_freq(treq, !chan);
1071 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1072 } else {
1073 treq = select_freq(rx_freqs[!chan], !chan, false);
1074 tres = usrp_dev->set_rx_freq(treq, !chan);
1075 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1076
1077 }
1078 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1079 }
1080
1081 return true;
1082}
1083
Thomas Tsou204a9f12013-10-29 18:34:16 -04001084bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001085{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001086 if (chan >= tx_freqs.size()) {
1087 LOG(ALERT) << "Requested non-existent channel " << chan;
1088 return false;
1089 }
1090
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001091 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001092}
1093
Thomas Tsou204a9f12013-10-29 18:34:16 -04001094bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001095{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001096 if (chan >= rx_freqs.size()) {
1097 LOG(ALERT) << "Requested non-existent channel " << chan;
1098 return false;
1099 }
1100
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001101 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001102}
1103
Thomas Tsou204a9f12013-10-29 18:34:16 -04001104double uhd_device::getTxFreq(size_t chan)
1105{
1106 if (chan >= tx_freqs.size()) {
1107 LOG(ALERT) << "Requested non-existent channel " << chan;
1108 return 0.0;
1109 }
1110
1111 return tx_freqs[chan];
1112}
1113
1114double uhd_device::getRxFreq(size_t chan)
1115{
1116 if (chan >= rx_freqs.size()) {
1117 LOG(ALERT) << "Requested non-existent channel " << chan;
1118 return 0.0;
1119 }
1120
1121 return rx_freqs[chan];
1122}
1123
kurtis.heimerl965e7572011-11-26 03:16:54 +00001124bool uhd_device::recv_async_msg()
1125{
ttsou60dc4c92012-08-08 23:30:23 +00001126 uhd::async_metadata_t md;
1127 if (!usrp_dev->get_device()->recv_async_msg(md))
kurtis.heimerl965e7572011-11-26 03:16:54 +00001128 return false;
1129
1130 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001131 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001132 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001133
1134 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1135 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1136 LOG(ERR) << str_code(md);
1137 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001138 }
1139
1140 return true;
1141}
1142
1143std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1144{
1145 std::ostringstream ost("UHD: ");
1146
1147 switch (metadata.error_code) {
1148 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1149 ost << "No error";
1150 break;
1151 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1152 ost << "No packet received, implementation timed-out";
1153 break;
1154 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1155 ost << "A stream command was issued in the past";
1156 break;
1157 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1158 ost << "Expected another stream command";
1159 break;
1160 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1161 ost << "An internal receive buffer has filled";
1162 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001163 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1164 ost << "Multi-channel alignment failed";
1165 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001166 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1167 ost << "The packet could not be parsed";
1168 break;
1169 default:
1170 ost << "Unknown error " << metadata.error_code;
1171 }
1172
1173 if (metadata.has_time_spec)
1174 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1175
1176 return ost.str();
1177}
1178
1179std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1180{
1181 std::ostringstream ost("UHD: ");
1182
1183 switch (metadata.event_code) {
1184 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1185 ost << "A packet was successfully transmitted";
1186 break;
1187 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1188 ost << "An internal send buffer has emptied";
1189 break;
1190 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1191 ost << "Packet loss between host and device";
1192 break;
1193 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1194 ost << "Packet time was too late or too early";
1195 break;
1196 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1197 ost << "Underflow occurred inside a packet";
1198 break;
1199 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1200 ost << "Packet loss within a burst";
1201 break;
1202 default:
1203 ost << "Unknown error " << metadata.event_code;
1204 }
1205
1206 if (metadata.has_time_spec)
1207 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1208
1209 return ost.str();
1210}
1211
1212smpl_buf::smpl_buf(size_t len, double rate)
1213 : buf_len(len), clk_rt(rate),
1214 time_start(0), time_end(0), data_start(0), data_end(0)
1215{
1216 data = new uint32_t[len];
1217}
1218
1219smpl_buf::~smpl_buf()
1220{
1221 delete[] data;
1222}
1223
1224ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1225{
1226 if (timestamp < time_start)
1227 return ERROR_TIMESTAMP;
1228 else if (timestamp >= time_end)
1229 return 0;
1230 else
1231 return time_end - timestamp;
1232}
1233
1234ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1235{
1236 return avail_smpls(convert_time(timespec, clk_rt));
1237}
1238
1239ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1240{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001241 int type_sz = 2 * sizeof(short);
1242
kurtis.heimerl965e7572011-11-26 03:16:54 +00001243 // Check for valid read
1244 if (timestamp < time_start)
1245 return ERROR_TIMESTAMP;
1246 if (timestamp >= time_end)
1247 return 0;
1248 if (len >= buf_len)
1249 return ERROR_READ;
1250
1251 // How many samples should be copied
1252 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001253 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001254 num_smpls = len;
1255
1256 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001257 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001258
1259 // Read it
1260 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001261 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001262 memcpy(buf, data + read_start, numBytes);
1263 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001264 size_t first_cp = (buf_len - read_start) * type_sz;
1265 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001266
1267 memcpy(buf, data + read_start, first_cp);
1268 memcpy((char*) buf + first_cp, data, second_cp);
1269 }
1270
1271 data_start = (read_start + len) % buf_len;
1272 time_start = timestamp + len;
1273
1274 if (time_start > time_end)
1275 return ERROR_READ;
1276 else
1277 return num_smpls;
1278}
1279
1280ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1281{
1282 return read(buf, len, convert_time(ts, clk_rt));
1283}
1284
1285ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1286{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001287 int type_sz = 2 * sizeof(short);
1288
kurtis.heimerl965e7572011-11-26 03:16:54 +00001289 // Check for valid write
1290 if ((len == 0) || (len >= buf_len))
1291 return ERROR_WRITE;
1292 if ((timestamp + len) <= time_end)
1293 return ERROR_TIMESTAMP;
1294
1295 // Starting index
1296 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1297
1298 // Write it
1299 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001300 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001301 memcpy(data + write_start, buf, numBytes);
1302 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001303 size_t first_cp = (buf_len - write_start) * type_sz;
1304 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001305
1306 memcpy(data + write_start, buf, first_cp);
1307 memcpy(data, (char*) buf + first_cp, second_cp);
1308 }
1309
1310 data_end = (write_start + len) % buf_len;
1311 time_end = timestamp + len;
1312
Thomas Tsou18d3b832014-02-13 14:55:23 -05001313 if (!data_start)
1314 data_start = write_start;
1315
kurtis.heimerl965e7572011-11-26 03:16:54 +00001316 if (((write_start + len) > buf_len) && (data_end > data_start))
1317 return ERROR_OVERFLOW;
1318 else if (time_end <= time_start)
1319 return ERROR_WRITE;
1320 else
1321 return len;
1322}
1323
1324ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1325{
1326 return write(buf, len, convert_time(ts, clk_rt));
1327}
1328
Tom Tsou1ae25562014-12-05 12:56:34 -08001329std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001330{
1331 std::ostringstream ost("Sample buffer: ");
1332
Tom Tsou1ae25562014-12-05 12:56:34 -08001333 ost << "timestamp = " << ts;
1334 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001335 ost << ", time_start = " << time_start;
1336 ost << ", time_end = " << time_end;
1337 ost << ", data_start = " << data_start;
1338 ost << ", data_end = " << data_end;
1339
1340 return ost.str();
1341}
1342
1343std::string smpl_buf::str_code(ssize_t code)
1344{
1345 switch (code) {
1346 case ERROR_TIMESTAMP:
1347 return "Sample buffer: Requested timestamp is not valid";
1348 case ERROR_READ:
1349 return "Sample buffer: Read error";
1350 case ERROR_WRITE:
1351 return "Sample buffer: Write error";
1352 case ERROR_OVERFLOW:
1353 return "Sample buffer: Overrun";
1354 default:
1355 return "Sample buffer: Unknown error";
1356 }
1357}
1358
Thomas Tsou8e17df72014-03-06 14:16:11 -05001359RadioDevice *RadioDevice::make(size_t sps, size_t chans,
1360 bool diversity, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001361{
Thomas Tsou8e17df72014-03-06 14:16:11 -05001362 return new uhd_device(sps, chans, diversity, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001363}