blob: 145a15a7cc20a84688de69141ae65aebbb0f91fc [file] [log] [blame]
kurtis.heimerl965e7572011-11-26 03:16:54 +00001/*
2* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
3*
4* This software is distributed under the terms of the GNU Affero Public License.
5* See the COPYING file in the main directory for details.
6*
7* This use of this software may be subject to additional restrictions.
8* See the LEGAL file in the main directory for details.
9
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU Affero General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Affero General Public License for more details.
19
20 You should have received a copy of the GNU Affero General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23*/
24
25#include "radioDevice.h"
26#include "Threads.h"
27#include "Logger.h"
28#include <uhd/usrp/single_usrp.hpp>
29#include <uhd/utils/thread_priority.hpp>
30
31/*
32 use_ext_ref - Enable external 10MHz clock reference
33
34 master_clk_rt - Master clock frequency
35
36 rx_smpl_offset - Timing correction in seconds between receive and
37 transmit timestamps. This value corrects for delays on
38 on the RF side of the timestamping point of the device.
kurtis.heimerlbe6abe12011-11-26 03:17:05 +000039 This value is generally empirically measured.
kurtis.heimerl965e7572011-11-26 03:16:54 +000040
41 smpl_buf_sz - The receive sample buffer size in bytes.
42*/
43const bool use_ext_ref = false;
44const double master_clk_rt = 52e6;
kurtis.heimerlbe6abe12011-11-26 03:17:05 +000045const double rx_smpl_offset = .0000869;
kurtis.heimerl965e7572011-11-26 03:16:54 +000046const size_t smpl_buf_sz = (1 << 20);
47
48/** Timestamp conversion
49 @param timestamp a UHD or OpenBTS timestamp
50 @param rate sample rate
51 @return the converted timestamp
52*/
53uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
54{
55 double secs = (double) ticks / rate;
56 return uhd::time_spec_t(secs);
57}
58
59TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
60{
61 size_t ticks = ts.get_full_secs() * rate;
62 return ts.get_tick_count(rate) + ticks;
63}
64
65/*
66 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
67 or UHD style timestamps. Time conversions are handled
68 internally or accessable through the static convert calls.
69*/
70class smpl_buf {
71public:
72 /** Sample buffer constructor
73 @param len number of 32-bit samples the buffer should hold
74 @param rate sample clockrate
75 @param timestamp
76 */
77 smpl_buf(size_t len, double rate);
78 ~smpl_buf();
79
80 /** Query number of samples available for reading
81 @param timestamp time of first sample
82 @return number of available samples or error
83 */
84 ssize_t avail_smpls(TIMESTAMP timestamp) const;
85 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
86
87 /** Read and write
88 @param buf pointer to buffer
89 @param len number of samples desired to read or write
90 @param timestamp time of first stample
91 @return number of actual samples read or written or error
92 */
93 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
94 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
95 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
96 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
97
98 /** Buffer status string
99 @return a formatted string describing internal buffer state
100 */
101 std::string str_status() const;
102
103 /** Formatted error string
104 @param code an error code
105 @return a formatted error string
106 */
107 static std::string str_code(ssize_t code);
108
109 enum err_code {
110 ERROR_TIMESTAMP = -1,
111 ERROR_READ = -2,
112 ERROR_WRITE = -3,
113 ERROR_OVERFLOW = -4
114 };
115
116private:
117 uint32_t *data;
118 size_t buf_len;
119
120 double clk_rt;
121
122 TIMESTAMP time_start;
123 TIMESTAMP time_end;
124
125 size_t data_start;
126 size_t data_end;
127};
128
129/*
130 uhd_device - UHD implementation of the Device interface. Timestamped samples
131 are sent to and received from the device. An intermediate buffer
132 on the receive side collects and aligns packets of samples.
133 Events and errors such as underruns are reported asynchronously
134 by the device and received in a separate thread.
135*/
136class uhd_device : public RadioDevice {
137public:
138 uhd_device(double rate, bool skip_rx);
139 ~uhd_device();
140
141 bool open();
142 bool start();
143 bool stop();
144 void setPriority();
145
146 int readSamples(short *buf, int len, bool *overrun,
147 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
148
149 int writeSamples(short *buf, int len, bool *underrun,
150 TIMESTAMP timestamp, bool isControl);
151
152 bool updateAlignment(TIMESTAMP timestamp);
153
154 bool setTxFreq(double wFreq);
155 bool setRxFreq(double wFreq);
156
157 inline TIMESTAMP initialWriteTimestamp() { return 0; }
158 inline TIMESTAMP initialReadTimestamp() { return 0; }
159
160 inline double fullScaleInputValue() { return 13500.0; }
161 inline double fullScaleOutputValue() { return 9450.0; }
162
163 double setRxGain(double dB) { return 0; }
164 double getRxGain(void) { return 0; }
165 double maxRxGain(void) { return 0; }
166 double minRxGain(void) { return 0; }
167
168 double setTxGain(double dB) { return 0; }
169 double maxTxGain(void) { return 0; }
170 double minTxGain(void) { return 0; }
171
172 double getTxFreq() { return 0; }
173 double getRxFreq() { return 0; }
174
175 inline double getSampleRate() { return actual_smpl_rt; }
176 inline double numberRead() { return rx_pkt_cnt; }
177 inline double numberWritten() { return 0; }
178
179 /** Receive and process asynchronous message
180 @return true if message received or false on timeout or error
181 */
182 bool recv_async_msg();
183
184private:
185 uhd::usrp::single_usrp::sptr usrp_dev;
186
187 double desired_smpl_rt;
188 double actual_smpl_rt;
189
190 size_t tx_spp;
191 size_t rx_spp;
192
193 bool started;
194 bool aligned;
195 bool skip_rx;
196
197 size_t rx_pkt_cnt;
198 size_t drop_cnt;
199 uhd::time_spec_t prev_ts;
200
201 TIMESTAMP ts_offset;
202 smpl_buf *rx_smpl_buf;
203
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000204 bool flush_recv(size_t num_pkts);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000205 std::string str_code(uhd::rx_metadata_t metadata);
206 std::string str_code(uhd::async_metadata_t metadata);
207
208 Thread async_event_thrd;
209};
210
211void *async_event_loop(uhd_device *dev)
212{
213 while (1) {
214 dev->recv_async_msg();
215 pthread_testcancel();
216 }
217}
218
219uhd_device::uhd_device(double rate, bool skip_rx)
220 : desired_smpl_rt(rate), actual_smpl_rt(0), tx_spp(0), rx_spp(0),
221 started(false), aligned(true), rx_pkt_cnt(0), drop_cnt(0),
222 prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
223{
224 this->skip_rx = skip_rx;
225}
226
227uhd_device::~uhd_device()
228{
229 stop();
230
231 if (rx_smpl_buf)
232 delete rx_smpl_buf;
233}
234
235static double set_usrp_rates(uhd::usrp::single_usrp::sptr dev, double rate)
236{
237 double actual_rate;
238
239 dev->set_tx_rate(rate);
240 dev->set_rx_rate(rate);
241 actual_rate = dev->get_tx_rate();
242
243 if (actual_rate != rate) {
244 LOG(ERROR) << "Actual sample rate differs from desired rate";
245 return -1.0;
246 }
247 if (dev->get_rx_rate() != actual_rate) {
248 LOG(ERROR) << "Transmit and receive sample rates do not match";
249 return -1.0;
250 }
251
252 return actual_rate;
253}
254
255static void set_usrp_tx_gain(uhd::usrp::single_usrp::sptr dev, double)
256{
257 uhd::gain_range_t range = dev->get_tx_gain_range();
258 dev->set_tx_gain((range.start() + range.stop()) / 2);
259}
260
261static void set_usrp_rx_gain(uhd::usrp::single_usrp::sptr dev, double)
262{
263 uhd::gain_range_t range = dev->get_rx_gain_range();
264 dev->set_rx_gain((range.start() + range.stop()) / 2);
265}
266
267static void set_usrp_ref_clk(uhd::usrp::single_usrp::sptr dev, bool ext_clk)
268{
269 uhd::clock_config_t clk_cfg;
270
271 clk_cfg.pps_source = uhd::clock_config_t::PPS_SMA;
272 clk_cfg.pps_polarity = uhd::clock_config_t::PPS_NEG;
273
274 if (ext_clk)
275 clk_cfg.ref_source = uhd::clock_config_t::REF_SMA;
276 else
277 clk_cfg.ref_source = uhd::clock_config_t::REF_INT;
278
279 dev->set_clock_config(clk_cfg);
280}
281
282bool uhd_device::open()
283{
284 LOG(INFO) << "creating USRP device...";
285
kurtis.heimerlc4919972011-11-26 03:17:00 +0000286 // Use the first available USRP E100
287 uhd::device_addr_t dev_addr("type=usrp-e");
kurtis.heimerl965e7572011-11-26 03:16:54 +0000288 try {
289 usrp_dev = uhd::usrp::single_usrp::make(dev_addr);
290 }
291
292 catch(...) {
293 LOG(ERROR) << "USRP make failed";
294 return false;
295 }
296
297 // Set master clock rate
298 usrp_dev->set_master_clock_rate(master_clk_rt);
299
300 // Number of samples per over-the-wire packet
301 tx_spp = usrp_dev->get_device()->get_max_send_samps_per_packet();
302 rx_spp = usrp_dev->get_device()->get_max_recv_samps_per_packet();
303
304 // Set rates
305 actual_smpl_rt = set_usrp_rates(usrp_dev, desired_smpl_rt);
306 if (actual_smpl_rt < 0)
307 return false;
308
309 // Create receive buffer
310 size_t buf_len = smpl_buf_sz / sizeof(uint32_t);
311 rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
312
313 // Set receive chain sample offset
314 ts_offset = (TIMESTAMP)(rx_smpl_offset * actual_smpl_rt);
315
316 // Set gains to midpoint
317 set_usrp_tx_gain(usrp_dev, 0.0);
318
319 // Set reference clock
320 set_usrp_ref_clk(usrp_dev, use_ext_ref);
321
322 // Print configuration
323 LOG(INFO) << usrp_dev->get_pp_string();
324
325 return true;
326}
327
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000328bool uhd_device::flush_recv(size_t num_pkts)
329{
330 uhd::rx_metadata_t metadata;
331 size_t num_smpls;
332 uint32_t buff[rx_spp];
333
334 for (size_t i = 0; i < num_pkts; i++) {
335 num_smpls = usrp_dev->get_device()->recv(
336 buff,
337 rx_spp,
338 metadata,
339 uhd::io_type_t::COMPLEX_INT16,
340 uhd::device::RECV_MODE_ONE_PACKET);
341
342 if (!num_smpls)
343 return false;
344 }
345
346 return true;
347}
348
kurtis.heimerl965e7572011-11-26 03:16:54 +0000349bool uhd_device::start()
350{
351 LOG(INFO) << "Starting USRP...";
352
353 if (started) {
354 LOG(ERROR) << "Device already started";
355 return false;
356 }
357
358 setPriority();
359
360 // Start asynchronous event (underrun check) loop
361 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
362
363 // Start streaming
364 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
365 cmd.stream_now = true;
366 usrp_dev->set_time_now(uhd::time_spec_t(0.0));
367
368 if (!skip_rx)
369 usrp_dev->issue_stream_cmd(cmd);
370
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000371 // Flush out any early garbage
372 if (!flush_recv(20))
373 return false;
374
kurtis.heimerl965e7572011-11-26 03:16:54 +0000375 // Display usrp time
376 double time_now = usrp_dev->get_time_now().get_real_secs();
377 LOG(INFO) << "The current time is " << time_now << " seconds";
378
379 started = true;
380 return true;
381}
382
383bool uhd_device::stop()
384{
385 uhd::stream_cmd_t stream_cmd =
386 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
387
388 usrp_dev->issue_stream_cmd(stream_cmd);
389
390 started = false;
391 return true;
392}
393
394void uhd_device::setPriority()
395{
396 uhd::set_thread_priority_safe();
397 return;
398}
399
400static int check_rx_md_err(uhd::rx_metadata_t &md, uhd::time_spec_t &prev_ts)
401{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000402 uhd::time_spec_t ts;
403
kurtis.heimerl965e7572011-11-26 03:16:54 +0000404 // Missing timestamp
405 if (!md.has_time_spec) {
406 LOG(ERROR) << "UHD: Received packet missing timestamp";
407 return -1;
408 }
409
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000410 ts = md.time_spec;
411
kurtis.heimerl965e7572011-11-26 03:16:54 +0000412 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000413 if (ts < prev_ts) {
414 LOG(ERROR) << "UHD: Loss of monotonic: " << ts.get_real_secs();
415 LOG(ERROR) << "UHD: Previous time: " << prev_ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000416 return -1;
417 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000418 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000419 }
420
421 return 0;
422}
423
424int uhd_device::readSamples(short *buf, int len, bool *overrun,
425 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
426{
427 ssize_t rc;
428 uhd::time_spec_t ts;
429 uhd::rx_metadata_t metadata;
430 uint32_t pkt_buf[rx_spp];
431
432 if (skip_rx)
433 return 0;
434
435 // Shift read time with respect to transmit clock
436 timestamp += ts_offset;
437
438 ts = convert_time(timestamp, actual_smpl_rt);
439 LOG(DEEPDEBUG) << "Requested timestamp = " << ts.get_real_secs();
440
441 // Check that timestamp is valid
442 rc = rx_smpl_buf->avail_smpls(timestamp);
443 if (rc < 0) {
444 LOG(ERROR) << rx_smpl_buf->str_code(rc);
445 LOG(ERROR) << rx_smpl_buf->str_status();
446 return 0;
447 }
448
449 // Receive samples from the usrp until we have enough
450 while (rx_smpl_buf->avail_smpls(timestamp) < len) {
451 size_t num_smpls = usrp_dev->get_device()->recv(
452 (void*)pkt_buf,
453 rx_spp,
454 metadata,
455 uhd::io_type_t::COMPLEX_INT16,
456 uhd::device::RECV_MODE_ONE_PACKET);
457
458 rx_pkt_cnt++;
459
460 // Recv error in UHD
461 if (!num_smpls) {
462 LOG(ERROR) << str_code(metadata);
463 return 0;
464 }
465
466 // Other metadata timing checks
467 if (check_rx_md_err(metadata, prev_ts) < 0)
468 return 0;
469
470 ts = metadata.time_spec;
471 LOG(DEEPDEBUG) << "Received timestamp = " << ts.get_real_secs();
472
473 rc = rx_smpl_buf->write(pkt_buf,
474 num_smpls,
475 metadata.time_spec);
476
477 // Continue on local overrun, exit on other errors
478 if ((rc < 0)) {
479 LOG(ERROR) << rx_smpl_buf->str_code(rc);
480 LOG(ERROR) << rx_smpl_buf->str_status();
481 if (rc != smpl_buf::ERROR_OVERFLOW)
482 return 0;
483 }
484 }
485
486 // We have enough samples
487 rc = rx_smpl_buf->read(buf, len, timestamp);
488 if ((rc < 0) || (rc != len)) {
489 LOG(ERROR) << rx_smpl_buf->str_code(rc);
490 LOG(ERROR) << rx_smpl_buf->str_status();
491 return 0;
492 }
493
494 return len;
495}
496
497int uhd_device::writeSamples(short *buf, int len, bool *underrun,
498 unsigned long long timestamp,bool isControl)
499{
500 uhd::tx_metadata_t metadata;
501 metadata.has_time_spec = true;
502 metadata.start_of_burst = false;
503 metadata.end_of_burst = false;
504 metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
505
506 // No control packets
507 if (isControl) {
508 LOG(ERROR) << "Control packets not supported";
509 return 0;
510 }
511
512 // Drop a fixed number of packets (magic value)
513 if (!aligned) {
514 drop_cnt++;
515
516 if (drop_cnt == 1) {
517 LOG(DEBUG) << "Aligning transmitter: stop burst";
518 metadata.end_of_burst = true;
519 } else if (drop_cnt < 30) {
520 LOG(DEEPDEBUG) << "Aligning transmitter: packet advance";
521 *underrun = true;
522 return len;
523 } else {
524 LOG(DEBUG) << "Aligning transmitter: start burst";
525 metadata.start_of_burst = true;
526 aligned = true;
527 drop_cnt = 0;
528 }
529 }
530
531 size_t num_smpls = usrp_dev->get_device()->send(buf,
532 len,
533 metadata,
534 uhd::io_type_t::COMPLEX_INT16,
535 uhd::device::SEND_MODE_FULL_BUFF);
536
537 if (num_smpls != (unsigned)len)
538 LOG(ERROR) << "UHD: Sent fewer samples than requested";
539
540 return num_smpls;
541}
542
543bool uhd_device::updateAlignment(TIMESTAMP timestamp)
544{
545 /* NOP */
546 return true;
547}
548
549bool uhd_device::setTxFreq(double wFreq)
550{
551 uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
552 LOG(INFO) << tr.to_pp_string();
553 return true;
554}
555
556bool uhd_device::setRxFreq(double wFreq)
557{
558 uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
559 LOG(INFO) << tr.to_pp_string();
560 return true;
561}
562
563bool uhd_device::recv_async_msg()
564{
565 uhd::async_metadata_t metadata;
566 if (!usrp_dev->get_device()->recv_async_msg(metadata))
567 return false;
568
569 // Assume that any error requires resynchronization
570 if (metadata.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
571 aligned = false;
572 LOG(INFO) << str_code(metadata);
573 }
574
575 return true;
576}
577
578std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
579{
580 std::ostringstream ost("UHD: ");
581
582 switch (metadata.error_code) {
583 case uhd::rx_metadata_t::ERROR_CODE_NONE:
584 ost << "No error";
585 break;
586 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
587 ost << "No packet received, implementation timed-out";
588 break;
589 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
590 ost << "A stream command was issued in the past";
591 break;
592 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
593 ost << "Expected another stream command";
594 break;
595 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
596 ost << "An internal receive buffer has filled";
597 break;
598 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
599 ost << "The packet could not be parsed";
600 break;
601 default:
602 ost << "Unknown error " << metadata.error_code;
603 }
604
605 if (metadata.has_time_spec)
606 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
607
608 return ost.str();
609}
610
611std::string uhd_device::str_code(uhd::async_metadata_t metadata)
612{
613 std::ostringstream ost("UHD: ");
614
615 switch (metadata.event_code) {
616 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
617 ost << "A packet was successfully transmitted";
618 break;
619 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
620 ost << "An internal send buffer has emptied";
621 break;
622 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
623 ost << "Packet loss between host and device";
624 break;
625 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
626 ost << "Packet time was too late or too early";
627 break;
628 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
629 ost << "Underflow occurred inside a packet";
630 break;
631 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
632 ost << "Packet loss within a burst";
633 break;
634 default:
635 ost << "Unknown error " << metadata.event_code;
636 }
637
638 if (metadata.has_time_spec)
639 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
640
641 return ost.str();
642}
643
644smpl_buf::smpl_buf(size_t len, double rate)
645 : buf_len(len), clk_rt(rate),
646 time_start(0), time_end(0), data_start(0), data_end(0)
647{
648 data = new uint32_t[len];
649}
650
651smpl_buf::~smpl_buf()
652{
653 delete[] data;
654}
655
656ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
657{
658 if (timestamp < time_start)
659 return ERROR_TIMESTAMP;
660 else if (timestamp >= time_end)
661 return 0;
662 else
663 return time_end - timestamp;
664}
665
666ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
667{
668 return avail_smpls(convert_time(timespec, clk_rt));
669}
670
671ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
672{
673 // Check for valid read
674 if (timestamp < time_start)
675 return ERROR_TIMESTAMP;
676 if (timestamp >= time_end)
677 return 0;
678 if (len >= buf_len)
679 return ERROR_READ;
680
681 // How many samples should be copied
682 size_t num_smpls = time_end - timestamp;
683 if (num_smpls > len);
684 num_smpls = len;
685
686 // Starting index
687 size_t read_start = data_start + (timestamp - time_start);
688
689 // Read it
690 if (read_start + num_smpls < buf_len) {
691 size_t numBytes = len * 2 * sizeof(short);
692 memcpy(buf, data + read_start, numBytes);
693 } else {
694 size_t first_cp = (buf_len - read_start) * 2 * sizeof(short);
695 size_t second_cp = len * 2 * sizeof(short) - first_cp;
696
697 memcpy(buf, data + read_start, first_cp);
698 memcpy((char*) buf + first_cp, data, second_cp);
699 }
700
701 data_start = (read_start + len) % buf_len;
702 time_start = timestamp + len;
703
704 if (time_start > time_end)
705 return ERROR_READ;
706 else
707 return num_smpls;
708}
709
710ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
711{
712 return read(buf, len, convert_time(ts, clk_rt));
713}
714
715ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
716{
717 // Check for valid write
718 if ((len == 0) || (len >= buf_len))
719 return ERROR_WRITE;
720 if ((timestamp + len) <= time_end)
721 return ERROR_TIMESTAMP;
722
723 // Starting index
724 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
725
726 // Write it
727 if ((write_start + len) < buf_len) {
728 size_t numBytes = len * 2 * sizeof(short);
729 memcpy(data + write_start, buf, numBytes);
730 } else {
731 size_t first_cp = (buf_len - write_start) * 2 * sizeof(short);
732 size_t second_cp = len * 2 * sizeof(short) - first_cp;
733
734 memcpy(data + write_start, buf, first_cp);
735 memcpy(data, (char*) buf + first_cp, second_cp);
736 }
737
738 data_end = (write_start + len) % buf_len;
739 time_end = timestamp + len;
740
741 if (((write_start + len) > buf_len) && (data_end > data_start))
742 return ERROR_OVERFLOW;
743 else if (time_end <= time_start)
744 return ERROR_WRITE;
745 else
746 return len;
747}
748
749ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
750{
751 return write(buf, len, convert_time(ts, clk_rt));
752}
753
754std::string smpl_buf::str_status() const
755{
756 std::ostringstream ost("Sample buffer: ");
757
758 ost << "length = " << buf_len;
759 ost << ", time_start = " << time_start;
760 ost << ", time_end = " << time_end;
761 ost << ", data_start = " << data_start;
762 ost << ", data_end = " << data_end;
763
764 return ost.str();
765}
766
767std::string smpl_buf::str_code(ssize_t code)
768{
769 switch (code) {
770 case ERROR_TIMESTAMP:
771 return "Sample buffer: Requested timestamp is not valid";
772 case ERROR_READ:
773 return "Sample buffer: Read error";
774 case ERROR_WRITE:
775 return "Sample buffer: Write error";
776 case ERROR_OVERFLOW:
777 return "Sample buffer: Overrun";
778 default:
779 return "Sample buffer: Unknown error";
780 }
781}
782
783RadioDevice *RadioDevice::make(double smpl_rt, bool skip_rx)
784{
785 return new uhd_device(smpl_rt, skip_rx);
786}