blob: 4b43ccfc56cdd6a78a7b7db410ce23bfb7fa7a1a [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 Tsoucb69f082013-04-08 14:18:26 -040035#define B100_CLK_RT 52e6
36#define B100_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 Tsouc88d8d52013-08-21 17:55:54 -040045 UMTRX,
Thomas Tsou02d88d12013-04-05 15:36:30 -040046 NUM_USRP_TYPES,
47};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000048
Thomas Tsoue3e88142013-04-05 20:42:41 -040049struct uhd_dev_offset {
50 enum uhd_dev_type type;
51 int sps;
52 double offset;
53};
54
kurtis.heimerl965e7572011-11-26 03:16:54 +000055/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040056 * Tx / Rx sample offset values. In a perfect world, there is no group delay
57 * though analog components, and behaviour through digital filters exactly
58 * matches calculated values. In reality, there are unaccounted factors,
59 * which are captured in these empirically measured (using a loopback test)
60 * timing correction values.
61 *
62 * Notes:
63 * USRP1 with timestamps is not supported by UHD.
64 */
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080065static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
Thomas Tsoue3e88142013-04-05 20:42:41 -040066 { USRP1, 1, 0.0 },
Thomas Tsoue3e88142013-04-05 20:42:41 -040067 { USRP1, 4, 0.0 },
68 { USRP2, 1, 5.4394e-5 },
Thomas Tsoue3e88142013-04-05 20:42:41 -040069 { USRP2, 4, 0.0 },
70 { B100, 1, 9.4778e-5 },
Thomas Tsoue3e88142013-04-05 20:42:41 -040071 { B100, 4, 2.9418e-5 },
Thomas Tsouc88d8d52013-08-21 17:55:54 -040072 { UMTRX, 1, 9.4778e-5 },
Thomas Tsouc88d8d52013-08-21 17:55:54 -040073 { UMTRX, 4, 0.0 },
Thomas Tsoue3e88142013-04-05 20:42:41 -040074};
kurtis.heimerl965e7572011-11-26 03:16:54 +000075
Thomas Tsoue3e88142013-04-05 20:42:41 -040076static double get_dev_offset(enum uhd_dev_type type, int sps)
77{
78 if (type == USRP1) {
79 LOG(ERR) << "Invalid device type";
80 return 0.0;
81 }
kurtis.heimerl965e7572011-11-26 03:16:54 +000082
Thomas Tsoue3e88142013-04-05 20:42:41 -040083 switch (sps) {
84 case 1:
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080085 return uhd_offsets[2 * type + 0].offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -040086 case 4:
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080087 return uhd_offsets[2 * type + 1].offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -040088 }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000089
Thomas Tsoue3e88142013-04-05 20:42:41 -040090 LOG(ERR) << "Unsupported samples-per-symbols: " << sps;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080091 return 0.0;
Thomas Tsoue3e88142013-04-05 20:42:41 -040092}
kurtis.heimerlce317332011-11-26 03:18:39 +000093
Thomas Tsoucb69f082013-04-08 14:18:26 -040094/*
95 * Select sample rate based on device type and requested samples-per-symbol.
96 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
97 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080098 */
Thomas Tsoucb69f082013-04-08 14:18:26 -040099static double select_rate(uhd_dev_type type, int sps)
100{
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800101 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400102 return -9999.99;
103
104 switch (type) {
105 case USRP2:
106 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400107 case B100:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400108 case UMTRX:
109 return GSMRATE * sps;
110 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400111 break;
112 }
113
114 LOG(ALERT) << "Unknown device type " << type;
115 return -9999.99;
116}
117
kurtis.heimerl965e7572011-11-26 03:16:54 +0000118/** Timestamp conversion
119 @param timestamp a UHD or OpenBTS timestamp
120 @param rate sample rate
121 @return the converted timestamp
122*/
123uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
124{
125 double secs = (double) ticks / rate;
126 return uhd::time_spec_t(secs);
127}
128
129TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
130{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +0000131 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000132 return ts.get_tick_count(rate) + ticks;
133}
134
135/*
136 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
137 or UHD style timestamps. Time conversions are handled
138 internally or accessable through the static convert calls.
139*/
140class smpl_buf {
141public:
142 /** Sample buffer constructor
143 @param len number of 32-bit samples the buffer should hold
144 @param rate sample clockrate
145 @param timestamp
146 */
147 smpl_buf(size_t len, double rate);
148 ~smpl_buf();
149
150 /** Query number of samples available for reading
151 @param timestamp time of first sample
152 @return number of available samples or error
153 */
154 ssize_t avail_smpls(TIMESTAMP timestamp) const;
155 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
156
157 /** Read and write
158 @param buf pointer to buffer
159 @param len number of samples desired to read or write
160 @param timestamp time of first stample
161 @return number of actual samples read or written or error
162 */
163 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
164 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
165 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
166 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
167
168 /** Buffer status string
169 @return a formatted string describing internal buffer state
170 */
171 std::string str_status() const;
172
173 /** Formatted error string
174 @param code an error code
175 @return a formatted error string
176 */
177 static std::string str_code(ssize_t code);
178
179 enum err_code {
180 ERROR_TIMESTAMP = -1,
181 ERROR_READ = -2,
182 ERROR_WRITE = -3,
183 ERROR_OVERFLOW = -4
184 };
185
186private:
187 uint32_t *data;
188 size_t buf_len;
189
190 double clk_rt;
191
192 TIMESTAMP time_start;
193 TIMESTAMP time_end;
194
195 size_t data_start;
196 size_t data_end;
197};
198
199/*
200 uhd_device - UHD implementation of the Device interface. Timestamped samples
201 are sent to and received from the device. An intermediate buffer
202 on the receive side collects and aligns packets of samples.
203 Events and errors such as underruns are reported asynchronously
204 by the device and received in a separate thread.
205*/
206class uhd_device : public RadioDevice {
207public:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400208 uhd_device(int sps, bool skip_rx);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000209 ~uhd_device();
210
Thomas Tsoucb69f082013-04-08 14:18:26 -0400211 int open(const std::string &args);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000212 bool start();
213 bool stop();
kurtis.heimerl68292102011-11-26 03:17:28 +0000214 void restart(uhd::time_spec_t ts);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000215 void setPriority();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400216 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000217
218 int readSamples(short *buf, int len, bool *overrun,
219 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
220
221 int writeSamples(short *buf, int len, bool *underrun,
222 TIMESTAMP timestamp, bool isControl);
223
224 bool updateAlignment(TIMESTAMP timestamp);
225
226 bool setTxFreq(double wFreq);
227 bool setRxFreq(double wFreq);
228
229 inline TIMESTAMP initialWriteTimestamp() { return 0; }
230 inline TIMESTAMP initialReadTimestamp() { return 0; }
231
Thomas Tsoue3e88142013-04-05 20:42:41 -0400232 inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000233 inline double fullScaleOutputValue() { return 32000; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000234
kurtis.heimerl02d04052011-11-26 03:17:10 +0000235 double setRxGain(double db);
236 double getRxGain(void) { return rx_gain; }
237 double maxRxGain(void) { return rx_gain_max; }
238 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000239
kurtis.heimerl02d04052011-11-26 03:17:10 +0000240 double setTxGain(double db);
241 double maxTxGain(void) { return tx_gain_max; }
242 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000243
kurtis.heimerl02d04052011-11-26 03:17:10 +0000244 double getTxFreq() { return tx_freq; }
245 double getRxFreq() { return rx_freq; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000246
247 inline double getSampleRate() { return actual_smpl_rt; }
248 inline double numberRead() { return rx_pkt_cnt; }
249 inline double numberWritten() { return 0; }
250
251 /** Receive and process asynchronous message
252 @return true if message received or false on timeout or error
253 */
254 bool recv_async_msg();
255
kurtis.heimerld4be0742011-11-26 03:17:46 +0000256 enum err_code {
257 ERROR_TIMING = -1,
258 ERROR_UNRECOVERABLE = -2,
259 ERROR_UNHANDLED = -3,
260 };
261
kurtis.heimerl965e7572011-11-26 03:16:54 +0000262private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000263 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400264 uhd::tx_streamer::sptr tx_stream;
265 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400266 enum TxWindowType tx_window;
267 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000268
Thomas Tsoue3e88142013-04-05 20:42:41 -0400269 int sps;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000270 double desired_smpl_rt, actual_smpl_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000271
kurtis.heimerl02d04052011-11-26 03:17:10 +0000272 double tx_gain, tx_gain_min, tx_gain_max;
273 double rx_gain, rx_gain_min, rx_gain_max;
274
275 double tx_freq, rx_freq;
276 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000277
278 bool started;
279 bool aligned;
280 bool skip_rx;
281
282 size_t rx_pkt_cnt;
283 size_t drop_cnt;
284 uhd::time_spec_t prev_ts;
285
286 TIMESTAMP ts_offset;
287 smpl_buf *rx_smpl_buf;
288
kurtis.heimerl02d04052011-11-26 03:17:10 +0000289 void init_gains();
kurtis.heimerl24481de2011-11-26 03:17:18 +0000290 void set_ref_clk(bool ext_clk);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400291 int set_master_clk(double rate);
292 int set_rates(double rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000293 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000294 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000295 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000296
kurtis.heimerl965e7572011-11-26 03:16:54 +0000297 std::string str_code(uhd::rx_metadata_t metadata);
298 std::string str_code(uhd::async_metadata_t metadata);
299
300 Thread async_event_thrd;
301};
302
303void *async_event_loop(uhd_device *dev)
304{
305 while (1) {
306 dev->recv_async_msg();
307 pthread_testcancel();
308 }
309}
310
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000311/*
312 Catch and drop underrun 'U' and overrun 'O' messages from stdout
313 since we already report using the logging facility. Direct
314 everything else appropriately.
315 */
316void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
317{
318 switch (type) {
319 case uhd::msg::status:
320 LOG(INFO) << msg;
321 break;
322 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000323 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000324 break;
325 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000326 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000327 break;
328 case uhd::msg::fastpath:
329 break;
330 }
331}
332
Thomas Tsoucb69f082013-04-08 14:18:26 -0400333uhd_device::uhd_device(int sps, bool skip_rx)
334 : tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0),
kurtis.heimerl02d04052011-11-26 03:17:10 +0000335 rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0),
336 tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000337 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
kurtis.heimerl965e7572011-11-26 03:16:54 +0000338 prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
339{
Thomas Tsoue3e88142013-04-05 20:42:41 -0400340 this->sps = sps;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000341 this->skip_rx = skip_rx;
342}
343
344uhd_device::~uhd_device()
345{
346 stop();
347
348 if (rx_smpl_buf)
349 delete rx_smpl_buf;
350}
351
kurtis.heimerl24481de2011-11-26 03:17:18 +0000352void uhd_device::init_gains()
353{
354 uhd::gain_range_t range;
355
356 range = usrp_dev->get_tx_gain_range();
357 tx_gain_min = range.start();
358 tx_gain_max = range.stop();
359
360 range = usrp_dev->get_rx_gain_range();
361 rx_gain_min = range.start();
362 rx_gain_max = range.stop();
363
364 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
365 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
366
367 tx_gain = usrp_dev->get_tx_gain();
368 rx_gain = usrp_dev->get_rx_gain();
369
370 return;
371}
372
373void uhd_device::set_ref_clk(bool ext_clk)
374{
kurtis.heimerl24481de2011-11-26 03:17:18 +0000375 if (ext_clk)
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400376 usrp_dev->set_clock_source("external");
kurtis.heimerl24481de2011-11-26 03:17:18 +0000377
378 return;
379}
380
Thomas Tsou02d88d12013-04-05 15:36:30 -0400381int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000382{
Thomas Tsou02d88d12013-04-05 15:36:30 -0400383 double actual_clk_rt;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000384
Thomas Tsou7e068062013-04-08 19:39:37 -0400385 try {
386 usrp_dev->set_master_clock_rate(clk_rate);
387 actual_clk_rt = usrp_dev->get_master_clock_rate();
388 } catch (const std::exception &ex) {
389 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
390 LOG(ALERT) << ex.what();
391 return -1;
392 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000393
Thomas Tsou02d88d12013-04-05 15:36:30 -0400394 if (actual_clk_rt != clk_rate) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000395 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400396 LOG(ALERT) << "Requested clock rate " << clk_rate;
kurtis.heimerle3320322011-11-28 06:26:08 +0000397 LOG(ALERT) << "Actual clock rate " << actual_clk_rt;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400398 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000399 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400400
401 return 0;
402}
403
404int uhd_device::set_rates(double rate)
405{
Thomas Tsoucb69f082013-04-08 14:18:26 -0400406 double offset_limit = 10.0;
407 double tx_offset, rx_offset;
408
Thomas Tsou02d88d12013-04-05 15:36:30 -0400409 // B100 is the only device where we set FPGA clocking
410 if (dev_type == B100) {
411 if (set_master_clk(B100_CLK_RT) < 0)
412 return -1;
413 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000414
415 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400416 try {
417 usrp_dev->set_tx_rate(rate);
418 usrp_dev->set_rx_rate(rate);
419 } catch (const std::exception &ex) {
420 LOG(ALERT) << "UHD rate setting failed: " << rate;
421 LOG(ALERT) << ex.what();
422 return -1;
423 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400424 actual_smpl_rt = usrp_dev->get_tx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000425
Thomas Tsoucb69f082013-04-08 14:18:26 -0400426 tx_offset = actual_smpl_rt - rate;
427 rx_offset = usrp_dev->get_rx_rate() - rate;
428 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000429 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400430 LOG(ALERT) << "Tx/Rx (" << actual_smpl_rt << "/"
431 << usrp_dev->get_rx_rate() << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400432 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000433 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000434
Thomas Tsou02d88d12013-04-05 15:36:30 -0400435 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000436}
437
kurtis.heimerl02d04052011-11-26 03:17:10 +0000438double uhd_device::setTxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000439{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000440 usrp_dev->set_tx_gain(db);
441 tx_gain = usrp_dev->get_tx_gain();
442
443 LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
444
445 return tx_gain;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000446}
447
kurtis.heimerl02d04052011-11-26 03:17:10 +0000448double uhd_device::setRxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000449{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000450 usrp_dev->set_rx_gain(db);
451 rx_gain = usrp_dev->get_rx_gain();
452
453 LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
454
455 return rx_gain;
456}
457
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000458/*
459 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400460 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000461 deal with the transport latency. Reject the USRP1 because UHD doesn't
462 support timestamped samples with it.
463 */
464bool uhd_device::parse_dev_type()
465{
466 std::string mboard_str, dev_str;
467 uhd::property_tree::sptr prop_tree;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400468 size_t usrp1_str, usrp2_str, b100_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000469
470 prop_tree = usrp_dev->get_device()->get_tree();
471 dev_str = prop_tree->access<std::string>("/name").get();
472 mboard_str = usrp_dev->get_mboard_name();
473
474 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400475 usrp2_str = dev_str.find("USRP2");
476 b100_str = mboard_str.find("B100");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400477 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000478
479 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000480 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
481 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400482 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000483 return false;
484 }
485
Thomas Tsoucb69f082013-04-08 14:18:26 -0400486 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400487 tx_window = TX_WINDOW_USRP1;
488 LOG(INFO) << "Using USRP1 type transmit window for "
489 << dev_str << " " << mboard_str;
490 dev_type = B100;
491 return true;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400492 } else if (usrp2_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400493 dev_type = USRP2;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400494 } else if (umtrx_str != std::string::npos) {
495 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400496 } else {
497 LOG(ALERT) << "Unknown UHD device type";
498 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000499 }
500
Thomas Tsou02d88d12013-04-05 15:36:30 -0400501 tx_window = TX_WINDOW_FIXED;
502 LOG(INFO) << "Using fixed transmit window for "
503 << dev_str << " " << mboard_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000504 return true;
505}
506
Thomas Tsoucb69f082013-04-08 14:18:26 -0400507int uhd_device::open(const std::string &args)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000508{
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000509 // Register msg handler
510 uhd::msg::register_handler(&uhd_msg_handler);
511
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000512 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000513 uhd::device_addr_t addr(args);
514 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000515 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000516 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400517 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000518 }
519
520 // Use the first found device
521 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000522 try {
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000523 usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
kurtis.heimerle380af32011-11-26 03:18:55 +0000524 } catch(...) {
ttsou3b5c0c12012-02-14 17:58:11 +0000525 LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400526 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000527 }
528
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000529 // Check for a valid device type and set bus type
530 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400531 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000532
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000533#ifdef EXTREF
534 set_ref_clk(true);
535#endif
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400536 // Create TX and RX streamers
537 uhd::stream_args_t stream_args("sc16");
538 tx_stream = usrp_dev->get_tx_stream(stream_args);
539 rx_stream = usrp_dev->get_rx_stream(stream_args);
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000540
kurtis.heimerl965e7572011-11-26 03:16:54 +0000541 // Number of samples per over-the-wire packet
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400542 tx_spp = tx_stream->get_max_num_samps();
543 rx_spp = rx_stream->get_max_num_samps();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000544
545 // Set rates
Thomas Tsoucb69f082013-04-08 14:18:26 -0400546 desired_smpl_rt = select_rate(dev_type, sps);
Thomas Tsou84c60f62013-08-22 18:26:06 -0400547 if ((desired_smpl_rt > 0.0) && (set_rates(desired_smpl_rt) < 0))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400548 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000549
550 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400551 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000552 rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
553
554 // Set receive chain sample offset
Thomas Tsoue3e88142013-04-05 20:42:41 -0400555 double offset = get_dev_offset(dev_type, sps);
556 if (offset == 0.0) {
557 LOG(ERR) << "Unsupported configuration, no correction applied";
558 ts_offset = 0;
559 } else {
560 ts_offset = (TIMESTAMP) (offset * actual_smpl_rt);
561 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000562
kurtis.heimerl02d04052011-11-26 03:17:10 +0000563 // Initialize and shadow gain values
564 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000565
kurtis.heimerl965e7572011-11-26 03:16:54 +0000566 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000567 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000568
Thomas Tsoucb69f082013-04-08 14:18:26 -0400569 if (dev_type == USRP2)
570 return RESAMP;
571
572 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000573}
574
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000575bool uhd_device::flush_recv(size_t num_pkts)
576{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000577 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000578 size_t num_smpls;
579 uint32_t buff[rx_spp];
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000580 float timeout;
581
582 // Use .01 sec instead of the default .1 sec
583 timeout = .01;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000584
585 for (size_t i = 0; i < num_pkts; i++) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400586 num_smpls = rx_stream->recv(buff, rx_spp, md,
587 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000588 if (!num_smpls) {
589 switch (md.error_code) {
590 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
591 return true;
592 default:
593 continue;
594 }
595 }
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000596 }
597
598 return true;
599}
600
kurtis.heimerl68292102011-11-26 03:17:28 +0000601void uhd_device::restart(uhd::time_spec_t ts)
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000602{
kurtis.heimerl68292102011-11-26 03:17:28 +0000603 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
604 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000605
kurtis.heimerl68292102011-11-26 03:17:28 +0000606 flush_recv(50);
607
608 usrp_dev->set_time_now(ts);
609 aligned = false;
610
611 cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
612 cmd.stream_now = true;
613 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000614}
615
kurtis.heimerl965e7572011-11-26 03:16:54 +0000616bool uhd_device::start()
617{
618 LOG(INFO) << "Starting USRP...";
619
620 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000621 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000622 return false;
623 }
624
625 setPriority();
626
627 // Start asynchronous event (underrun check) loop
628 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
629
630 // Start streaming
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000631 restart(uhd::time_spec_t(0.0));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000632
kurtis.heimerl965e7572011-11-26 03:16:54 +0000633 // Display usrp time
634 double time_now = usrp_dev->get_time_now().get_real_secs();
635 LOG(INFO) << "The current time is " << time_now << " seconds";
636
637 started = true;
638 return true;
639}
640
641bool uhd_device::stop()
642{
643 uhd::stream_cmd_t stream_cmd =
644 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
645
646 usrp_dev->issue_stream_cmd(stream_cmd);
647
648 started = false;
649 return true;
650}
651
652void uhd_device::setPriority()
653{
654 uhd::set_thread_priority_safe();
655 return;
656}
657
kurtis.heimerld4be0742011-11-26 03:17:46 +0000658int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000659{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000660 uhd::time_spec_t ts;
661
kurtis.heimerld4be0742011-11-26 03:17:46 +0000662 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000663 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000664
665 switch (md.error_code) {
666 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000667 LOG(ALERT) << "UHD: Receive timed out";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000668 return ERROR_UNRECOVERABLE;
669 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
670 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
671 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
672 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
673 default:
674 return ERROR_UNHANDLED;
675 }
676 }
677
kurtis.heimerl965e7572011-11-26 03:16:54 +0000678 // Missing timestamp
679 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000680 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000681 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000682 }
683
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000684 ts = md.time_spec;
685
kurtis.heimerl965e7572011-11-26 03:16:54 +0000686 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000687 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000688 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000689 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
690 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000691 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000692 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000693 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000694 }
695
696 return 0;
697}
698
699int uhd_device::readSamples(short *buf, int len, bool *overrun,
700 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
701{
702 ssize_t rc;
703 uhd::time_spec_t ts;
704 uhd::rx_metadata_t metadata;
705 uint32_t pkt_buf[rx_spp];
706
707 if (skip_rx)
708 return 0;
709
710 // Shift read time with respect to transmit clock
711 timestamp += ts_offset;
712
713 ts = convert_time(timestamp, actual_smpl_rt);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000714 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000715
716 // Check that timestamp is valid
717 rc = rx_smpl_buf->avail_smpls(timestamp);
718 if (rc < 0) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000719 LOG(ERR) << rx_smpl_buf->str_code(rc);
720 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000721 return 0;
722 }
723
724 // Receive samples from the usrp until we have enough
725 while (rx_smpl_buf->avail_smpls(timestamp) < len) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400726 size_t num_smpls = rx_stream->recv(
kurtis.heimerl965e7572011-11-26 03:16:54 +0000727 (void*)pkt_buf,
728 rx_spp,
729 metadata,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400730 0.1,
731 true);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000732 rx_pkt_cnt++;
733
kurtis.heimerld4be0742011-11-26 03:17:46 +0000734 // Check for errors
735 rc = check_rx_md_err(metadata, num_smpls);
736 switch (rc) {
737 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000738 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
739 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000740 exit(-1);
741 case ERROR_TIMING:
742 restart(prev_ts);
743 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000744 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000745 }
746
kurtis.heimerl965e7572011-11-26 03:16:54 +0000747
748 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000749 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000750
751 rc = rx_smpl_buf->write(pkt_buf,
752 num_smpls,
753 metadata.time_spec);
754
755 // Continue on local overrun, exit on other errors
756 if ((rc < 0)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000757 LOG(ERR) << rx_smpl_buf->str_code(rc);
758 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000759 if (rc != smpl_buf::ERROR_OVERFLOW)
760 return 0;
761 }
762 }
763
764 // We have enough samples
765 rc = rx_smpl_buf->read(buf, len, timestamp);
766 if ((rc < 0) || (rc != len)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000767 LOG(ERR) << rx_smpl_buf->str_code(rc);
768 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000769 return 0;
770 }
771
772 return len;
773}
774
775int uhd_device::writeSamples(short *buf, int len, bool *underrun,
776 unsigned long long timestamp,bool isControl)
777{
778 uhd::tx_metadata_t metadata;
779 metadata.has_time_spec = true;
780 metadata.start_of_burst = false;
781 metadata.end_of_burst = false;
782 metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
783
784 // No control packets
785 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000786 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000787 return 0;
788 }
789
790 // Drop a fixed number of packets (magic value)
791 if (!aligned) {
792 drop_cnt++;
793
794 if (drop_cnt == 1) {
795 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000796 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000797 metadata.end_of_burst = true;
798 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000799 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000800 return len;
801 } else {
802 LOG(DEBUG) << "Aligning transmitter: start burst";
803 metadata.start_of_burst = true;
804 aligned = true;
805 drop_cnt = 0;
806 }
807 }
808
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400809 size_t num_smpls = tx_stream->send(buf, len, metadata);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000810
ttsoub371ed52012-01-09 18:11:34 +0000811 if (num_smpls != (unsigned) len) {
812 LOG(ALERT) << "UHD: Device send timed out";
813 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
814 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
815 exit(-1);
816 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000817
818 return num_smpls;
819}
820
821bool uhd_device::updateAlignment(TIMESTAMP timestamp)
822{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000823 return true;
824}
825
826bool uhd_device::setTxFreq(double wFreq)
827{
828 uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000829 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000830 tx_freq = usrp_dev->get_tx_freq();
831
kurtis.heimerl965e7572011-11-26 03:16:54 +0000832 return true;
833}
834
835bool uhd_device::setRxFreq(double wFreq)
836{
837 uhd::tune_result_t tr = usrp_dev->set_rx_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 rx_freq = usrp_dev->get_rx_freq();
840
kurtis.heimerl965e7572011-11-26 03:16:54 +0000841 return true;
842}
843
844bool uhd_device::recv_async_msg()
845{
ttsou60dc4c92012-08-08 23:30:23 +0000846 uhd::async_metadata_t md;
847 if (!usrp_dev->get_device()->recv_async_msg(md))
kurtis.heimerl965e7572011-11-26 03:16:54 +0000848 return false;
849
850 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +0000851 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +0000852 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +0000853
854 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
855 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
856 LOG(ERR) << str_code(md);
857 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000858 }
859
860 return true;
861}
862
863std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
864{
865 std::ostringstream ost("UHD: ");
866
867 switch (metadata.error_code) {
868 case uhd::rx_metadata_t::ERROR_CODE_NONE:
869 ost << "No error";
870 break;
871 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
872 ost << "No packet received, implementation timed-out";
873 break;
874 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
875 ost << "A stream command was issued in the past";
876 break;
877 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
878 ost << "Expected another stream command";
879 break;
880 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
881 ost << "An internal receive buffer has filled";
882 break;
883 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
884 ost << "The packet could not be parsed";
885 break;
886 default:
887 ost << "Unknown error " << metadata.error_code;
888 }
889
890 if (metadata.has_time_spec)
891 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
892
893 return ost.str();
894}
895
896std::string uhd_device::str_code(uhd::async_metadata_t metadata)
897{
898 std::ostringstream ost("UHD: ");
899
900 switch (metadata.event_code) {
901 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
902 ost << "A packet was successfully transmitted";
903 break;
904 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
905 ost << "An internal send buffer has emptied";
906 break;
907 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
908 ost << "Packet loss between host and device";
909 break;
910 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
911 ost << "Packet time was too late or too early";
912 break;
913 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
914 ost << "Underflow occurred inside a packet";
915 break;
916 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
917 ost << "Packet loss within a burst";
918 break;
919 default:
920 ost << "Unknown error " << metadata.event_code;
921 }
922
923 if (metadata.has_time_spec)
924 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
925
926 return ost.str();
927}
928
929smpl_buf::smpl_buf(size_t len, double rate)
930 : buf_len(len), clk_rt(rate),
931 time_start(0), time_end(0), data_start(0), data_end(0)
932{
933 data = new uint32_t[len];
934}
935
936smpl_buf::~smpl_buf()
937{
938 delete[] data;
939}
940
941ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
942{
943 if (timestamp < time_start)
944 return ERROR_TIMESTAMP;
945 else if (timestamp >= time_end)
946 return 0;
947 else
948 return time_end - timestamp;
949}
950
951ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
952{
953 return avail_smpls(convert_time(timespec, clk_rt));
954}
955
956ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
957{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000958 int type_sz = 2 * sizeof(short);
959
kurtis.heimerl965e7572011-11-26 03:16:54 +0000960 // Check for valid read
961 if (timestamp < time_start)
962 return ERROR_TIMESTAMP;
963 if (timestamp >= time_end)
964 return 0;
965 if (len >= buf_len)
966 return ERROR_READ;
967
968 // How many samples should be copied
969 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +0000970 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000971 num_smpls = len;
972
973 // Starting index
974 size_t read_start = data_start + (timestamp - time_start);
975
976 // Read it
977 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000978 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000979 memcpy(buf, data + read_start, numBytes);
980 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000981 size_t first_cp = (buf_len - read_start) * type_sz;
982 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000983
984 memcpy(buf, data + read_start, first_cp);
985 memcpy((char*) buf + first_cp, data, second_cp);
986 }
987
988 data_start = (read_start + len) % buf_len;
989 time_start = timestamp + len;
990
991 if (time_start > time_end)
992 return ERROR_READ;
993 else
994 return num_smpls;
995}
996
997ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
998{
999 return read(buf, len, convert_time(ts, clk_rt));
1000}
1001
1002ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1003{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001004 int type_sz = 2 * sizeof(short);
1005
kurtis.heimerl965e7572011-11-26 03:16:54 +00001006 // Check for valid write
1007 if ((len == 0) || (len >= buf_len))
1008 return ERROR_WRITE;
1009 if ((timestamp + len) <= time_end)
1010 return ERROR_TIMESTAMP;
1011
1012 // Starting index
1013 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1014
1015 // Write it
1016 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001017 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001018 memcpy(data + write_start, buf, numBytes);
1019 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001020 size_t first_cp = (buf_len - write_start) * type_sz;
1021 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001022
1023 memcpy(data + write_start, buf, first_cp);
1024 memcpy(data, (char*) buf + first_cp, second_cp);
1025 }
1026
1027 data_end = (write_start + len) % buf_len;
1028 time_end = timestamp + len;
1029
1030 if (((write_start + len) > buf_len) && (data_end > data_start))
1031 return ERROR_OVERFLOW;
1032 else if (time_end <= time_start)
1033 return ERROR_WRITE;
1034 else
1035 return len;
1036}
1037
1038ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1039{
1040 return write(buf, len, convert_time(ts, clk_rt));
1041}
1042
1043std::string smpl_buf::str_status() const
1044{
1045 std::ostringstream ost("Sample buffer: ");
1046
1047 ost << "length = " << buf_len;
1048 ost << ", time_start = " << time_start;
1049 ost << ", time_end = " << time_end;
1050 ost << ", data_start = " << data_start;
1051 ost << ", data_end = " << data_end;
1052
1053 return ost.str();
1054}
1055
1056std::string smpl_buf::str_code(ssize_t code)
1057{
1058 switch (code) {
1059 case ERROR_TIMESTAMP:
1060 return "Sample buffer: Requested timestamp is not valid";
1061 case ERROR_READ:
1062 return "Sample buffer: Read error";
1063 case ERROR_WRITE:
1064 return "Sample buffer: Write error";
1065 case ERROR_OVERFLOW:
1066 return "Sample buffer: Overrun";
1067 default:
1068 return "Sample buffer: Unknown error";
1069 }
1070}
1071
Thomas Tsoucb69f082013-04-08 14:18:26 -04001072RadioDevice *RadioDevice::make(int sps, bool skip_rx)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001073{
Thomas Tsoucb69f082013-04-08 14:18:26 -04001074 return new uhd_device(sps, skip_rx);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001075}