Statistics
| Branch: | Tag: | Revision:

root / host / lib / usrp / usrp1 / io_impl.cpp @ 20c18678

History | View | Annotate | Download (22.3 KB)

1
//
2
// Copyright 2010-2011 Ettus Research LLC
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

    
18
#include "validate_subdev_spec.hpp"
19
#define SRPH_DONT_CHECK_SEQUENCE
20
#include "../../transport/super_recv_packet_handler.hpp"
21
#define SSPH_DONT_PAD_TO_ONE
22
#include "../../transport/super_send_packet_handler.hpp"
23
#include "usrp1_calc_mux.hpp"
24
#include "fpga_regs_standard.h"
25
#include "usrp_commands.h"
26
#include "usrp1_impl.hpp"
27
#include <uhd/utils/msg.hpp>
28
#include <uhd/utils/tasks.hpp>
29
#include <uhd/utils/safe_call.hpp>
30
#include <uhd/transport/bounded_buffer.hpp>
31
#include <boost/math/special_functions/sign.hpp>
32
#include <boost/math/special_functions/round.hpp>
33
#include <boost/thread/thread.hpp>
34
#include <boost/bind.hpp>
35
#include <boost/format.hpp>
36
#include <boost/make_shared.hpp>
37

    
38
using namespace uhd;
39
using namespace uhd::usrp;
40
using namespace uhd::transport;
41

    
42
static const size_t alignment_padding = 512;
43

    
44
/***********************************************************************
45
 * Helper struct to associate an offset with a buffer
46
 **********************************************************************/
47
struct offset_send_buffer{
48
    offset_send_buffer(void){
49
        /* NOP */
50
    }
51

    
52
    offset_send_buffer(managed_send_buffer::sptr buff, size_t offset = 0):
53
        buff(buff), offset(offset)
54
    {
55
        /* NOP */
56
    }
57

    
58
    //member variables
59
    managed_send_buffer::sptr buff;
60
    size_t offset; /* in bytes */
61
};
62

    
63
/***********************************************************************
64
 * Reusable managed send buffer to handle aligned commits
65
 **********************************************************************/
66
class offset_managed_send_buffer : public managed_send_buffer{
67
public:
68
    typedef boost::function<void(offset_send_buffer&, offset_send_buffer&, size_t)> commit_cb_type;
69
    offset_managed_send_buffer(const commit_cb_type &commit_cb):
70
        _commit_cb(commit_cb)
71
    {
72
        /* NOP */
73
    }
74

    
75
    void commit(size_t size){
76
        if (size != 0) this->_commit_cb(_curr_buff, _next_buff, size);
77
    }
78

    
79
    sptr get_new(
80
        offset_send_buffer &curr_buff,
81
        offset_send_buffer &next_buff
82
    ){
83
        _curr_buff = curr_buff;
84
        _next_buff = next_buff;
85
        return make_managed_buffer(this);
86
    }
87

    
88
private:
89
    void  *get_buff(void) const{return _curr_buff.buff->cast<char *>() + _curr_buff.offset;}
90
    size_t get_size(void) const{return _curr_buff.buff->size()         - _curr_buff.offset;}
91

    
92
    offset_send_buffer _curr_buff, _next_buff;
93
    commit_cb_type _commit_cb;
94
};
95

    
96
/***********************************************************************
97
 * BS VRT packer/unpacker functions (since samples don't have headers)
98
 **********************************************************************/
99
static void usrp1_bs_vrt_packer(
100
    boost::uint32_t *,
101
    vrt::if_packet_info_t &if_packet_info
102
){
103
    if_packet_info.num_header_words32 = 0;
104
    if_packet_info.num_packet_words32 = if_packet_info.num_payload_words32;
105
}
106

    
107
static void usrp1_bs_vrt_unpacker(
108
    const boost::uint32_t *,
109
    vrt::if_packet_info_t &if_packet_info
110
){
111
    if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA;
112
    if_packet_info.num_payload_words32 = if_packet_info.num_packet_words32;
113
    if_packet_info.num_header_words32 = 0;
114
    if_packet_info.packet_count = 0;
115
    if_packet_info.sob = false;
116
    if_packet_info.eob = false;
117
    if_packet_info.has_sid = false;
118
    if_packet_info.has_cid = false;
119
    if_packet_info.has_tsi = false;
120
    if_packet_info.has_tsf = false;
121
    if_packet_info.has_tlr = false;
122
}
123

    
124
/***********************************************************************
125
 * IO Implementation Details
126
 **********************************************************************/
127
struct usrp1_impl::io_impl{
128
    io_impl(zero_copy_if::sptr data_transport):
129
        data_transport(data_transport),
130
        curr_buff(offset_send_buffer(data_transport->get_send_buff())),
131
        omsb(boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, _1, _2, _3))
132
    {
133
        /* NOP */
134
    }
135

    
136
    ~io_impl(void){
137
        UHD_SAFE_CALL(flush_send_buff();)
138
    }
139

    
140
    zero_copy_if::sptr data_transport;
141

    
142
    //wrapper around the actual send buffer interface
143
    //all of this to ensure only aligned lengths are committed
144
    //NOTE: you must commit before getting a new buffer
145
    //since the vrt packet handler obeys this, we are ok
146
    offset_send_buffer curr_buff;
147
    offset_managed_send_buffer omsb;
148
    void commit_send_buff(offset_send_buffer&, offset_send_buffer&, size_t);
149
    void flush_send_buff(void);
150
    managed_send_buffer::sptr get_send_buff(double timeout){
151
        //try to get a new managed buffer with timeout
152
        offset_send_buffer next_buff(data_transport->get_send_buff(timeout));
153
        if (not next_buff.buff.get()) return managed_send_buffer::sptr(); /* propagate timeout here */
154

    
155
        //make a new managed buffer with the offset buffs
156
        return omsb.get_new(curr_buff, next_buff);
157
    }
158

    
159
    task::sptr vandal_task;
160
    boost::system_time last_send_time;
161
};
162

    
163
/*!
164
 * Perform an actual commit on the send buffer:
165
 * Copy the remainder of alignment to the next buffer.
166
 * Commit the current buffer at multiples of alignment.
167
 */
168
void usrp1_impl::io_impl::commit_send_buff(
169
    offset_send_buffer &curr,
170
    offset_send_buffer &next,
171
    size_t num_bytes
172
){
173
    //total number of bytes now in the current buffer
174
    size_t bytes_in_curr_buffer = curr.offset + num_bytes;
175

    
176
    //calculate how many to commit and remainder
177
    size_t num_bytes_remaining = bytes_in_curr_buffer % alignment_padding;
178
    size_t num_bytes_to_commit = bytes_in_curr_buffer - num_bytes_remaining;
179

    
180
    //copy the remainder into the next buffer
181
    std::memcpy(
182
        next.buff->cast<char *>() + next.offset,
183
        curr.buff->cast<char *>() + num_bytes_to_commit,
184
        num_bytes_remaining
185
    );
186

    
187
    //update the offset into the next buffer
188
    next.offset += num_bytes_remaining;
189

    
190
    //commit the current buffer
191
    curr.buff->commit(num_bytes_to_commit);
192

    
193
    //store the next buffer for the next call
194
    curr_buff = next;
195
}
196

    
197
/*!
198
 * Flush the current buffer by padding out to alignment and committing.
199
 */
200
void usrp1_impl::io_impl::flush_send_buff(void){
201
    //calculate the number of bytes to alignment
202
    size_t bytes_to_pad = (-1*curr_buff.offset)%alignment_padding;
203

    
204
    //send at least alignment_padding to guarantee zeros are sent
205
    if (bytes_to_pad == 0) bytes_to_pad = alignment_padding;
206

    
207
    //get the buffer, clear, and commit (really current buffer)
208
    managed_send_buffer::sptr buff = this->get_send_buff(.1);
209
    if (buff.get() != NULL){
210
        std::memset(buff->cast<void *>(), 0, bytes_to_pad);
211
        buff->commit(bytes_to_pad);
212
    }
213
}
214

    
215
/***********************************************************************
216
 * Initialize internals within this file
217
 **********************************************************************/
218
void usrp1_impl::io_init(void){
219

    
220
    _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport));
221

    
222
    //create a new vandal thread to poll xerflow conditions
223
    _io_impl->vandal_task = task::make(boost::bind(
224
        &usrp1_impl::vandal_conquest_loop, this
225
    ));
226

    
227
    //init as disabled, then call the real function (uses restore)
228
    this->enable_rx(false);
229
    this->enable_tx(false);
230
    rx_stream_on_off(false);
231
    tx_stream_on_off(false);
232
    _io_impl->flush_send_buff();
233
}
234

    
235
void usrp1_impl::rx_stream_on_off(bool enb){
236
    this->restore_rx(enb);
237
    //drain any junk in the receive transport after stop streaming command
238
    while(not enb and _data_transport->get_recv_buff().get() != NULL){
239
        /* NOP */
240
    }
241
}
242

    
243
void usrp1_impl::tx_stream_on_off(bool enb){
244
    _io_impl->last_send_time = boost::get_system_time();
245
    if (_tx_enabled and not enb) _io_impl->flush_send_buff();
246
    this->restore_tx(enb);
247
}
248

    
249
/*!
250
 * Casually poll the overflow and underflow registers.
251
 * On an underflow, push an async message into the queue and print.
252
 * On an overflow, interleave an inline message into recv and print.
253
 * This procedure creates "soft" inline and async user messages.
254
 */
255
void usrp1_impl::vandal_conquest_loop(void){
256

    
257
    //initialize the async metadata
258
    async_metadata_t async_metadata;
259
    async_metadata.channel = 0;
260
    async_metadata.has_time_spec = true;
261
    async_metadata.event_code = async_metadata_t::EVENT_CODE_UNDERFLOW;
262

    
263
    //initialize the inline metadata
264
    rx_metadata_t inline_metadata;
265
    inline_metadata.has_time_spec = true;
266
    inline_metadata.error_code = rx_metadata_t::ERROR_CODE_OVERFLOW;
267

    
268
    //start the polling loop...
269
    try{ while (not boost::this_thread::interruption_requested()){
270
        boost::uint8_t underflow = 0, overflow = 0;
271

    
272
        //shutoff transmit if it has been too long since send() was called
273
        if (_tx_enabled and (boost::get_system_time() - _io_impl->last_send_time) > boost::posix_time::milliseconds(100)){
274
            this->tx_stream_on_off(false);
275
        }
276

    
277
        //always poll regardless of enabled so we can clear the conditions
278
        _fx2_ctrl->usrp_control_read(
279
            VRQ_GET_STATUS, 0, GS_TX_UNDERRUN, &underflow, sizeof(underflow)
280
        );
281
        _fx2_ctrl->usrp_control_read(
282
            VRQ_GET_STATUS, 0, GS_RX_OVERRUN, &overflow, sizeof(overflow)
283
        );
284

    
285
        //handle message generation for xerflow conditions
286
        if (_tx_enabled and underflow){
287
            async_metadata.time_spec = _soft_time_ctrl->get_time();
288
            _soft_time_ctrl->get_async_queue().push_with_pop_on_full(async_metadata);
289
            UHD_MSG(fastpath) << "U";
290
        }
291
        if (_rx_enabled and overflow){
292
            inline_metadata.time_spec = _soft_time_ctrl->get_time();
293
            _soft_time_ctrl->get_inline_queue().push_with_pop_on_full(inline_metadata);
294
            UHD_MSG(fastpath) << "O";
295
        }
296

    
297
        boost::this_thread::sleep(boost::posix_time::milliseconds(50));
298
    }}
299
    catch(const boost::thread_interrupted &){} //normal exit condition
300
    catch(const std::exception &e){
301
        UHD_MSG(error) << "The vandal caught an unexpected exception " << e.what() << std::endl;
302
    }
303
}
304

    
305
/***********************************************************************
306
 * RX streamer wrapper that talks to soft time control
307
 **********************************************************************/
308
class usrp1_recv_packet_streamer : public sph::recv_packet_handler, public rx_streamer{
309
public:
310
    usrp1_recv_packet_streamer(const size_t max_num_samps, soft_time_ctrl::sptr stc){
311
        _max_num_samps = max_num_samps;
312
        _stc = stc;
313
    }
314

    
315
    size_t get_num_channels(void) const{
316
        return this->size();
317
    }
318

    
319
    size_t get_max_num_samps(void) const{
320
        return _max_num_samps;
321
    }
322

    
323
    size_t recv(
324
        const rx_streamer::buffs_type &buffs,
325
        const size_t nsamps_per_buff,
326
        uhd::rx_metadata_t &metadata,
327
        double timeout
328
    ){
329
        //interleave a "soft" inline message into the receive stream:
330
        if (_stc->get_inline_queue().pop_with_haste(metadata)) return 0;
331

    
332
        size_t num_samps_recvd = sph::recv_packet_handler::recv(
333
            buffs, nsamps_per_buff, metadata, timeout
334
        );
335

    
336
        return _stc->recv_post(metadata, num_samps_recvd);
337
    }
338

    
339
private:
340
    size_t _max_num_samps;
341
    soft_time_ctrl::sptr _stc;
342
};
343

    
344
/***********************************************************************
345
 * TX streamer wrapper that talks to soft time control
346
 **********************************************************************/
347
class usrp1_send_packet_streamer : public sph::send_packet_handler, public tx_streamer{
348
public:
349
    usrp1_send_packet_streamer(const size_t max_num_samps, soft_time_ctrl::sptr stc, boost::function<void(bool)> tx_enb_fcn){
350
        _max_num_samps = max_num_samps;
351
        this->set_max_samples_per_packet(_max_num_samps);
352
        _stc = stc;
353
        _tx_enb_fcn = tx_enb_fcn;
354
    }
355

    
356
    size_t get_num_channels(void) const{
357
        return this->size();
358
    }
359

    
360
    size_t get_max_num_samps(void) const{
361
        return _max_num_samps;
362
    }
363

    
364
    size_t send(
365
        const tx_streamer::buffs_type &buffs,
366
        const size_t nsamps_per_buff,
367
        const uhd::tx_metadata_t &metadata,
368
        double timeout
369
    ){
370
        if (_stc->send_pre(metadata, timeout)) return 0;
371

    
372
        _tx_enb_fcn(true); //always enable (it will do the right thing)
373
        size_t num_samps_sent = sph::send_packet_handler::send(
374
            buffs, nsamps_per_buff, metadata, timeout
375
        );
376

    
377
        //handle eob flag (commit the buffer, //disable the DACs)
378
        //check num samps sent to avoid flush on incomplete/timeout
379
        if (metadata.end_of_burst and num_samps_sent == nsamps_per_buff){
380
            async_metadata_t metadata;
381
            metadata.channel = 0;
382
            metadata.has_time_spec = true;
383
            metadata.time_spec = _stc->get_time();
384
            metadata.event_code = async_metadata_t::EVENT_CODE_BURST_ACK;
385
            _stc->get_async_queue().push_with_pop_on_full(metadata);
386
            _tx_enb_fcn(false);
387
        }
388

    
389
        return num_samps_sent;
390
    }
391

    
392
private:
393
    size_t _max_num_samps;
394
    soft_time_ctrl::sptr _stc;
395
    boost::function<void(bool)> _tx_enb_fcn;
396
};
397

    
398
/***********************************************************************
399
 * Properties callback methods below
400
 **********************************************************************/
401
void usrp1_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){
402

    
403
    //sanity checking
404
    validate_subdev_spec(_tree, spec, "rx");
405

    
406
    _rx_subdev_spec = spec; //shadow
407

    
408
    //set the mux and set the number of rx channels
409
    std::vector<mapping_pair_t> mapping;
410
    BOOST_FOREACH(const subdev_spec_pair_t &pair, spec){
411
        const std::string conn = _tree->access<std::string>(str(boost::format(
412
            "/mboards/0/dboards/%s/rx_frontends/%s/connection"
413
        ) % pair.db_name % pair.sd_name)).get();
414
        mapping.push_back(std::make_pair(pair.db_name, conn));
415
    }
416
    bool s = this->disable_rx();
417
    _iface->poke32(FR_RX_MUX, calc_rx_mux(mapping));
418
    this->restore_rx(s);
419
}
420

    
421
void usrp1_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){
422

    
423
    //sanity checking
424
    validate_subdev_spec(_tree, spec, "tx");
425

    
426
    _tx_subdev_spec = spec; //shadow
427

    
428
    //set the mux and set the number of tx channels
429
    std::vector<mapping_pair_t> mapping;
430
    BOOST_FOREACH(const subdev_spec_pair_t &pair, spec){
431
        const std::string conn = _tree->access<std::string>(str(boost::format(
432
            "/mboards/0/dboards/%s/tx_frontends/%s/connection"
433
        ) % pair.db_name % pair.sd_name)).get();
434
        mapping.push_back(std::make_pair(pair.db_name, conn));
435
    }
436
    bool s = this->disable_tx();
437
    _iface->poke32(FR_TX_MUX, calc_tx_mux(mapping));
438
    this->restore_tx(s);
439
}
440

    
441
double usrp1_impl::update_rx_samp_rate(size_t dspno, const double samp_rate){
442

    
443
    const size_t rate = uhd::clip<size_t>(
444
        boost::math::iround(_master_clock_rate / samp_rate), size_t(std::ceil(_master_clock_rate / 16e6)), 256
445
    );
446

    
447
    if (dspno == 0){ //only care if dsp0 is set since its homogeneous
448
        bool s = this->disable_rx();
449
        _iface->poke32(FR_DECIM_RATE, rate/2 - 1);
450
        this->restore_rx(s);
451

    
452
        //update the streamer if created
453
        boost::shared_ptr<usrp1_recv_packet_streamer> my_streamer =
454
            boost::dynamic_pointer_cast<usrp1_recv_packet_streamer>(_rx_streamer.lock());
455
        if (my_streamer.get() != NULL){
456
            my_streamer->set_samp_rate(_master_clock_rate / rate);
457
        }
458
    }
459

    
460
    return _master_clock_rate / rate;
461
}
462

    
463
double usrp1_impl::update_tx_samp_rate(size_t dspno, const double samp_rate){
464

    
465
    const size_t rate = uhd::clip<size_t>(
466
        boost::math::iround(_master_clock_rate / samp_rate), size_t(std::ceil(_master_clock_rate / 8e6)), 256
467
    );
468

    
469
    if (dspno == 0){ //only care if dsp0 is set since its homogeneous
470
        bool s = this->disable_tx();
471
        _iface->poke32(FR_INTERP_RATE, rate/2 - 1);
472
        this->restore_tx(s);
473

    
474
        //update the streamer if created
475
        boost::shared_ptr<usrp1_send_packet_streamer> my_streamer =
476
            boost::dynamic_pointer_cast<usrp1_send_packet_streamer>(_tx_streamer.lock());
477
        if (my_streamer.get() != NULL){
478
            my_streamer->set_samp_rate(_master_clock_rate / rate);
479
        }
480
    }
481

    
482
    return _master_clock_rate / rate;
483
}
484

    
485
void usrp1_impl::update_rates(void){
486
    const fs_path mb_path = "/mboards/0";
487
    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){
488
        _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").update();
489
    }
490
    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){
491
        _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").update();
492
    }
493
}
494

    
495
double usrp1_impl::update_rx_dsp_freq(const size_t dspno, const double freq_){
496

    
497
    //correct for outside of rate (wrap around)
498
    double freq = std::fmod(freq_, _master_clock_rate);
499
    if (std::abs(freq) > _master_clock_rate/2.0)
500
        freq -= boost::math::sign(freq)*_master_clock_rate;
501

    
502
    //calculate the freq register word (signed)
503
    UHD_ASSERT_THROW(std::abs(freq) <= _master_clock_rate/2.0);
504
    static const double scale_factor = std::pow(2.0, 32);
505
    const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _master_clock_rate) * scale_factor));
506

    
507
    static const boost::uint32_t dsp_index_to_reg_val[4] = {
508
        FR_RX_FREQ_0, FR_RX_FREQ_1, FR_RX_FREQ_2, FR_RX_FREQ_3
509
    };
510
    _iface->poke32(dsp_index_to_reg_val[dspno], ~freq_word + 1);
511

    
512
    return (double(freq_word) / scale_factor) * _master_clock_rate;
513
}
514

    
515
double usrp1_impl::update_tx_dsp_freq(const size_t dspno, const double freq){
516
    //map the freq shift key to a subdev spec to a particular codec chip
517
    _dbc[_tx_subdev_spec.at(dspno).db_name].codec->set_duc_freq(freq, _master_clock_rate);
518
    return freq; //assume infinite precision
519
}
520

    
521
/***********************************************************************
522
 * Async Data
523
 **********************************************************************/
524
bool usrp1_impl::recv_async_msg(
525
    async_metadata_t &async_metadata, double timeout
526
){
527
    boost::this_thread::disable_interruption di; //disable because the wait can throw
528
    return _soft_time_ctrl->get_async_queue().pop_with_timed_wait(async_metadata, timeout);
529
}
530

    
531
/***********************************************************************
532
 * Receive streamer
533
 **********************************************************************/
534
rx_streamer::sptr usrp1_impl::get_rx_stream(const uhd::stream_args_t &args){
535
    if (args.otw_format == "sc16"){
536
        _iface->poke32(FR_RX_FORMAT, 0
537
            | (0 << bmFR_RX_FORMAT_SHIFT_SHIFT)
538
            | (16 << bmFR_RX_FORMAT_WIDTH_SHIFT)
539
            | bmFR_RX_FORMAT_WANT_Q
540
        );
541
    }
542
    else if (args.otw_format == "sc8"){
543
        _iface->poke32(FR_RX_FORMAT, 0
544
            | (8 << bmFR_RX_FORMAT_SHIFT_SHIFT)
545
            | (8 << bmFR_RX_FORMAT_WIDTH_SHIFT)
546
            | bmFR_RX_FORMAT_WANT_Q
547
            | bmFR_RX_FORMAT_BYPASS_HB //needed for 16Msps
548
        );
549
    }
550
    else{
551
        throw uhd::value_error("USRP1 RX cannot handle requested wire format: " + args.otw_format);
552
    }
553

    
554
    //map an empty channel set to chan0
555
    const std::vector<size_t> channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
556

    
557
    //calculate packet size
558
    const size_t bpp = _data_transport->get_recv_frame_size()/channels.size();
559
    const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format);
560

    
561
    //make the new streamer given the samples per packet
562
    boost::shared_ptr<usrp1_recv_packet_streamer> my_streamer =
563
        boost::make_shared<usrp1_recv_packet_streamer>(spp, _soft_time_ctrl);
564

    
565
    //init some streamer stuff
566
    my_streamer->set_tick_rate(_master_clock_rate);
567
    my_streamer->set_vrt_unpacker(&usrp1_bs_vrt_unpacker);
568
    my_streamer->set_xport_chan_get_buff(0, boost::bind(
569
        &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1
570
    ));
571

    
572
    //set the converter
573
    uhd::convert::id_type id;
574
    id.input_markup = args.otw_format + "_item16_usrp1";
575
    id.num_inputs = 1;
576
    id.output_markup = args.cpu_format;
577
    id.num_outputs = channels.size();
578
    id.args = args.args;
579
    my_streamer->set_converter(id);
580

    
581
    //save as weak ptr for update access
582
    _rx_streamer = my_streamer;
583

    
584
    //sets all tick and samp rates on this streamer
585
    this->update_rates();
586

    
587
    return my_streamer;
588
}
589

    
590
/***********************************************************************
591
 * Transmit streamer
592
 **********************************************************************/
593
tx_streamer::sptr usrp1_impl::get_tx_stream(const uhd::stream_args_t &args){
594
    if (args.otw_format != "sc16"){
595
        throw uhd::value_error("USRP1 TX cannot handle requested wire format: " + args.otw_format);
596
    }
597

    
598
    //map an empty channel set to chan0
599
    const std::vector<size_t> channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
600

    
601
    //calculate packet size
602
    const size_t bpp = _data_transport->get_send_frame_size()/channels.size();
603
    const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format);
604

    
605
    //make the new streamer given the samples per packet
606
    boost::function<void(bool)> tx_fcn = boost::bind(&usrp1_impl::tx_stream_on_off, this, _1);
607
    boost::shared_ptr<usrp1_send_packet_streamer> my_streamer =
608
        boost::make_shared<usrp1_send_packet_streamer>(spp, _soft_time_ctrl, tx_fcn);
609

    
610
    //init some streamer stuff
611
    my_streamer->set_tick_rate(_master_clock_rate);
612
    my_streamer->set_vrt_packer(&usrp1_bs_vrt_packer);
613
    my_streamer->set_xport_chan_get_buff(0, boost::bind(
614
        &usrp1_impl::io_impl::get_send_buff, _io_impl.get(), _1
615
    ));
616

    
617
    //set the converter
618
    uhd::convert::id_type id;
619
    id.input_markup = args.cpu_format;
620
    id.num_inputs = channels.size();
621
    id.output_markup = args.otw_format + "_item16_usrp1";
622
    id.num_outputs = 1;
623
    id.args = args.args;
624
    my_streamer->set_converter(id);
625

    
626
    //save as weak ptr for update access
627
    _tx_streamer = my_streamer;
628

    
629
    //sets all tick and samp rates on this streamer
630
    this->update_rates();
631

    
632
    return my_streamer;
633
}