blob: 2bd767cc2d1b15d387c4d06f50aef40b9e03b50d [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 Tsou092f7572013-04-04 17:04:39 -040035#define BXXX_CLK_RT 52e6
36#define BXXX_BASE_RT GSMRATE
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040037#define USRP2_BASE_RT 390625
Thomas Tsoue3e88142013-04-05 20:42:41 -040038#define TX_AMPL 0.3
39#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040040
41enum uhd_dev_type {
42 USRP1,
43 USRP2,
44 B100,
Thomas Tsou092f7572013-04-04 17:04:39 -040045 B200,
Thomas Tsouc88d8d52013-08-21 17:55:54 -040046 UMTRX,
Thomas Tsou02d88d12013-04-05 15:36:30 -040047 NUM_USRP_TYPES,
48};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000049
Thomas Tsoue3e88142013-04-05 20:42:41 -040050struct uhd_dev_offset {
51 enum uhd_dev_type type;
52 int sps;
53 double offset;
54};
55
kurtis.heimerl965e7572011-11-26 03:16:54 +000056/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040057 * Tx / Rx sample offset values. In a perfect world, there is no group delay
58 * though analog components, and behaviour through digital filters exactly
59 * matches calculated values. In reality, there are unaccounted factors,
60 * which are captured in these empirically measured (using a loopback test)
61 * timing correction values.
62 *
63 * Notes:
64 * USRP1 with timestamps is not supported by UHD.
65 */
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080066static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
Thomas Tsoue3e88142013-04-05 20:42:41 -040067 { USRP1, 1, 0.0 },
Thomas Tsoue3e88142013-04-05 20:42:41 -040068 { USRP1, 4, 0.0 },
69 { USRP2, 1, 5.4394e-5 },
Thomas Tsoue3e88142013-04-05 20:42:41 -040070 { USRP2, 4, 0.0 },
71 { B100, 1, 9.4778e-5 },
Thomas Tsoue3e88142013-04-05 20:42:41 -040072 { B100, 4, 2.9418e-5 },
Thomas Tsou092f7572013-04-04 17:04:39 -040073 { B200, 1, 0.0 },
74 { B200, 4, 9.8358e-5 },
Thomas Tsouc88d8d52013-08-21 17:55:54 -040075 { UMTRX, 1, 9.4778e-5 },
Thomas Tsouc88d8d52013-08-21 17:55:54 -040076 { UMTRX, 4, 0.0 },
Thomas Tsoue3e88142013-04-05 20:42:41 -040077};
kurtis.heimerl965e7572011-11-26 03:16:54 +000078
Thomas Tsoue3e88142013-04-05 20:42:41 -040079static double get_dev_offset(enum uhd_dev_type type, int sps)
80{
81 if (type == USRP1) {
82 LOG(ERR) << "Invalid device type";
83 return 0.0;
84 }
kurtis.heimerl965e7572011-11-26 03:16:54 +000085
Thomas Tsoue3e88142013-04-05 20:42:41 -040086 switch (sps) {
87 case 1:
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080088 return uhd_offsets[2 * type + 0].offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -040089 case 4:
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080090 return uhd_offsets[2 * type + 1].offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -040091 }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000092
Thomas Tsoue3e88142013-04-05 20:42:41 -040093 LOG(ERR) << "Unsupported samples-per-symbols: " << sps;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080094 return 0.0;
Thomas Tsoue3e88142013-04-05 20:42:41 -040095}
kurtis.heimerlce317332011-11-26 03:18:39 +000096
Thomas Tsoucb69f082013-04-08 14:18:26 -040097/*
98 * Select sample rate based on device type and requested samples-per-symbol.
99 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
100 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800101 */
Thomas Tsoucb69f082013-04-08 14:18:26 -0400102static double select_rate(uhd_dev_type type, int sps)
103{
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800104 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400105 return -9999.99;
106
107 switch (type) {
108 case USRP2:
109 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400110 case B100:
Thomas Tsou092f7572013-04-04 17:04:39 -0400111 case B200:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400112 case UMTRX:
113 return GSMRATE * sps;
114 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400115 break;
116 }
117
118 LOG(ALERT) << "Unknown device type " << type;
119 return -9999.99;
120}
121
kurtis.heimerl965e7572011-11-26 03:16:54 +0000122/** Timestamp conversion
123 @param timestamp a UHD or OpenBTS timestamp
124 @param rate sample rate
125 @return the converted timestamp
126*/
127uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
128{
129 double secs = (double) ticks / rate;
130 return uhd::time_spec_t(secs);
131}
132
133TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
134{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +0000135 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000136 return ts.get_tick_count(rate) + ticks;
137}
138
139/*
140 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
141 or UHD style timestamps. Time conversions are handled
142 internally or accessable through the static convert calls.
143*/
144class smpl_buf {
145public:
146 /** Sample buffer constructor
147 @param len number of 32-bit samples the buffer should hold
148 @param rate sample clockrate
149 @param timestamp
150 */
151 smpl_buf(size_t len, double rate);
152 ~smpl_buf();
153
154 /** Query number of samples available for reading
155 @param timestamp time of first sample
156 @return number of available samples or error
157 */
158 ssize_t avail_smpls(TIMESTAMP timestamp) const;
159 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
160
161 /** Read and write
162 @param buf pointer to buffer
163 @param len number of samples desired to read or write
164 @param timestamp time of first stample
165 @return number of actual samples read or written or error
166 */
167 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
168 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
169 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
170 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
171
172 /** Buffer status string
173 @return a formatted string describing internal buffer state
174 */
175 std::string str_status() const;
176
177 /** Formatted error string
178 @param code an error code
179 @return a formatted error string
180 */
181 static std::string str_code(ssize_t code);
182
183 enum err_code {
184 ERROR_TIMESTAMP = -1,
185 ERROR_READ = -2,
186 ERROR_WRITE = -3,
187 ERROR_OVERFLOW = -4
188 };
189
190private:
191 uint32_t *data;
192 size_t buf_len;
193
194 double clk_rt;
195
196 TIMESTAMP time_start;
197 TIMESTAMP time_end;
198
199 size_t data_start;
200 size_t data_end;
201};
202
203/*
204 uhd_device - UHD implementation of the Device interface. Timestamped samples
205 are sent to and received from the device. An intermediate buffer
206 on the receive side collects and aligns packets of samples.
207 Events and errors such as underruns are reported asynchronously
208 by the device and received in a separate thread.
209*/
210class uhd_device : public RadioDevice {
211public:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400212 uhd_device(int sps, bool skip_rx);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000213 ~uhd_device();
214
Thomas Tsoucb69f082013-04-08 14:18:26 -0400215 int open(const std::string &args);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000216 bool start();
217 bool stop();
kurtis.heimerl68292102011-11-26 03:17:28 +0000218 void restart(uhd::time_spec_t ts);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000219 void setPriority();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400220 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000221
222 int readSamples(short *buf, int len, bool *overrun,
223 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
224
225 int writeSamples(short *buf, int len, bool *underrun,
226 TIMESTAMP timestamp, bool isControl);
227
228 bool updateAlignment(TIMESTAMP timestamp);
229
230 bool setTxFreq(double wFreq);
231 bool setRxFreq(double wFreq);
232
233 inline TIMESTAMP initialWriteTimestamp() { return 0; }
234 inline TIMESTAMP initialReadTimestamp() { return 0; }
235
Thomas Tsoue3e88142013-04-05 20:42:41 -0400236 inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000237 inline double fullScaleOutputValue() { return 32000; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000238
kurtis.heimerl02d04052011-11-26 03:17:10 +0000239 double setRxGain(double db);
240 double getRxGain(void) { return rx_gain; }
241 double maxRxGain(void) { return rx_gain_max; }
242 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000243
kurtis.heimerl02d04052011-11-26 03:17:10 +0000244 double setTxGain(double db);
245 double maxTxGain(void) { return tx_gain_max; }
246 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000247
kurtis.heimerl02d04052011-11-26 03:17:10 +0000248 double getTxFreq() { return tx_freq; }
249 double getRxFreq() { return rx_freq; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000250
251 inline double getSampleRate() { return actual_smpl_rt; }
252 inline double numberRead() { return rx_pkt_cnt; }
253 inline double numberWritten() { return 0; }
254
255 /** Receive and process asynchronous message
256 @return true if message received or false on timeout or error
257 */
258 bool recv_async_msg();
259
kurtis.heimerld4be0742011-11-26 03:17:46 +0000260 enum err_code {
261 ERROR_TIMING = -1,
262 ERROR_UNRECOVERABLE = -2,
263 ERROR_UNHANDLED = -3,
264 };
265
kurtis.heimerl965e7572011-11-26 03:16:54 +0000266private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000267 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400268 uhd::tx_streamer::sptr tx_stream;
269 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400270 enum TxWindowType tx_window;
271 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000272
Thomas Tsoue3e88142013-04-05 20:42:41 -0400273 int sps;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000274 double desired_smpl_rt, actual_smpl_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000275
kurtis.heimerl02d04052011-11-26 03:17:10 +0000276 double tx_gain, tx_gain_min, tx_gain_max;
277 double rx_gain, rx_gain_min, rx_gain_max;
278
279 double tx_freq, rx_freq;
280 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000281
282 bool started;
283 bool aligned;
284 bool skip_rx;
285
286 size_t rx_pkt_cnt;
287 size_t drop_cnt;
288 uhd::time_spec_t prev_ts;
289
290 TIMESTAMP ts_offset;
291 smpl_buf *rx_smpl_buf;
292
kurtis.heimerl02d04052011-11-26 03:17:10 +0000293 void init_gains();
kurtis.heimerl24481de2011-11-26 03:17:18 +0000294 void set_ref_clk(bool ext_clk);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400295 int set_master_clk(double rate);
296 int set_rates(double rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000297 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000298 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000299 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000300
kurtis.heimerl965e7572011-11-26 03:16:54 +0000301 std::string str_code(uhd::rx_metadata_t metadata);
302 std::string str_code(uhd::async_metadata_t metadata);
303
304 Thread async_event_thrd;
305};
306
307void *async_event_loop(uhd_device *dev)
308{
309 while (1) {
310 dev->recv_async_msg();
311 pthread_testcancel();
312 }
313}
314
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000315/*
316 Catch and drop underrun 'U' and overrun 'O' messages from stdout
317 since we already report using the logging facility. Direct
318 everything else appropriately.
319 */
320void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
321{
322 switch (type) {
323 case uhd::msg::status:
324 LOG(INFO) << msg;
325 break;
326 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000327 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000328 break;
329 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000330 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000331 break;
332 case uhd::msg::fastpath:
333 break;
334 }
335}
336
Thomas Tsoucb69f082013-04-08 14:18:26 -0400337uhd_device::uhd_device(int sps, bool skip_rx)
338 : tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0),
kurtis.heimerl02d04052011-11-26 03:17:10 +0000339 rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0),
340 tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000341 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
kurtis.heimerl965e7572011-11-26 03:16:54 +0000342 prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
343{
Thomas Tsoue3e88142013-04-05 20:42:41 -0400344 this->sps = sps;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000345 this->skip_rx = skip_rx;
346}
347
348uhd_device::~uhd_device()
349{
350 stop();
351
352 if (rx_smpl_buf)
353 delete rx_smpl_buf;
354}
355
kurtis.heimerl24481de2011-11-26 03:17:18 +0000356void uhd_device::init_gains()
357{
358 uhd::gain_range_t range;
359
360 range = usrp_dev->get_tx_gain_range();
361 tx_gain_min = range.start();
362 tx_gain_max = range.stop();
363
364 range = usrp_dev->get_rx_gain_range();
365 rx_gain_min = range.start();
366 rx_gain_max = range.stop();
367
368 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
369 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
370
371 tx_gain = usrp_dev->get_tx_gain();
372 rx_gain = usrp_dev->get_rx_gain();
373
374 return;
375}
376
377void uhd_device::set_ref_clk(bool ext_clk)
378{
kurtis.heimerl24481de2011-11-26 03:17:18 +0000379 if (ext_clk)
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400380 usrp_dev->set_clock_source("external");
kurtis.heimerl24481de2011-11-26 03:17:18 +0000381
382 return;
383}
384
Thomas Tsou02d88d12013-04-05 15:36:30 -0400385int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000386{
Thomas Tsou092f7572013-04-04 17:04:39 -0400387 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000388
Thomas Tsou7e068062013-04-08 19:39:37 -0400389 try {
390 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400391 } catch (const std::exception &ex) {
392 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
393 LOG(ALERT) << ex.what();
394 return -1;
395 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000396
Thomas Tsou092f7572013-04-04 17:04:39 -0400397 actual = usrp_dev->get_master_clock_rate();
398 offset = fabs(clk_rate - actual);
399
400 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000401 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400402 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400403 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400404 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000405 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400406
407 return 0;
408}
409
410int uhd_device::set_rates(double rate)
411{
Thomas Tsou092f7572013-04-04 17:04:39 -0400412 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400413 double tx_offset, rx_offset;
414
Thomas Tsou092f7572013-04-04 17:04:39 -0400415 // B100/200 are the only device where we set FPGA clocking
416 if ((dev_type == B100) || (dev_type == B200)) {
417 if (set_master_clk(BXXX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400418 return -1;
419 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000420
421 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400422 try {
423 usrp_dev->set_tx_rate(rate);
424 usrp_dev->set_rx_rate(rate);
425 } catch (const std::exception &ex) {
426 LOG(ALERT) << "UHD rate setting failed: " << rate;
427 LOG(ALERT) << ex.what();
428 return -1;
429 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400430 actual_smpl_rt = usrp_dev->get_tx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000431
Thomas Tsou092f7572013-04-04 17:04:39 -0400432 tx_offset = fabs(usrp_dev->get_tx_rate() - rate);
433 rx_offset = fabs(usrp_dev->get_rx_rate() - rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400434 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000435 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsou092f7572013-04-04 17:04:39 -0400436 LOG(ALERT) << "Tx/Rx (" << usrp_dev->get_rx_rate() << "/"
Thomas Tsoucb69f082013-04-08 14:18:26 -0400437 << usrp_dev->get_rx_rate() << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400438 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000439 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000440
Thomas Tsou02d88d12013-04-05 15:36:30 -0400441 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000442}
443
kurtis.heimerl02d04052011-11-26 03:17:10 +0000444double uhd_device::setTxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000445{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000446 usrp_dev->set_tx_gain(db);
447 tx_gain = usrp_dev->get_tx_gain();
448
449 LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
450
451 return tx_gain;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000452}
453
kurtis.heimerl02d04052011-11-26 03:17:10 +0000454double uhd_device::setRxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000455{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000456 usrp_dev->set_rx_gain(db);
457 rx_gain = usrp_dev->get_rx_gain();
458
459 LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
460
461 return rx_gain;
462}
463
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000464/*
465 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400466 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000467 deal with the transport latency. Reject the USRP1 because UHD doesn't
468 support timestamped samples with it.
469 */
470bool uhd_device::parse_dev_type()
471{
472 std::string mboard_str, dev_str;
473 uhd::property_tree::sptr prop_tree;
Thomas Tsou092f7572013-04-04 17:04:39 -0400474 size_t usrp1_str, usrp2_str, b100_str, b200_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000475
476 prop_tree = usrp_dev->get_device()->get_tree();
477 dev_str = prop_tree->access<std::string>("/name").get();
478 mboard_str = usrp_dev->get_mboard_name();
479
480 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400481 usrp2_str = dev_str.find("USRP2");
482 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400483 b200_str = mboard_str.find("B200");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400484 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000485
486 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000487 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
488 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400489 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000490 return false;
491 }
492
Thomas Tsoucb69f082013-04-08 14:18:26 -0400493 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400494 tx_window = TX_WINDOW_USRP1;
495 LOG(INFO) << "Using USRP1 type transmit window for "
496 << dev_str << " " << mboard_str;
497 dev_type = B100;
498 return true;
Thomas Tsou092f7572013-04-04 17:04:39 -0400499 } else if (b200_str != std::string::npos) {
500 dev_type = B200;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400501 } else if (usrp2_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400502 dev_type = USRP2;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400503 } else if (umtrx_str != std::string::npos) {
504 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400505 } else {
Thomas Tsou092f7572013-04-04 17:04:39 -0400506 LOG(ALERT) << "Unknown UHD device type " << dev_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400507 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000508 }
509
Thomas Tsou02d88d12013-04-05 15:36:30 -0400510 tx_window = TX_WINDOW_FIXED;
511 LOG(INFO) << "Using fixed transmit window for "
512 << dev_str << " " << mboard_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000513 return true;
514}
515
Thomas Tsoucb69f082013-04-08 14:18:26 -0400516int uhd_device::open(const std::string &args)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000517{
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000518 // Register msg handler
519 uhd::msg::register_handler(&uhd_msg_handler);
520
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000521 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000522 uhd::device_addr_t addr(args);
523 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000524 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000525 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400526 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000527 }
528
529 // Use the first found device
530 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000531 try {
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000532 usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
kurtis.heimerle380af32011-11-26 03:18:55 +0000533 } catch(...) {
ttsou3b5c0c12012-02-14 17:58:11 +0000534 LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400535 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000536 }
537
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000538 // Check for a valid device type and set bus type
539 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400540 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000541
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000542#ifdef EXTREF
543 set_ref_clk(true);
544#endif
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400545 // Create TX and RX streamers
546 uhd::stream_args_t stream_args("sc16");
547 tx_stream = usrp_dev->get_tx_stream(stream_args);
548 rx_stream = usrp_dev->get_rx_stream(stream_args);
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000549
kurtis.heimerl965e7572011-11-26 03:16:54 +0000550 // Number of samples per over-the-wire packet
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400551 tx_spp = tx_stream->get_max_num_samps();
552 rx_spp = rx_stream->get_max_num_samps();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000553
554 // Set rates
Thomas Tsoucb69f082013-04-08 14:18:26 -0400555 desired_smpl_rt = select_rate(dev_type, sps);
Thomas Tsou84c60f62013-08-22 18:26:06 -0400556 if ((desired_smpl_rt > 0.0) && (set_rates(desired_smpl_rt) < 0))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400557 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000558
559 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400560 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000561 rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
562
563 // Set receive chain sample offset
Thomas Tsoue3e88142013-04-05 20:42:41 -0400564 double offset = get_dev_offset(dev_type, sps);
565 if (offset == 0.0) {
566 LOG(ERR) << "Unsupported configuration, no correction applied";
567 ts_offset = 0;
568 } else {
569 ts_offset = (TIMESTAMP) (offset * actual_smpl_rt);
570 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000571
kurtis.heimerl02d04052011-11-26 03:17:10 +0000572 // Initialize and shadow gain values
573 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000574
kurtis.heimerl965e7572011-11-26 03:16:54 +0000575 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000576 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000577
Thomas Tsoucb69f082013-04-08 14:18:26 -0400578 if (dev_type == USRP2)
579 return RESAMP;
580
581 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000582}
583
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000584bool uhd_device::flush_recv(size_t num_pkts)
585{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000586 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000587 size_t num_smpls;
588 uint32_t buff[rx_spp];
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000589 float timeout;
590
591 // Use .01 sec instead of the default .1 sec
592 timeout = .01;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000593
594 for (size_t i = 0; i < num_pkts; i++) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400595 num_smpls = rx_stream->recv(buff, rx_spp, md,
596 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000597 if (!num_smpls) {
598 switch (md.error_code) {
599 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
600 return true;
601 default:
602 continue;
603 }
604 }
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000605 }
606
607 return true;
608}
609
kurtis.heimerl68292102011-11-26 03:17:28 +0000610void uhd_device::restart(uhd::time_spec_t ts)
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000611{
kurtis.heimerl68292102011-11-26 03:17:28 +0000612 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
613 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000614
kurtis.heimerl68292102011-11-26 03:17:28 +0000615 flush_recv(50);
616
617 usrp_dev->set_time_now(ts);
618 aligned = false;
619
620 cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
621 cmd.stream_now = true;
622 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000623}
624
kurtis.heimerl965e7572011-11-26 03:16:54 +0000625bool uhd_device::start()
626{
627 LOG(INFO) << "Starting USRP...";
628
629 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000630 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000631 return false;
632 }
633
634 setPriority();
635
636 // Start asynchronous event (underrun check) loop
637 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
638
639 // Start streaming
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000640 restart(uhd::time_spec_t(0.0));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000641
kurtis.heimerl965e7572011-11-26 03:16:54 +0000642 // Display usrp time
643 double time_now = usrp_dev->get_time_now().get_real_secs();
644 LOG(INFO) << "The current time is " << time_now << " seconds";
645
646 started = true;
647 return true;
648}
649
650bool uhd_device::stop()
651{
652 uhd::stream_cmd_t stream_cmd =
653 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
654
655 usrp_dev->issue_stream_cmd(stream_cmd);
656
657 started = false;
658 return true;
659}
660
661void uhd_device::setPriority()
662{
663 uhd::set_thread_priority_safe();
664 return;
665}
666
kurtis.heimerld4be0742011-11-26 03:17:46 +0000667int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000668{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000669 uhd::time_spec_t ts;
670
kurtis.heimerld4be0742011-11-26 03:17:46 +0000671 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000672 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000673
674 switch (md.error_code) {
675 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000676 LOG(ALERT) << "UHD: Receive timed out";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000677 return ERROR_UNRECOVERABLE;
678 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
679 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
680 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
681 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
682 default:
683 return ERROR_UNHANDLED;
684 }
685 }
686
kurtis.heimerl965e7572011-11-26 03:16:54 +0000687 // Missing timestamp
688 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000689 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000690 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000691 }
692
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000693 ts = md.time_spec;
694
kurtis.heimerl965e7572011-11-26 03:16:54 +0000695 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000696 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000697 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000698 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
699 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000700 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000701 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000702 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000703 }
704
705 return 0;
706}
707
708int uhd_device::readSamples(short *buf, int len, bool *overrun,
709 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
710{
711 ssize_t rc;
712 uhd::time_spec_t ts;
713 uhd::rx_metadata_t metadata;
714 uint32_t pkt_buf[rx_spp];
715
716 if (skip_rx)
717 return 0;
718
719 // Shift read time with respect to transmit clock
720 timestamp += ts_offset;
721
722 ts = convert_time(timestamp, actual_smpl_rt);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000723 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000724
725 // Check that timestamp is valid
726 rc = rx_smpl_buf->avail_smpls(timestamp);
727 if (rc < 0) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000728 LOG(ERR) << rx_smpl_buf->str_code(rc);
729 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000730 return 0;
731 }
732
733 // Receive samples from the usrp until we have enough
734 while (rx_smpl_buf->avail_smpls(timestamp) < len) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400735 size_t num_smpls = rx_stream->recv(
kurtis.heimerl965e7572011-11-26 03:16:54 +0000736 (void*)pkt_buf,
737 rx_spp,
738 metadata,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400739 0.1,
740 true);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000741 rx_pkt_cnt++;
742
kurtis.heimerld4be0742011-11-26 03:17:46 +0000743 // Check for errors
744 rc = check_rx_md_err(metadata, num_smpls);
745 switch (rc) {
746 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000747 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
748 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000749 exit(-1);
750 case ERROR_TIMING:
751 restart(prev_ts);
752 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000753 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000754 }
755
kurtis.heimerl965e7572011-11-26 03:16:54 +0000756
757 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000758 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000759
760 rc = rx_smpl_buf->write(pkt_buf,
761 num_smpls,
762 metadata.time_spec);
763
764 // Continue on local overrun, exit on other errors
765 if ((rc < 0)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000766 LOG(ERR) << rx_smpl_buf->str_code(rc);
767 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000768 if (rc != smpl_buf::ERROR_OVERFLOW)
769 return 0;
770 }
771 }
772
773 // We have enough samples
774 rc = rx_smpl_buf->read(buf, len, timestamp);
775 if ((rc < 0) || (rc != len)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000776 LOG(ERR) << rx_smpl_buf->str_code(rc);
777 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000778 return 0;
779 }
780
781 return len;
782}
783
784int uhd_device::writeSamples(short *buf, int len, bool *underrun,
785 unsigned long long timestamp,bool isControl)
786{
787 uhd::tx_metadata_t metadata;
788 metadata.has_time_spec = true;
789 metadata.start_of_burst = false;
790 metadata.end_of_burst = false;
791 metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
792
793 // No control packets
794 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000795 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000796 return 0;
797 }
798
799 // Drop a fixed number of packets (magic value)
800 if (!aligned) {
801 drop_cnt++;
802
803 if (drop_cnt == 1) {
804 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000805 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000806 metadata.end_of_burst = true;
807 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000808 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000809 return len;
810 } else {
811 LOG(DEBUG) << "Aligning transmitter: start burst";
812 metadata.start_of_burst = true;
813 aligned = true;
814 drop_cnt = 0;
815 }
816 }
817
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400818 size_t num_smpls = tx_stream->send(buf, len, metadata);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000819
ttsoub371ed52012-01-09 18:11:34 +0000820 if (num_smpls != (unsigned) len) {
821 LOG(ALERT) << "UHD: Device send timed out";
822 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
823 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
824 exit(-1);
825 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000826
827 return num_smpls;
828}
829
830bool uhd_device::updateAlignment(TIMESTAMP timestamp)
831{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000832 return true;
833}
834
835bool uhd_device::setTxFreq(double wFreq)
836{
837 uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000838 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000839 tx_freq = usrp_dev->get_tx_freq();
840
kurtis.heimerl965e7572011-11-26 03:16:54 +0000841 return true;
842}
843
844bool uhd_device::setRxFreq(double wFreq)
845{
846 uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000847 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000848 rx_freq = usrp_dev->get_rx_freq();
849
kurtis.heimerl965e7572011-11-26 03:16:54 +0000850 return true;
851}
852
853bool uhd_device::recv_async_msg()
854{
ttsou60dc4c92012-08-08 23:30:23 +0000855 uhd::async_metadata_t md;
856 if (!usrp_dev->get_device()->recv_async_msg(md))
kurtis.heimerl965e7572011-11-26 03:16:54 +0000857 return false;
858
859 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +0000860 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +0000861 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +0000862
863 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
864 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
865 LOG(ERR) << str_code(md);
866 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000867 }
868
869 return true;
870}
871
872std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
873{
874 std::ostringstream ost("UHD: ");
875
876 switch (metadata.error_code) {
877 case uhd::rx_metadata_t::ERROR_CODE_NONE:
878 ost << "No error";
879 break;
880 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
881 ost << "No packet received, implementation timed-out";
882 break;
883 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
884 ost << "A stream command was issued in the past";
885 break;
886 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
887 ost << "Expected another stream command";
888 break;
889 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
890 ost << "An internal receive buffer has filled";
891 break;
892 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
893 ost << "The packet could not be parsed";
894 break;
895 default:
896 ost << "Unknown error " << metadata.error_code;
897 }
898
899 if (metadata.has_time_spec)
900 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
901
902 return ost.str();
903}
904
905std::string uhd_device::str_code(uhd::async_metadata_t metadata)
906{
907 std::ostringstream ost("UHD: ");
908
909 switch (metadata.event_code) {
910 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
911 ost << "A packet was successfully transmitted";
912 break;
913 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
914 ost << "An internal send buffer has emptied";
915 break;
916 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
917 ost << "Packet loss between host and device";
918 break;
919 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
920 ost << "Packet time was too late or too early";
921 break;
922 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
923 ost << "Underflow occurred inside a packet";
924 break;
925 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
926 ost << "Packet loss within a burst";
927 break;
928 default:
929 ost << "Unknown error " << metadata.event_code;
930 }
931
932 if (metadata.has_time_spec)
933 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
934
935 return ost.str();
936}
937
938smpl_buf::smpl_buf(size_t len, double rate)
939 : buf_len(len), clk_rt(rate),
940 time_start(0), time_end(0), data_start(0), data_end(0)
941{
942 data = new uint32_t[len];
943}
944
945smpl_buf::~smpl_buf()
946{
947 delete[] data;
948}
949
950ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
951{
952 if (timestamp < time_start)
953 return ERROR_TIMESTAMP;
954 else if (timestamp >= time_end)
955 return 0;
956 else
957 return time_end - timestamp;
958}
959
960ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
961{
962 return avail_smpls(convert_time(timespec, clk_rt));
963}
964
965ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
966{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000967 int type_sz = 2 * sizeof(short);
968
kurtis.heimerl965e7572011-11-26 03:16:54 +0000969 // Check for valid read
970 if (timestamp < time_start)
971 return ERROR_TIMESTAMP;
972 if (timestamp >= time_end)
973 return 0;
974 if (len >= buf_len)
975 return ERROR_READ;
976
977 // How many samples should be copied
978 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +0000979 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000980 num_smpls = len;
981
982 // Starting index
983 size_t read_start = data_start + (timestamp - time_start);
984
985 // Read it
986 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000987 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000988 memcpy(buf, data + read_start, numBytes);
989 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000990 size_t first_cp = (buf_len - read_start) * type_sz;
991 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000992
993 memcpy(buf, data + read_start, first_cp);
994 memcpy((char*) buf + first_cp, data, second_cp);
995 }
996
997 data_start = (read_start + len) % buf_len;
998 time_start = timestamp + len;
999
1000 if (time_start > time_end)
1001 return ERROR_READ;
1002 else
1003 return num_smpls;
1004}
1005
1006ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1007{
1008 return read(buf, len, convert_time(ts, clk_rt));
1009}
1010
1011ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1012{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001013 int type_sz = 2 * sizeof(short);
1014
kurtis.heimerl965e7572011-11-26 03:16:54 +00001015 // Check for valid write
1016 if ((len == 0) || (len >= buf_len))
1017 return ERROR_WRITE;
1018 if ((timestamp + len) <= time_end)
1019 return ERROR_TIMESTAMP;
1020
1021 // Starting index
1022 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1023
1024 // Write it
1025 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001026 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001027 memcpy(data + write_start, buf, numBytes);
1028 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001029 size_t first_cp = (buf_len - write_start) * type_sz;
1030 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001031
1032 memcpy(data + write_start, buf, first_cp);
1033 memcpy(data, (char*) buf + first_cp, second_cp);
1034 }
1035
1036 data_end = (write_start + len) % buf_len;
1037 time_end = timestamp + len;
1038
1039 if (((write_start + len) > buf_len) && (data_end > data_start))
1040 return ERROR_OVERFLOW;
1041 else if (time_end <= time_start)
1042 return ERROR_WRITE;
1043 else
1044 return len;
1045}
1046
1047ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1048{
1049 return write(buf, len, convert_time(ts, clk_rt));
1050}
1051
1052std::string smpl_buf::str_status() const
1053{
1054 std::ostringstream ost("Sample buffer: ");
1055
1056 ost << "length = " << buf_len;
1057 ost << ", time_start = " << time_start;
1058 ost << ", time_end = " << time_end;
1059 ost << ", data_start = " << data_start;
1060 ost << ", data_end = " << data_end;
1061
1062 return ost.str();
1063}
1064
1065std::string smpl_buf::str_code(ssize_t code)
1066{
1067 switch (code) {
1068 case ERROR_TIMESTAMP:
1069 return "Sample buffer: Requested timestamp is not valid";
1070 case ERROR_READ:
1071 return "Sample buffer: Read error";
1072 case ERROR_WRITE:
1073 return "Sample buffer: Write error";
1074 case ERROR_OVERFLOW:
1075 return "Sample buffer: Overrun";
1076 default:
1077 return "Sample buffer: Unknown error";
1078 }
1079}
1080
Thomas Tsoucb69f082013-04-08 14:18:26 -04001081RadioDevice *RadioDevice::make(int sps, bool skip_rx)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001082{
Thomas Tsoucb69f082013-04-08 14:18:26 -04001083 return new uhd_device(sps, skip_rx);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001084}