Statistics
| Branch: | Tag: | Revision:

root / host / lib / transport / super_send_packet_handler.hpp @ 00b6d835

History | View | Annotate | Download (10.3 KB)

1
//
2
// Copyright 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
#ifndef INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP
19
#define INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP
20

    
21
#include <uhd/config.hpp>
22
#include <uhd/exception.hpp>
23
#include <uhd/convert.hpp>
24
#include <uhd/stream.hpp>
25
#include <uhd/utils/msg.hpp>
26
#include <uhd/utils/byteswap.hpp>
27
#include <uhd/types/metadata.hpp>
28
#include <uhd/transport/vrt_if_packet.hpp>
29
#include <uhd/transport/zero_copy.hpp>
30
#include <boost/thread/thread_time.hpp>
31
#include <boost/foreach.hpp>
32
#include <boost/function.hpp>
33
#include <iostream>
34
#include <vector>
35

    
36
namespace uhd{ namespace transport{ namespace sph{
37

    
38
/***********************************************************************
39
 * Super send packet handler
40
 *
41
 * A send packet handler represents a group of channels.
42
 * The channel group shares a common sample rate.
43
 * All channels are sent in unison in send().
44
 **********************************************************************/
45
class send_packet_handler{
46
public:
47
    typedef boost::function<managed_send_buffer::sptr(double)> get_buff_type;
48
    typedef void(*vrt_packer_type)(boost::uint32_t *, vrt::if_packet_info_t &);
49
    //typedef boost::function<void(boost::uint32_t *, vrt::if_packet_info_t &)> vrt_packer_type;
50

    
51
    /*!
52
     * Make a new packet handler for send
53
     * \param size the number of transport channels
54
     */
55
    send_packet_handler(const size_t size = 1):
56
        _next_packet_seq(0)
57
    {
58
        this->resize(size);
59
        this->set_scale_factor(32767.);
60
    }
61

    
62
    //! Resize the number of transport channels
63
    void resize(const size_t size){
64
        if (this->size() == size) return;
65
        _props.resize(size);
66
        static const boost::uint64_t zero = 0;
67
        _zero_buffs.resize(size, &zero);
68
    }
69

    
70
    //! Get the channel width of this handler
71
    size_t size(void) const{
72
        return _props.size();
73
    }
74

    
75
    //! Setup the vrt packer function and offset
76
    void set_vrt_packer(const vrt_packer_type &vrt_packer, const size_t header_offset_words32 = 0){
77
        _vrt_packer = vrt_packer;
78
        _header_offset_words32 = header_offset_words32;
79
    }
80

    
81
    //! Set the rate of ticks per second
82
    void set_tick_rate(const double rate){
83
        _tick_rate = rate;
84
    }
85

    
86
    //! Set the rate of samples per second
87
    void set_samp_rate(const double rate){
88
        _samp_rate = rate;
89
    }
90

    
91
    /*!
92
     * Set the function to get a managed buffer.
93
     * \param xport_chan which transport channel
94
     * \param get_buff the getter function
95
     */
96
    void set_xport_chan_get_buff(const size_t xport_chan, const get_buff_type &get_buff){
97
        _props.at(xport_chan).get_buff = get_buff;
98
    }
99

    
100
    //! Set the conversion routine for all channels
101
    void set_converter(const uhd::convert::id_type &id){
102
        _io_buffs.resize(id.num_inputs);
103
        _converter = uhd::convert::get_converter(id);
104
        _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.output_format);
105
        _bytes_per_cpu_item = uhd::convert::get_bytes_per_item(id.input_format);
106
    }
107

    
108
    /*!
109
     * Set the maximum number of samples per host packet.
110
     * Ex: A USRP1 in dual channel mode would be half.
111
     * \param num_samps the maximum samples in a packet
112
     */
113
    void set_max_samples_per_packet(const size_t num_samps){
114
        _max_samples_per_packet = num_samps;
115
    }
116

    
117
    //! Set the scale factor used in float conversion
118
    void set_scale_factor(const double scale_factor){
119
        _scale_factor = scale_factor;
120
    }
121

    
122
    /*******************************************************************
123
     * Send:
124
     * The entry point for the fast-path send calls.
125
     * Dispatch into combinations of single packet send calls.
126
     ******************************************************************/
127
    UHD_INLINE size_t send(
128
        const uhd::tx_streamer::buffs_type &buffs,
129
        const size_t nsamps_per_buff,
130
        const uhd::tx_metadata_t &metadata,
131
        const double timeout
132
    ){
133
        //translate the metadata to vrt if packet info
134
        vrt::if_packet_info_t if_packet_info;
135
        if_packet_info.has_sid = false;
136
        if_packet_info.has_cid = false;
137
        if_packet_info.has_tlr = false;
138
        if_packet_info.has_tsi = metadata.has_time_spec;
139
        if_packet_info.has_tsf = metadata.has_time_spec;
140
        if_packet_info.tsi     = boost::uint32_t(metadata.time_spec.get_full_secs());
141
        if_packet_info.tsf     = boost::uint64_t(metadata.time_spec.get_tick_count(_tick_rate));
142
        if_packet_info.sob     = metadata.start_of_burst;
143
        if_packet_info.eob     = metadata.end_of_burst;
144

    
145
        if (nsamps_per_buff <= _max_samples_per_packet){
146

    
147
            //TODO remove this code when sample counts of zero are supported by hardware
148
            #ifndef SSPH_DONT_PAD_TO_ONE
149
            if (nsamps_per_buff == 0) return send_one_packet(
150
                _zero_buffs, 1, if_packet_info, timeout
151
            ) & 0x0;
152
            #endif
153

    
154
            return send_one_packet(buffs, nsamps_per_buff, if_packet_info, timeout);
155
        }
156
        size_t total_num_samps_sent = 0;
157

    
158
        //false until final fragment
159
        if_packet_info.eob = false;
160

    
161
        const size_t num_fragments = (nsamps_per_buff-1)/_max_samples_per_packet;
162
        const size_t final_length = ((nsamps_per_buff-1)%_max_samples_per_packet)+1;
163

    
164
        //loop through the following fragment indexes
165
        for (size_t i = 0; i < num_fragments; i++){
166

    
167
            //send a fragment with the helper function
168
            const size_t num_samps_sent = send_one_packet(
169
                buffs, _max_samples_per_packet,
170
                if_packet_info, timeout,
171
                total_num_samps_sent*_bytes_per_cpu_item
172
            );
173
            total_num_samps_sent += num_samps_sent;
174
            if (num_samps_sent == 0) return total_num_samps_sent;
175

    
176
            //setup metadata for the next fragment
177
            const time_spec_t time_spec = metadata.time_spec + time_spec_t(0, total_num_samps_sent, _samp_rate);
178
            if_packet_info.tsi = boost::uint32_t(time_spec.get_full_secs());
179
            if_packet_info.tsf = boost::uint64_t(time_spec.get_tick_count(_tick_rate));
180
            if_packet_info.sob = false;
181

    
182
        }
183

    
184
        //send the final fragment with the helper function
185
        if_packet_info.eob = metadata.end_of_burst;
186
        return total_num_samps_sent + send_one_packet(
187
            buffs, final_length,
188
            if_packet_info, timeout,
189
            total_num_samps_sent*_bytes_per_cpu_item
190
        );
191
    }
192

    
193
private:
194

    
195
    vrt_packer_type _vrt_packer;
196
    size_t _header_offset_words32;
197
    double _tick_rate, _samp_rate;
198
    struct xport_chan_props_type{
199
        get_buff_type get_buff;
200
    };
201
    std::vector<xport_chan_props_type> _props;
202
    std::vector<const void *> _io_buffs; //used in conversion
203
    size_t _bytes_per_otw_item; //used in conversion
204
    size_t _bytes_per_cpu_item; //used in conversion
205
    uhd::convert::function_type _converter; //used in conversion
206
    size_t _max_samples_per_packet;
207
    std::vector<const void *> _zero_buffs;
208
    size_t _next_packet_seq;
209
    double _scale_factor;
210

    
211
    /*******************************************************************
212
     * Send a single packet:
213
     ******************************************************************/
214
    UHD_INLINE size_t send_one_packet(
215
        const uhd::tx_streamer::buffs_type &buffs,
216
        const size_t nsamps_per_buff,
217
        vrt::if_packet_info_t &if_packet_info,
218
        const double timeout,
219
        const size_t buffer_offset_bytes = 0
220
    ){
221
        //load the rest of the if_packet_info in here
222
        if_packet_info.num_payload_bytes = nsamps_per_buff*_io_buffs.size()*_bytes_per_otw_item;
223
        if_packet_info.num_payload_words32 = (if_packet_info.num_payload_bytes + 3/*round up*/)/sizeof(boost::uint32_t);
224
        if_packet_info.packet_count = _next_packet_seq;
225

    
226
        size_t buff_index = 0;
227
        BOOST_FOREACH(xport_chan_props_type &props, _props){
228
            managed_send_buffer::sptr buff = props.get_buff(timeout);
229
            if (buff.get() == NULL) return 0; //timeout
230

    
231
            //fill a vector with pointers to the io buffers
232
            BOOST_FOREACH(const void *&io_buff, _io_buffs){
233
                io_buff = reinterpret_cast<const char *>(buffs[buff_index++]) + buffer_offset_bytes;
234
            }
235
            boost::uint32_t *otw_mem = buff->cast<boost::uint32_t *>() + _header_offset_words32;
236

    
237
            //pack metadata into a vrt header
238
            _vrt_packer(otw_mem, if_packet_info);
239
            otw_mem += if_packet_info.num_header_words32;
240

    
241
            //copy-convert the samples into the send buffer
242
            _converter(_io_buffs, otw_mem, nsamps_per_buff, _scale_factor);
243

    
244
            //commit the samples to the zero-copy interface
245
            size_t num_bytes_total = (_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t);
246
            buff->commit(num_bytes_total);
247

    
248
        }
249
        _next_packet_seq++; //increment sequence after commits
250
        return nsamps_per_buff;
251
    }
252
};
253

    
254
class send_packet_streamer : public send_packet_handler, public tx_streamer{
255
public:
256
    send_packet_streamer(const size_t max_num_samps){
257
        _max_num_samps = max_num_samps;
258
        this->set_max_samples_per_packet(_max_num_samps);
259
    }
260

    
261
    size_t get_num_channels(void) const{
262
        return this->size();
263
    }
264

    
265
    size_t get_max_num_samps(void) const{
266
        return _max_num_samps;
267
    }
268

    
269
    size_t send(
270
        const tx_streamer::buffs_type &buffs,
271
        const size_t nsamps_per_buff,
272
        const uhd::tx_metadata_t &metadata,
273
        const double timeout
274
    ){
275
        return send_packet_handler::send(buffs, nsamps_per_buff, metadata, timeout);
276
    }
277

    
278
private:
279
    size_t _max_num_samps;
280
};
281

    
282
}}} //namespace
283

    
284
#endif /* INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP */