blob: cc90d732b536fcc5580ac611c847fe0058b05e3b [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"
25#include <uhd/usrp/single_usrp.hpp>
26#include <uhd/utils/thread_priority.hpp>
27
kurtis.heimerlce317332011-11-26 03:18:39 +000028#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
kurtis.heimerl965e7572011-11-26 03:16:54 +000032/*
33 use_ext_ref - Enable external 10MHz clock reference
34
kurtis.heimerlce317332011-11-26 03:18:39 +000035 master_clk_rt - Master clock frequency - ignored if host resampling is
36 enabled
kurtis.heimerl965e7572011-11-26 03:16:54 +000037
38 rx_smpl_offset - Timing correction in seconds between receive and
39 transmit timestamps. This value corrects for delays on
40 on the RF side of the timestamping point of the device.
kurtis.heimerlbe6abe12011-11-26 03:17:05 +000041 This value is generally empirically measured.
kurtis.heimerl965e7572011-11-26 03:16:54 +000042
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000043 smpl_buf_sz - The receive sample buffer size in bytes.
44
45 tx_ampl - Transmit amplitude must be between 0 and 1.0
kurtis.heimerl965e7572011-11-26 03:16:54 +000046*/
47const bool use_ext_ref = false;
48const double master_clk_rt = 52e6;
kurtis.heimerl965e7572011-11-26 03:16:54 +000049const size_t smpl_buf_sz = (1 << 20);
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000050const float tx_ampl = .3;
kurtis.heimerl965e7572011-11-26 03:16:54 +000051
kurtis.heimerlce317332011-11-26 03:18:39 +000052#ifdef RESAMPLE
53const double rx_smpl_offset = .00005;
54#else
55const double rx_smpl_offset = .0000869;
56#endif
57
kurtis.heimerl965e7572011-11-26 03:16:54 +000058/** Timestamp conversion
59 @param timestamp a UHD or OpenBTS timestamp
60 @param rate sample rate
61 @return the converted timestamp
62*/
63uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
64{
65 double secs = (double) ticks / rate;
66 return uhd::time_spec_t(secs);
67}
68
69TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
70{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +000071 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +000072 return ts.get_tick_count(rate) + ticks;
73}
74
75/*
76 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
77 or UHD style timestamps. Time conversions are handled
78 internally or accessable through the static convert calls.
79*/
80class smpl_buf {
81public:
82 /** Sample buffer constructor
83 @param len number of 32-bit samples the buffer should hold
84 @param rate sample clockrate
85 @param timestamp
86 */
87 smpl_buf(size_t len, double rate);
88 ~smpl_buf();
89
90 /** Query number of samples available for reading
91 @param timestamp time of first sample
92 @return number of available samples or error
93 */
94 ssize_t avail_smpls(TIMESTAMP timestamp) const;
95 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
96
97 /** Read and write
98 @param buf pointer to buffer
99 @param len number of samples desired to read or write
100 @param timestamp time of first stample
101 @return number of actual samples read or written or error
102 */
103 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
104 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
105 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
106 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
107
108 /** Buffer status string
109 @return a formatted string describing internal buffer state
110 */
111 std::string str_status() const;
112
113 /** Formatted error string
114 @param code an error code
115 @return a formatted error string
116 */
117 static std::string str_code(ssize_t code);
118
119 enum err_code {
120 ERROR_TIMESTAMP = -1,
121 ERROR_READ = -2,
122 ERROR_WRITE = -3,
123 ERROR_OVERFLOW = -4
124 };
125
126private:
127 uint32_t *data;
128 size_t buf_len;
129
130 double clk_rt;
131
132 TIMESTAMP time_start;
133 TIMESTAMP time_end;
134
135 size_t data_start;
136 size_t data_end;
137};
138
139/*
140 uhd_device - UHD implementation of the Device interface. Timestamped samples
141 are sent to and received from the device. An intermediate buffer
142 on the receive side collects and aligns packets of samples.
143 Events and errors such as underruns are reported asynchronously
144 by the device and received in a separate thread.
145*/
146class uhd_device : public RadioDevice {
147public:
148 uhd_device(double rate, bool skip_rx);
149 ~uhd_device();
150
151 bool open();
152 bool start();
153 bool stop();
kurtis.heimerl68292102011-11-26 03:17:28 +0000154 void restart(uhd::time_spec_t ts);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000155 void setPriority();
156
157 int readSamples(short *buf, int len, bool *overrun,
158 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
159
160 int writeSamples(short *buf, int len, bool *underrun,
161 TIMESTAMP timestamp, bool isControl);
162
163 bool updateAlignment(TIMESTAMP timestamp);
164
165 bool setTxFreq(double wFreq);
166 bool setRxFreq(double wFreq);
167
168 inline TIMESTAMP initialWriteTimestamp() { return 0; }
169 inline TIMESTAMP initialReadTimestamp() { return 0; }
170
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000171 inline double fullScaleInputValue() { return 32000 * tx_ampl; }
172 inline double fullScaleOutputValue() { return 32000; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000173
kurtis.heimerl02d04052011-11-26 03:17:10 +0000174 double setRxGain(double db);
175 double getRxGain(void) { return rx_gain; }
176 double maxRxGain(void) { return rx_gain_max; }
177 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000178
kurtis.heimerl02d04052011-11-26 03:17:10 +0000179 double setTxGain(double db);
180 double maxTxGain(void) { return tx_gain_max; }
181 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000182
kurtis.heimerl02d04052011-11-26 03:17:10 +0000183 double getTxFreq() { return tx_freq; }
184 double getRxFreq() { return rx_freq; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000185
186 inline double getSampleRate() { return actual_smpl_rt; }
187 inline double numberRead() { return rx_pkt_cnt; }
188 inline double numberWritten() { return 0; }
189
190 /** Receive and process asynchronous message
191 @return true if message received or false on timeout or error
192 */
193 bool recv_async_msg();
194
kurtis.heimerld4be0742011-11-26 03:17:46 +0000195 enum err_code {
196 ERROR_TIMING = -1,
197 ERROR_UNRECOVERABLE = -2,
198 ERROR_UNHANDLED = -3,
199 };
200
kurtis.heimerl965e7572011-11-26 03:16:54 +0000201private:
202 uhd::usrp::single_usrp::sptr usrp_dev;
203
kurtis.heimerl02d04052011-11-26 03:17:10 +0000204 double desired_smpl_rt, actual_smpl_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000205
kurtis.heimerl02d04052011-11-26 03:17:10 +0000206 double tx_gain, tx_gain_min, tx_gain_max;
207 double rx_gain, rx_gain_min, rx_gain_max;
208
209 double tx_freq, rx_freq;
210 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000211
212 bool started;
213 bool aligned;
214 bool skip_rx;
215
216 size_t rx_pkt_cnt;
217 size_t drop_cnt;
218 uhd::time_spec_t prev_ts;
219
220 TIMESTAMP ts_offset;
221 smpl_buf *rx_smpl_buf;
222
kurtis.heimerl02d04052011-11-26 03:17:10 +0000223 void init_gains();
kurtis.heimerl24481de2011-11-26 03:17:18 +0000224 void set_ref_clk(bool ext_clk);
225 double set_rates(double rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000226 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000227 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000228
kurtis.heimerl965e7572011-11-26 03:16:54 +0000229 std::string str_code(uhd::rx_metadata_t metadata);
230 std::string str_code(uhd::async_metadata_t metadata);
231
232 Thread async_event_thrd;
233};
234
235void *async_event_loop(uhd_device *dev)
236{
237 while (1) {
238 dev->recv_async_msg();
239 pthread_testcancel();
240 }
241}
242
243uhd_device::uhd_device(double rate, bool skip_rx)
kurtis.heimerl02d04052011-11-26 03:17:10 +0000244 : desired_smpl_rt(rate), actual_smpl_rt(0),
245 tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0),
246 rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0),
247 tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000248 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
kurtis.heimerl965e7572011-11-26 03:16:54 +0000249 prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
250{
251 this->skip_rx = skip_rx;
252}
253
254uhd_device::~uhd_device()
255{
256 stop();
257
258 if (rx_smpl_buf)
259 delete rx_smpl_buf;
260}
261
kurtis.heimerl24481de2011-11-26 03:17:18 +0000262void uhd_device::init_gains()
263{
264 uhd::gain_range_t range;
265
266 range = usrp_dev->get_tx_gain_range();
267 tx_gain_min = range.start();
268 tx_gain_max = range.stop();
269
270 range = usrp_dev->get_rx_gain_range();
271 rx_gain_min = range.start();
272 rx_gain_max = range.stop();
273
274 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
275 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
276
277 tx_gain = usrp_dev->get_tx_gain();
278 rx_gain = usrp_dev->get_rx_gain();
279
280 return;
281}
282
283void uhd_device::set_ref_clk(bool ext_clk)
284{
285 uhd::clock_config_t clk_cfg;
286
287 clk_cfg.pps_source = uhd::clock_config_t::PPS_SMA;
kurtis.heimerl24481de2011-11-26 03:17:18 +0000288
289 if (ext_clk)
290 clk_cfg.ref_source = uhd::clock_config_t::REF_SMA;
291 else
292 clk_cfg.ref_source = uhd::clock_config_t::REF_INT;
293
294 usrp_dev->set_clock_config(clk_cfg);
295
296 return;
297}
298
299double uhd_device::set_rates(double rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000300{
kurtis.heimerld3e25902011-11-26 03:18:13 +0000301 double actual_rt, actual_clk_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000302
kurtis.heimerlce317332011-11-26 03:18:39 +0000303#ifndef RESAMPLE
kurtis.heimerld3e25902011-11-26 03:18:13 +0000304 // Set master clock rate
305 usrp_dev->set_master_clock_rate(master_clk_rt);
306 actual_clk_rt = usrp_dev->get_master_clock_rate();
307
308 if (actual_clk_rt != master_clk_rt) {
309 LOG(ERROR) << "Failed to set master clock rate";
310 return -1.0;
311 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000312#endif
kurtis.heimerld3e25902011-11-26 03:18:13 +0000313
314 // Set sample rates
kurtis.heimerl24481de2011-11-26 03:17:18 +0000315 usrp_dev->set_tx_rate(rate);
316 usrp_dev->set_rx_rate(rate);
kurtis.heimerld3e25902011-11-26 03:18:13 +0000317 actual_rt = usrp_dev->get_tx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000318
kurtis.heimerld3e25902011-11-26 03:18:13 +0000319 if (actual_rt != rate) {
kurtis.heimerl965e7572011-11-26 03:16:54 +0000320 LOG(ERROR) << "Actual sample rate differs from desired rate";
321 return -1.0;
322 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000323 if (usrp_dev->get_rx_rate() != actual_rt) {
kurtis.heimerl965e7572011-11-26 03:16:54 +0000324 LOG(ERROR) << "Transmit and receive sample rates do not match";
325 return -1.0;
326 }
327
kurtis.heimerld3e25902011-11-26 03:18:13 +0000328 return actual_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000329}
330
kurtis.heimerl02d04052011-11-26 03:17:10 +0000331double uhd_device::setTxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000332{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000333 usrp_dev->set_tx_gain(db);
334 tx_gain = usrp_dev->get_tx_gain();
335
336 LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
337
338 return tx_gain;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000339}
340
kurtis.heimerl02d04052011-11-26 03:17:10 +0000341double uhd_device::setRxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000342{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000343 usrp_dev->set_rx_gain(db);
344 rx_gain = usrp_dev->get_rx_gain();
345
346 LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
347
348 return rx_gain;
349}
350
kurtis.heimerl965e7572011-11-26 03:16:54 +0000351bool uhd_device::open()
352{
353 LOG(INFO) << "creating USRP device...";
354
kurtis.heimerl2b28c692011-11-26 03:18:08 +0000355 // Allow all UHD devices
356 uhd::device_addr_t dev_addr("");
kurtis.heimerl965e7572011-11-26 03:16:54 +0000357 try {
358 usrp_dev = uhd::usrp::single_usrp::make(dev_addr);
359 }
360
361 catch(...) {
362 LOG(ERROR) << "USRP make failed";
363 return false;
364 }
365
kurtis.heimerl965e7572011-11-26 03:16:54 +0000366 // Number of samples per over-the-wire packet
367 tx_spp = usrp_dev->get_device()->get_max_send_samps_per_packet();
368 rx_spp = usrp_dev->get_device()->get_max_recv_samps_per_packet();
369
370 // Set rates
kurtis.heimerl24481de2011-11-26 03:17:18 +0000371 actual_smpl_rt = set_rates(desired_smpl_rt);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000372 if (actual_smpl_rt < 0)
373 return false;
374
375 // Create receive buffer
376 size_t buf_len = smpl_buf_sz / sizeof(uint32_t);
377 rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
378
379 // Set receive chain sample offset
380 ts_offset = (TIMESTAMP)(rx_smpl_offset * actual_smpl_rt);
381
kurtis.heimerl02d04052011-11-26 03:17:10 +0000382 // Initialize and shadow gain values
383 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000384
385 // Set reference clock
kurtis.heimerl24481de2011-11-26 03:17:18 +0000386 set_ref_clk(use_ext_ref);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000387
388 // Print configuration
389 LOG(INFO) << usrp_dev->get_pp_string();
390
391 return true;
392}
393
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000394bool uhd_device::flush_recv(size_t num_pkts)
395{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000396 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000397 size_t num_smpls;
398 uint32_t buff[rx_spp];
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000399 float timeout;
400
401 // Use .01 sec instead of the default .1 sec
402 timeout = .01;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000403
404 for (size_t i = 0; i < num_pkts; i++) {
405 num_smpls = usrp_dev->get_device()->recv(
406 buff,
407 rx_spp,
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000408 md,
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000409 uhd::io_type_t::COMPLEX_INT16,
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000410 uhd::device::RECV_MODE_ONE_PACKET,
411 timeout);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000412
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000413 if (!num_smpls) {
414 switch (md.error_code) {
415 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
416 return true;
417 default:
418 continue;
419 }
420 }
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000421 }
422
423 return true;
424}
425
kurtis.heimerl68292102011-11-26 03:17:28 +0000426void uhd_device::restart(uhd::time_spec_t ts)
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000427{
kurtis.heimerl68292102011-11-26 03:17:28 +0000428 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
429 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000430
kurtis.heimerl68292102011-11-26 03:17:28 +0000431 flush_recv(50);
432
433 usrp_dev->set_time_now(ts);
434 aligned = false;
435
436 cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
437 cmd.stream_now = true;
438 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000439}
440
kurtis.heimerl965e7572011-11-26 03:16:54 +0000441bool uhd_device::start()
442{
443 LOG(INFO) << "Starting USRP...";
444
445 if (started) {
446 LOG(ERROR) << "Device already started";
447 return false;
448 }
449
450 setPriority();
451
452 // Start asynchronous event (underrun check) loop
453 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
454
455 // Start streaming
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000456 restart(uhd::time_spec_t(0.0));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000457
kurtis.heimerl965e7572011-11-26 03:16:54 +0000458 // Display usrp time
459 double time_now = usrp_dev->get_time_now().get_real_secs();
460 LOG(INFO) << "The current time is " << time_now << " seconds";
461
462 started = true;
463 return true;
464}
465
466bool uhd_device::stop()
467{
468 uhd::stream_cmd_t stream_cmd =
469 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
470
471 usrp_dev->issue_stream_cmd(stream_cmd);
472
473 started = false;
474 return true;
475}
476
477void uhd_device::setPriority()
478{
479 uhd::set_thread_priority_safe();
480 return;
481}
482
kurtis.heimerld4be0742011-11-26 03:17:46 +0000483int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000484{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000485 uhd::time_spec_t ts;
486
kurtis.heimerld4be0742011-11-26 03:17:46 +0000487 if (!num_smpls) {
488 LOG(ERROR) << str_code(md);
489
490 switch (md.error_code) {
491 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
492 return ERROR_UNRECOVERABLE;
493 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
494 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
495 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
496 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
497 default:
498 return ERROR_UNHANDLED;
499 }
500 }
501
kurtis.heimerl965e7572011-11-26 03:16:54 +0000502 // Missing timestamp
503 if (!md.has_time_spec) {
504 LOG(ERROR) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000505 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000506 }
507
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000508 ts = md.time_spec;
509
kurtis.heimerl965e7572011-11-26 03:16:54 +0000510 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000511 if (ts < prev_ts) {
512 LOG(ERROR) << "UHD: Loss of monotonic: " << ts.get_real_secs();
513 LOG(ERROR) << "UHD: Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000514 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000515 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000516 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000517 }
518
519 return 0;
520}
521
522int uhd_device::readSamples(short *buf, int len, bool *overrun,
523 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
524{
525 ssize_t rc;
526 uhd::time_spec_t ts;
527 uhd::rx_metadata_t metadata;
528 uint32_t pkt_buf[rx_spp];
529
530 if (skip_rx)
531 return 0;
532
533 // Shift read time with respect to transmit clock
534 timestamp += ts_offset;
535
536 ts = convert_time(timestamp, actual_smpl_rt);
537 LOG(DEEPDEBUG) << "Requested timestamp = " << ts.get_real_secs();
538
539 // Check that timestamp is valid
540 rc = rx_smpl_buf->avail_smpls(timestamp);
541 if (rc < 0) {
542 LOG(ERROR) << rx_smpl_buf->str_code(rc);
543 LOG(ERROR) << rx_smpl_buf->str_status();
544 return 0;
545 }
546
547 // Receive samples from the usrp until we have enough
548 while (rx_smpl_buf->avail_smpls(timestamp) < len) {
549 size_t num_smpls = usrp_dev->get_device()->recv(
550 (void*)pkt_buf,
551 rx_spp,
552 metadata,
553 uhd::io_type_t::COMPLEX_INT16,
554 uhd::device::RECV_MODE_ONE_PACKET);
555
556 rx_pkt_cnt++;
557
kurtis.heimerld4be0742011-11-26 03:17:46 +0000558 // Check for errors
559 rc = check_rx_md_err(metadata, num_smpls);
560 switch (rc) {
561 case ERROR_UNRECOVERABLE:
562 LOG(ERROR) << "UHD: Unrecoverable error, exiting.";
563 exit(-1);
564 case ERROR_TIMING:
565 restart(prev_ts);
566 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000567 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000568 }
569
kurtis.heimerl965e7572011-11-26 03:16:54 +0000570
571 ts = metadata.time_spec;
572 LOG(DEEPDEBUG) << "Received timestamp = " << ts.get_real_secs();
573
574 rc = rx_smpl_buf->write(pkt_buf,
575 num_smpls,
576 metadata.time_spec);
577
578 // Continue on local overrun, exit on other errors
579 if ((rc < 0)) {
580 LOG(ERROR) << rx_smpl_buf->str_code(rc);
581 LOG(ERROR) << rx_smpl_buf->str_status();
582 if (rc != smpl_buf::ERROR_OVERFLOW)
583 return 0;
584 }
585 }
586
587 // We have enough samples
588 rc = rx_smpl_buf->read(buf, len, timestamp);
589 if ((rc < 0) || (rc != len)) {
590 LOG(ERROR) << rx_smpl_buf->str_code(rc);
591 LOG(ERROR) << rx_smpl_buf->str_status();
592 return 0;
593 }
594
595 return len;
596}
597
598int uhd_device::writeSamples(short *buf, int len, bool *underrun,
599 unsigned long long timestamp,bool isControl)
600{
601 uhd::tx_metadata_t metadata;
602 metadata.has_time_spec = true;
603 metadata.start_of_burst = false;
604 metadata.end_of_burst = false;
605 metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
606
607 // No control packets
608 if (isControl) {
609 LOG(ERROR) << "Control packets not supported";
610 return 0;
611 }
612
613 // Drop a fixed number of packets (magic value)
614 if (!aligned) {
615 drop_cnt++;
616
617 if (drop_cnt == 1) {
618 LOG(DEBUG) << "Aligning transmitter: stop burst";
619 metadata.end_of_burst = true;
620 } else if (drop_cnt < 30) {
621 LOG(DEEPDEBUG) << "Aligning transmitter: packet advance";
622 *underrun = true;
623 return len;
624 } else {
625 LOG(DEBUG) << "Aligning transmitter: start burst";
626 metadata.start_of_burst = true;
627 aligned = true;
628 drop_cnt = 0;
629 }
630 }
631
632 size_t num_smpls = usrp_dev->get_device()->send(buf,
633 len,
634 metadata,
635 uhd::io_type_t::COMPLEX_INT16,
636 uhd::device::SEND_MODE_FULL_BUFF);
637
638 if (num_smpls != (unsigned)len)
639 LOG(ERROR) << "UHD: Sent fewer samples than requested";
640
641 return num_smpls;
642}
643
644bool uhd_device::updateAlignment(TIMESTAMP timestamp)
645{
kurtis.heimerl13074c92011-11-26 03:17:43 +0000646 aligned = false;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000647 return true;
648}
649
650bool uhd_device::setTxFreq(double wFreq)
651{
652 uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
653 LOG(INFO) << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000654 tx_freq = usrp_dev->get_tx_freq();
655
kurtis.heimerl965e7572011-11-26 03:16:54 +0000656 return true;
657}
658
659bool uhd_device::setRxFreq(double wFreq)
660{
661 uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
662 LOG(INFO) << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000663 rx_freq = usrp_dev->get_rx_freq();
664
kurtis.heimerl965e7572011-11-26 03:16:54 +0000665 return true;
666}
667
668bool uhd_device::recv_async_msg()
669{
670 uhd::async_metadata_t metadata;
671 if (!usrp_dev->get_device()->recv_async_msg(metadata))
672 return false;
673
674 // Assume that any error requires resynchronization
675 if (metadata.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
676 aligned = false;
kurtis.heimerl6cb348b2011-11-26 03:17:54 +0000677 LOG(ERROR) << str_code(metadata);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000678 }
679
680 return true;
681}
682
683std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
684{
685 std::ostringstream ost("UHD: ");
686
687 switch (metadata.error_code) {
688 case uhd::rx_metadata_t::ERROR_CODE_NONE:
689 ost << "No error";
690 break;
691 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
692 ost << "No packet received, implementation timed-out";
693 break;
694 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
695 ost << "A stream command was issued in the past";
696 break;
697 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
698 ost << "Expected another stream command";
699 break;
700 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
701 ost << "An internal receive buffer has filled";
702 break;
703 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
704 ost << "The packet could not be parsed";
705 break;
706 default:
707 ost << "Unknown error " << metadata.error_code;
708 }
709
710 if (metadata.has_time_spec)
711 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
712
713 return ost.str();
714}
715
716std::string uhd_device::str_code(uhd::async_metadata_t metadata)
717{
718 std::ostringstream ost("UHD: ");
719
720 switch (metadata.event_code) {
721 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
722 ost << "A packet was successfully transmitted";
723 break;
724 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
725 ost << "An internal send buffer has emptied";
726 break;
727 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
728 ost << "Packet loss between host and device";
729 break;
730 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
731 ost << "Packet time was too late or too early";
732 break;
733 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
734 ost << "Underflow occurred inside a packet";
735 break;
736 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
737 ost << "Packet loss within a burst";
738 break;
739 default:
740 ost << "Unknown error " << metadata.event_code;
741 }
742
743 if (metadata.has_time_spec)
744 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
745
746 return ost.str();
747}
748
749smpl_buf::smpl_buf(size_t len, double rate)
750 : buf_len(len), clk_rt(rate),
751 time_start(0), time_end(0), data_start(0), data_end(0)
752{
753 data = new uint32_t[len];
754}
755
756smpl_buf::~smpl_buf()
757{
758 delete[] data;
759}
760
761ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
762{
763 if (timestamp < time_start)
764 return ERROR_TIMESTAMP;
765 else if (timestamp >= time_end)
766 return 0;
767 else
768 return time_end - timestamp;
769}
770
771ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
772{
773 return avail_smpls(convert_time(timespec, clk_rt));
774}
775
776ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
777{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000778 int type_sz = 2 * sizeof(short);
779
kurtis.heimerl965e7572011-11-26 03:16:54 +0000780 // Check for valid read
781 if (timestamp < time_start)
782 return ERROR_TIMESTAMP;
783 if (timestamp >= time_end)
784 return 0;
785 if (len >= buf_len)
786 return ERROR_READ;
787
788 // How many samples should be copied
789 size_t num_smpls = time_end - timestamp;
790 if (num_smpls > len);
791 num_smpls = len;
792
793 // Starting index
794 size_t read_start = data_start + (timestamp - time_start);
795
796 // Read it
797 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000798 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000799 memcpy(buf, data + read_start, numBytes);
800 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000801 size_t first_cp = (buf_len - read_start) * type_sz;
802 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000803
804 memcpy(buf, data + read_start, first_cp);
805 memcpy((char*) buf + first_cp, data, second_cp);
806 }
807
808 data_start = (read_start + len) % buf_len;
809 time_start = timestamp + len;
810
811 if (time_start > time_end)
812 return ERROR_READ;
813 else
814 return num_smpls;
815}
816
817ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
818{
819 return read(buf, len, convert_time(ts, clk_rt));
820}
821
822ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
823{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000824 int type_sz = 2 * sizeof(short);
825
kurtis.heimerl965e7572011-11-26 03:16:54 +0000826 // Check for valid write
827 if ((len == 0) || (len >= buf_len))
828 return ERROR_WRITE;
829 if ((timestamp + len) <= time_end)
830 return ERROR_TIMESTAMP;
831
832 // Starting index
833 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
834
835 // Write it
836 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000837 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000838 memcpy(data + write_start, buf, numBytes);
839 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000840 size_t first_cp = (buf_len - write_start) * type_sz;
841 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000842
843 memcpy(data + write_start, buf, first_cp);
844 memcpy(data, (char*) buf + first_cp, second_cp);
845 }
846
847 data_end = (write_start + len) % buf_len;
848 time_end = timestamp + len;
849
850 if (((write_start + len) > buf_len) && (data_end > data_start))
851 return ERROR_OVERFLOW;
852 else if (time_end <= time_start)
853 return ERROR_WRITE;
854 else
855 return len;
856}
857
858ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
859{
860 return write(buf, len, convert_time(ts, clk_rt));
861}
862
863std::string smpl_buf::str_status() const
864{
865 std::ostringstream ost("Sample buffer: ");
866
867 ost << "length = " << buf_len;
868 ost << ", time_start = " << time_start;
869 ost << ", time_end = " << time_end;
870 ost << ", data_start = " << data_start;
871 ost << ", data_end = " << data_end;
872
873 return ost.str();
874}
875
876std::string smpl_buf::str_code(ssize_t code)
877{
878 switch (code) {
879 case ERROR_TIMESTAMP:
880 return "Sample buffer: Requested timestamp is not valid";
881 case ERROR_READ:
882 return "Sample buffer: Read error";
883 case ERROR_WRITE:
884 return "Sample buffer: Write error";
885 case ERROR_OVERFLOW:
886 return "Sample buffer: Overrun";
887 default:
888 return "Sample buffer: Unknown error";
889 }
890}
891
892RadioDevice *RadioDevice::make(double smpl_rt, bool skip_rx)
893{
894 return new uhd_device(smpl_rt, skip_rx);
895}