Statistics
| Branch: | Tag: | Revision:

root / gr-uhd / lib / gr_uhd_usrp_sink.cc @ 20a083fe

History | View | Annotate | Download (16.3 kB)

1
/*
2
 * Copyright 2010-2012 Free Software Foundation, Inc.
3
 * 
4
 * This file is part of GNU Radio
5
 * 
6
 * GNU Radio is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3, or (at your option)
9
 * any later version.
10
 * 
11
 * GNU Radio is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 * 
16
 * You should have received a copy of the GNU General Public License
17
 * along with GNU Radio; see the file COPYING.  If not, write to
18
 * the Free Software Foundation, Inc., 51 Franklin Street,
19
 * Boston, MA 02110-1301, USA.
20
 */
21
22
#include <gr_uhd_usrp_sink.h>
23
#include <gr_io_signature.h>
24
#include <stdexcept>
25
#include <boost/make_shared.hpp>
26
#include "gr_uhd_common.h"
27
28
static const pmt::pmt_t SOB_KEY = pmt::pmt_string_to_symbol("tx_sob");
29
static const pmt::pmt_t EOB_KEY = pmt::pmt_string_to_symbol("tx_eob");
30
static const pmt::pmt_t TIME_KEY = pmt::pmt_string_to_symbol("tx_time");
31
32
#include <uhd/convert.hpp>
33
inline gr_io_signature_sptr args_to_io_sig(const uhd::stream_args_t &args){
34
    const size_t nchan = std::max<size_t>(args.channels.size(), 1);
35
    #ifdef GR_UHD_USE_STREAM_API
36
        const size_t size = uhd::convert::get_bytes_per_item(args.cpu_format);
37
    #else
38
        size_t size = 0;
39
        if (args.cpu_format == "fc32") size = 8;
40
        if (args.cpu_format == "sc16") size = 4;
41
    #endif
42
    return gr_make_io_signature(nchan, nchan, size);
43
}
44
45
/***********************************************************************
46
 * UHD Multi USRP Sink Impl
47
 **********************************************************************/
48
class uhd_usrp_sink_impl : public uhd_usrp_sink{
49
public:
50
    uhd_usrp_sink_impl(
51
        const uhd::device_addr_t &device_addr,
52
        const uhd::stream_args_t &stream_args
53
    ):
54
        gr_sync_block(
55
            "gr uhd usrp sink",
56
            args_to_io_sig(stream_args),
57
            gr_make_io_signature(0, 0, 0)
58
        ),
59
        _stream_args(stream_args),
60
        _nchan(std::max<size_t>(1, stream_args.channels.size())),
61
        _stream_now(_nchan == 1),
62
        _start_time_set(false)
63
    {
64
        if (stream_args.cpu_format == "fc32") _type = boost::make_shared<uhd::io_type_t>(uhd::io_type_t::COMPLEX_FLOAT32);
65
        if (stream_args.cpu_format == "sc16") _type = boost::make_shared<uhd::io_type_t>(uhd::io_type_t::COMPLEX_INT16);
66
        _dev = uhd::usrp::multi_usrp::make(device_addr);
67
    }
68
69
    uhd::dict<std::string, std::string> get_usrp_info(size_t mboard, size_t chan){
70
        #ifdef UHD_USRP_MULTI_USRP_GET_USRP_INFO_API
71
        return _dev->get_usrp_info(mboard, chan);
72
        #else
73
        throw std::runtime_error("not implemented in this version");
74
        #endif
75
    }
76
77
    void set_subdev_spec(const std::string &spec, size_t mboard){
78
        return _dev->set_tx_subdev_spec(spec, mboard);
79
    }
80
81
    void set_samp_rate(double rate){
82
        _dev->set_tx_rate(rate);
83
        _sample_rate = this->get_samp_rate();
84
    }
85
86
    double get_samp_rate(void){
87
        return _dev->get_tx_rate();
88
    }
89
90
    uhd::meta_range_t get_samp_rates(void){
91
        #ifdef UHD_USRP_MULTI_USRP_GET_RATES_API
92
        return _dev->get_tx_rates();
93
        #else
94
        throw std::runtime_error("not implemented in this version");
95
        #endif
96
    }
97
98
    uhd::tune_result_t set_center_freq(
99
        const uhd::tune_request_t tune_request, size_t chan
100
    ){
101
        return _dev->set_tx_freq(tune_request, chan);
102
    }
103
104
    double get_center_freq(size_t chan){
105
        return _dev->get_tx_freq(chan);
106
    }
107
108
    uhd::freq_range_t get_freq_range(size_t chan){
109
        return _dev->get_tx_freq_range(chan);
110
    }
111
112
    void set_gain(double gain, size_t chan){
113
        return _dev->set_tx_gain(gain, chan);
114
    }
115
116
    void set_gain(double gain, const std::string &name, size_t chan){
117
        return _dev->set_tx_gain(gain, name, chan);
118
    }
119
120
    double get_gain(size_t chan){
121
        return _dev->get_tx_gain(chan);
122
    }
123
124
    double get_gain(const std::string &name, size_t chan){
125
        return _dev->get_tx_gain(name, chan);
126
    }
127
128
    std::vector<std::string> get_gain_names(size_t chan){
129
        return _dev->get_tx_gain_names(chan);
130
    }
131
132
    uhd::gain_range_t get_gain_range(size_t chan){
133
        return _dev->get_tx_gain_range(chan);
134
    }
135
136
    uhd::gain_range_t get_gain_range(const std::string &name, size_t chan){
137
        return _dev->get_tx_gain_range(name, chan);
138
    }
139
140
    void set_antenna(const std::string &ant, size_t chan){
141
        return _dev->set_tx_antenna(ant, chan);
142
    }
143
144
    std::string get_antenna(size_t chan){
145
        return _dev->get_tx_antenna(chan);
146
    }
147
148
    std::vector<std::string> get_antennas(size_t chan){
149
        return _dev->get_tx_antennas(chan);
150
    }
151
152
    void set_bandwidth(double bandwidth, size_t chan){
153
        return _dev->set_tx_bandwidth(bandwidth, chan);
154
    }
155
156
    void set_dc_offset(const std::complex<double> &offset, size_t chan){
157
        #ifdef UHD_USRP_MULTI_USRP_FRONTEND_CAL_API
158
        return _dev->set_tx_dc_offset(offset, chan);
159
        #else
160
        throw std::runtime_error("not implemented in this version");
161
        #endif
162
    }
163
164
    void set_iq_balance(const std::complex<double> &correction, size_t chan){
165
        #ifdef UHD_USRP_MULTI_USRP_FRONTEND_CAL_API
166
        return _dev->set_tx_iq_balance(correction, chan);
167
        #else
168
        throw std::runtime_error("not implemented in this version");
169
        #endif
170
    }
171
172
    uhd::sensor_value_t get_sensor(const std::string &name, size_t chan){
173
        return _dev->get_tx_sensor(name, chan);
174
    }
175
176
    std::vector<std::string> get_sensor_names(size_t chan){
177
        return _dev->get_tx_sensor_names(chan);
178
    }
179
180
    uhd::sensor_value_t get_mboard_sensor(const std::string &name, size_t mboard){
181
        return _dev->get_mboard_sensor(name, mboard);
182
    }
183
184
    std::vector<std::string> get_mboard_sensor_names(size_t mboard){
185
        return _dev->get_mboard_sensor_names(mboard);
186
    }
187
188
    void set_clock_config(const uhd::clock_config_t &clock_config, size_t mboard){
189
        return _dev->set_clock_config(clock_config, mboard);
190
    }
191
192
    void set_time_source(const std::string &source, const size_t mboard){
193
        #ifdef UHD_USRP_MULTI_USRP_REF_SOURCES_API
194
        return _dev->set_time_source(source, mboard);
195
        #else
196
        throw std::runtime_error("not implemented in this version");
197
        #endif
198
    }
199
200
    std::string get_time_source(const size_t mboard){
201
        #ifdef UHD_USRP_MULTI_USRP_REF_SOURCES_API
202
        return _dev->get_time_source(mboard);
203
        #else
204
        throw std::runtime_error("not implemented in this version");
205
        #endif
206
    }
207
208
    std::vector<std::string> get_time_sources(const size_t mboard){
209
        #ifdef UHD_USRP_MULTI_USRP_REF_SOURCES_API
210
        return _dev->get_time_sources(mboard);
211
        #else
212
        throw std::runtime_error("not implemented in this version");
213
        #endif
214
    }
215
216
    void set_clock_source(const std::string &source, const size_t mboard){
217
        #ifdef UHD_USRP_MULTI_USRP_REF_SOURCES_API
218
        return _dev->set_clock_source(source, mboard);
219
        #else
220
        throw std::runtime_error("not implemented in this version");
221
        #endif
222
    }
223
224
    std::string get_clock_source(const size_t mboard){
225
        #ifdef UHD_USRP_MULTI_USRP_REF_SOURCES_API
226
        return _dev->get_clock_source(mboard);
227
        #else
228
        throw std::runtime_error("not implemented in this version");
229
        #endif
230
    }
231
232
    std::vector<std::string> get_clock_sources(const size_t mboard){
233
        #ifdef UHD_USRP_MULTI_USRP_REF_SOURCES_API
234
        return _dev->get_clock_sources(mboard);
235
        #else
236
        throw std::runtime_error("not implemented in this version");
237
        #endif
238
    }
239
240
    double get_clock_rate(size_t mboard){
241
        return _dev->get_master_clock_rate(mboard);
242
    }
243
244
    void set_clock_rate(double rate, size_t mboard){
245
        return _dev->set_master_clock_rate(rate, mboard);
246
    }
247
248
    uhd::time_spec_t get_time_now(size_t mboard = 0){
249
        return _dev->get_time_now(mboard);
250
    }
251
252
    uhd::time_spec_t get_time_last_pps(size_t mboard){
253
        return _dev->get_time_last_pps(mboard);
254
    }
255
256
    void set_time_now(const uhd::time_spec_t &time_spec, size_t mboard){
257
        return _dev->set_time_now(time_spec, mboard);
258
    }
259
260
    void set_time_next_pps(const uhd::time_spec_t &time_spec){
261
        return _dev->set_time_next_pps(time_spec);
262
    }
263
264
    void set_time_unknown_pps(const uhd::time_spec_t &time_spec){
265
        return _dev->set_time_unknown_pps(time_spec);
266
    }
267
268
    void set_command_time(const uhd::time_spec_t &time_spec, size_t mboard){
269
        #ifdef UHD_USRP_MULTI_USRP_COMMAND_TIME_API
270
        return _dev->set_command_time(time_spec, mboard);
271
        #else
272
        throw std::runtime_error("not implemented in this version");
273
        #endif
274
    }
275
276
    void clear_command_time(size_t mboard){
277
        #ifdef UHD_USRP_MULTI_USRP_COMMAND_TIME_API
278
        return _dev->clear_command_time(mboard);
279
        #else
280
        throw std::runtime_error("not implemented in this version");
281
        #endif
282
    }
283
284
    uhd::usrp::dboard_iface::sptr get_dboard_iface(size_t chan){
285
        return _dev->get_tx_dboard_iface(chan);
286
    }
287
288
    uhd::usrp::multi_usrp::sptr get_device(void){
289
        return _dev;
290
    }
291
292
    void set_user_register(const uint8_t addr, const uint32_t data, size_t mboard){
293
        #ifdef UHD_USRP_MULTI_USRP_USER_REGS_API
294
        _dev->set_user_register(addr, data, mboard);
295
        #else
296
        throw std::runtime_error("not implemented in this version");
297
        #endif
298
    }
299
300
301
/***********************************************************************
302
 * Work
303
 **********************************************************************/
304
    int work(
305
        int noutput_items,
306
        gr_vector_const_void_star &input_items,
307
        gr_vector_void_star &output_items
308
    ){
309
        int ninput_items = noutput_items; //cuz its a sync block
310
311
        //send a mid-burst packet with time spec
312
        _metadata.start_of_burst = false;
313
        _metadata.end_of_burst = false;
314
315
        //collect tags in this work()
316
        const uint64_t samp0_count = nitems_read(0);
317
        get_tags_in_range(_tags, 0, samp0_count, samp0_count + ninput_items);
318
        if (not _tags.empty()) this->tag_work(ninput_items);
319
320
        #ifdef GR_UHD_USE_STREAM_API
321
        //send all ninput_items with metadata
322
        const size_t num_sent = _tx_stream->send(
323
            input_items, ninput_items, _metadata, 1.0
324
        );
325
        #else
326
        const size_t num_sent = _dev->get_device()->send(
327
            input_items, ninput_items, _metadata,
328
            *_type, uhd::device::SEND_MODE_FULL_BUFF, 1.0
329
        );
330
        #endif
331
332
        //increment the timespec by the number of samples sent
333
        _metadata.time_spec += uhd::time_spec_t(0, num_sent, _sample_rate);
334
        return num_sent;
335
    }
336
337
/***********************************************************************
338
 * Tag Work
339
 **********************************************************************/
340
    inline void tag_work(int &ninput_items){
341
        //the for loop below assumes tags sorted by count low -> high
342
        std::sort(_tags.begin(), _tags.end(), gr_tag_t::offset_compare);
343
344
        //extract absolute sample counts
345
        const gr_tag_t &tag0 = _tags.front();
346
        const uint64_t tag0_count = tag0.offset;
347
        const uint64_t samp0_count = this->nitems_read(0);
348
349
        //only transmit nsamples from 0 to the first tag
350
        //this ensures that the next work starts on a tag
351
        if (samp0_count != tag0_count){
352
            ninput_items = tag0_count - samp0_count;
353
            return;
354
        }
355
356
        //time will not be set unless a time tag is found
357
        _metadata.has_time_spec = false;
358
359
        //process all of the tags found with the same count as tag0
360
        BOOST_FOREACH(const gr_tag_t &my_tag, _tags){
361
            const uint64_t my_tag_count = my_tag.offset;
362
            const pmt::pmt_t &key = my_tag.key;
363
            const pmt::pmt_t &value = my_tag.value;
364
365
            //determine how many samples to send...
366
            //from zero until the next tag or end of work
367
            if (my_tag_count != tag0_count){
368
                ninput_items = my_tag_count - samp0_count;
369
                break;
370
            }
371
372
            //handle end of burst with a mini end of burst packet
373
            else if (pmt::pmt_equal(key, EOB_KEY)){
374
                _metadata.end_of_burst = pmt::pmt_to_bool(value);
375
                ninput_items = 1;
376
                return;
377
            }
378
379
            //set the start of burst flag in the metadata
380
            else if (pmt::pmt_equal(key, SOB_KEY)){
381
                _metadata.start_of_burst = pmt::pmt_to_bool(value);
382
            }
383
384
            //set the time specification in the metadata
385
            else if (pmt::pmt_equal(key, TIME_KEY)){
386
                _metadata.has_time_spec = true;
387
                _metadata.time_spec = uhd::time_spec_t(
388
                    pmt::pmt_to_uint64(pmt_tuple_ref(value, 0)),
389
                    pmt::pmt_to_double(pmt_tuple_ref(value, 1))
390
                );
391
            }
392
        }
393
    }
394
395
    void set_start_time(const uhd::time_spec_t &time){
396
        _start_time = time;
397
        _start_time_set = true;
398
        _stream_now = false;
399
    }
400
401
    //Send an empty start-of-burst packet to begin streaming.
402
    //Set at a time in the near future to avoid late packets.
403
    bool start(void){
404
        #ifdef GR_UHD_USE_STREAM_API
405
        _tx_stream = _dev->get_tx_stream(_stream_args);
406
        #endif
407
408
        _metadata.start_of_burst = true;
409
        _metadata.end_of_burst = false;
410
        _metadata.has_time_spec = not _stream_now;
411
        if (_start_time_set){
412
            _start_time_set = false; //cleared for next run
413
            _metadata.time_spec = _start_time;
414
        }
415
        else{
416
            _metadata.time_spec = get_time_now() + uhd::time_spec_t(0.01);
417
        }
418
419
        #ifdef GR_UHD_USE_STREAM_API
420
        _tx_stream->send(
421
            gr_vector_const_void_star(_nchan), 0, _metadata, 1.0
422
        );
423
        #else
424
        _dev->get_device()->send(
425
            gr_vector_const_void_star(_nchan), 0, _metadata,
426
            *_type, uhd::device::SEND_MODE_ONE_PACKET, 1.0
427
        );
428
        #endif
429
        return true;
430
    }
431
432
    //Send an empty end-of-burst packet to end streaming.
433
    //Ending the burst avoids an underflow error on stop.
434
    bool stop(void){
435
        _metadata.start_of_burst = false;
436
        _metadata.end_of_burst = true;
437
        _metadata.has_time_spec = false;
438
439
        #ifdef GR_UHD_USE_STREAM_API
440
        _tx_stream->send(gr_vector_const_void_star(_nchan), 0, _metadata, 1.0);
441
        #else
442
        _dev->get_device()->send(
443
            gr_vector_const_void_star(_nchan), 0, _metadata,
444
            *_type, uhd::device::SEND_MODE_ONE_PACKET, 1.0
445
        );
446
        #endif
447
        return true;
448
    }
449
450
private:
451
    uhd::usrp::multi_usrp::sptr _dev;
452
    const uhd::stream_args_t _stream_args;
453
    boost::shared_ptr<uhd::io_type_t> _type;
454
    #ifdef GR_UHD_USE_STREAM_API
455
    uhd::tx_streamer::sptr _tx_stream;
456
    #endif
457
    size_t _nchan;
458
    bool _stream_now;
459
    uhd::tx_metadata_t _metadata;
460
    double _sample_rate;
461
462
    uhd::time_spec_t _start_time;
463
    bool _start_time_set;
464
465
    //stream tags related stuff
466
    std::vector<gr_tag_t> _tags;
467
};
468
469
/***********************************************************************
470
 * Make UHD Multi USRP Sink
471
 **********************************************************************/
472
boost::shared_ptr<uhd_usrp_sink> uhd_make_usrp_sink(
473
    const uhd::device_addr_t &device_addr,
474
    const uhd::io_type_t &io_type,
475
    size_t num_channels
476
){
477
    //fill in the streamer args
478
    uhd::stream_args_t stream_args;
479
    switch(io_type.tid){
480
    case uhd::io_type_t::COMPLEX_FLOAT32: stream_args.cpu_format = "fc32"; break;
481
    case uhd::io_type_t::COMPLEX_INT16: stream_args.cpu_format = "sc16"; break;
482
    default: throw std::runtime_error("only complex float and shorts known to work");
483
    }
484
    stream_args.otw_format = "sc16"; //only sc16 known to work
485
    for (size_t chan = 0; chan < num_channels; chan++)
486
        stream_args.channels.push_back(chan); //linear mapping
487
488
    return uhd_make_usrp_sink(device_addr, stream_args);
489
}
490
491
boost::shared_ptr<uhd_usrp_sink> uhd_make_usrp_sink(
492
    const uhd::device_addr_t &device_addr,
493
    const uhd::stream_args_t &stream_args
494
){
495
    gr_uhd_check_abi();
496
    return boost::shared_ptr<uhd_usrp_sink>(
497
        new uhd_usrp_sink_impl(device_addr, stream_args)
498
    );
499
}