1//
2// Copyright 2011-2013 Ettus Research LLC
3// Copyright 2018 Ettus Research, a National Instruments Company
4//
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
7
8#pragma once
9
10#include <uhd/config.hpp>
11#include <uhd/types/device_addr.hpp>
12#include <uhd/types/metadata.hpp>
13#include <uhd/types/ref_vector.hpp>
14#include <uhd/types/stream_cmd.hpp>
15#include <uhd/utils/noncopyable.hpp>
16#include <boost/utility.hpp>
17#include <memory>
18#include <string>
19#include <vector>
20
21namespace uhd {
22
23/*!
24 * A struct of parameters to construct a streamer.
25 *
26 * Here is an example of how a stream args object could be used in conjunction
27 * with uhd::device::get_rx_stream():
28 *
29 * \code{.cpp}
30 * // 1. Create the stream args object and initialize the data formats to fc32 and sc16:
31 * uhd::stream_args_t stream_args("fc32", "sc16");
32 * // 2. Set the channel list, we want 3 streamers coming from channels
33 * // 0, 1 and 2, in that order:
34 * stream_args.channels = {0, 1, 2};
35 * // 3. Set optional args:
36 * stream_args.args["spp"] = "200"; // 200 samples per packet
37 * // Now use these args to create an rx streamer:
38 * // (We assume that usrp is a valid uhd::usrp::multi_usrp)
39 * uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args);
40 * // Now, any calls to rx_stream must provide a vector of 3 buffers,
41 * // one per channel.
42 * \endcode
43 *
44 * \b Note: Not all combinations of CPU and OTW format have conversion support.
45 * You may however write and register your own conversion routines.
46 */
47struct UHD_API stream_args_t
48{
49 //! Convenience constructor for streamer args
50 stream_args_t(const std::string& cpu = "", const std::string& otw = "")
51 {
52 cpu_format = cpu;
53 otw_format = otw;
54 }
55
56 /*!
57 * The CPU format is a string that describes the format of host memory.
58 * Conversions for the following CPU formats have been implemented:
59 * - fc64 - complex<double>
60 * - fc32 - complex<float>
61 * - sc16 - complex<int16_t>
62 * - sc8 - complex<int8_t>
63 *
64 * The following are not implemented, but are listed to demonstrate naming convention:
65 * - f32 - float
66 * - f64 - double
67 * - s16 - int16_t
68 * - s8 - int8_t
69 *
70 * The CPU format can be chosen depending on what the application requires.
71 */
72 std::string cpu_format;
73
74 /*!
75 * The OTW format is a string that describes the format over-the-wire.
76 * The following over-the-wire formats have been implemented:
77 * - sc16 - Q16 I16
78 * - sc8 - Q8_1 I8_1 Q8_0 I8_0
79 * - sc12 (Only some devices)
80 *
81 * The following are not implemented, but are listed to demonstrate naming convention:
82 * - s16 - R16_1 R16_0
83 * - s8 - R8_3 R8_2 R8_1 R8_0
84 *
85 * Setting the OTW ("over-the-wire") format is, in theory, transparent to the
86 * application, but changing this can have some side effects. Using less bits for
87 * example (e.g. when going from `otw_format` `sc16` to `sc8`) will reduce the dynamic
88 * range, and increases quantization noise. On the other hand, it reduces the load on
89 * the data link and thus allows more bandwidth (a USRP N210 can work with 25 MHz
90 * bandwidth for 16-Bit complex samples, and 50 MHz for 8-Bit complex samples).
91 */
92 std::string otw_format;
93
94 /*!
95 * The args parameter is used to pass arbitrary key/value pairs.
96 * Possible keys used by args (depends on implementation):
97 *
98 * - fullscale: specifies the full-scale amplitude when using floats.
99 * By default, the fullscale amplitude under floating point is 1.0.
100 * Set the "fullscale" to scale the samples in the host to the
101 * expected input range and/or output range of your application.
102 *
103 * - peak: specifies a fractional sample level to calculate scaling with the sc8 wire
104 * format. When using sc8 samples over the wire, the device must scale samples (both
105 * on the host and in the device) to satisfy the dynamic range needs. The peak value
106 * specifies a fraction of the maximum sample level (1.0 = 100%). Set peak to
107 * max_sample_level/full_scale_level to ensure optimum dynamic range.
108 *
109 * - underflow_policy: how the TX DSP should recover from underflow.
110 * Possible options are "next_burst" or "next_packet".
111 * In the "next_burst" mode, the DSP drops incoming packets until a new burst is
112 * started. In the "next_packet" mode, the DSP starts transmitting again at the next
113 * packet.
114 *
115 * - spp: (samples per packet) controls the size of RX packets.
116 * When not specified, the packets are always maximum frame size.
117 * Users should specify this option to request smaller than default
118 * packets, probably with the intention of reducing packet latency.
119 *
120 * - noclear: Used by tx_dsp_core_200 and rx_dsp_core_200
121 *
122 * The following are not implemented, but are listed for conceptual purposes:
123 * - function: magnitude or phase/magnitude
124 * - units: numeric units like counts or dBm
125 *
126 * Other options are device-specific:
127 * - port, addr: Alternative receiver streamer destination.
128 */
129 device_addr_t args;
130
131 /*! List of channel numbers (only used by non-RFNoC devices)
132 *
133 * Note: For RFNoC devices, this value is not used. To create a streamer
134 * with multiple channels, the uhd::rfnoc::rfnoc_graph::create_tx_streamer()
135 * and uhd::rfnoc::rfnoc_graph::create_rx_streamer() API calls have a
136 * \p num_ports argument.
137 *
138 * For non-RFNoC devices (i.e., USRP1, B100, B200, N200), this argument
139 * defines how streamer channels map to the front-end selection (see also
140 * \ref config_subdev).
141 *
142 * A very simple example is a B210 with a subdev spec of `A:A A:B`. This
143 * means the device has two channels available.
144 *
145 * Setting `stream_args.channels = {0, 1}` therefore configures MIMO
146 * streaming from both channels. By switching the channel indexes,
147 * `stream_args.channels = {1, 0}`, the channels are switched and the first
148 * channel of the USRP is mapped to the second channel in the application.
149 *
150 * If only a single channel is used for streaming, e.g.,
151 * `stream_args.channels = {1}` would only select a single channel (in this
152 * case, the second one). When streaming a single channel from the B-side
153 * radio of a USRP, this is a more versatile solution than setting the
154 * subdev spec globally to "A:B".
155 *
156 * Leave this blank to default to channel 0 (single-channel application).
157 */
158 std::vector<size_t> channels;
159};
160
161/*!
162 * The RX streamer is the host interface to receiving samples.
163 * It represents the layer between the samples on the host
164 * and samples inside the device's receive DSP processing.
165 */
166class UHD_API rx_streamer : uhd::noncopyable
167{
168public:
169 typedef std::shared_ptr<rx_streamer> sptr;
170
171 virtual ~rx_streamer(void);
172
173 //! Get the number of channels associated with this streamer
174 virtual size_t get_num_channels(void) const = 0;
175
176 //! Get the max number of samples per buffer per packet
177 virtual size_t get_max_num_samps(void) const = 0;
178
179 //! Typedef for a pointer to a single, or a collection of recv buffers
180 typedef ref_vector<void*> buffs_type;
181
182 /*!
183 * Receive buffers containing samples described by the metadata.
184 *
185 * Receive handles fragmentation as follows:
186 * If the buffer has insufficient space to hold all samples
187 * that were received in a single packet over-the-wire,
188 * then the buffer will be completely filled and the implementation
189 * will hold a pointer into the remaining portion of the packet.
190 * Subsequent calls will load from the remainder of the packet,
191 * and will flag the metadata to show that this is a fragment.
192 * The next call to receive, after the remainder becomes exhausted,
193 * will perform an over-the-wire receive as usual.
194 * See the rx metadata fragment flags and offset fields for details.
195 *
196 * This is a blocking call and will not return until the number
197 * of samples returned have been written into each buffer.
198 * Under a timeout condition, the number of samples returned
199 * may be less than the number of samples specified.
200 *
201 * The one_packet option allows the user to guarantee that
202 * the call will return after a single packet has been processed.
203 * This may be useful to maintain packet boundaries in some cases.
204 *
205 * Note on threading: recv() is *not* thread-safe, to avoid locking
206 * overhead. The application calling recv() is responsible for making
207 * sure that not more than one thread can call recv() on the same streamer
208 * at the same time. If there are multiple streamers, receiving from
209 * different sources, then those may be called from different threads
210 * simultaneously.
211 *
212 * \section stream_rx_error_handling Error Handling
213 *
214 * \p metadata is a value that is set inside this function (effectively, a
215 * return value), and should be checked
216 * for potential error codes (see rx_metadata_t::error_code_t).
217 *
218 * The most common error code when something goes wrong is an overrun (also
219 * referred to as overflow: error_code_t::ERROR_CODE_OVERFLOW). This error
220 * code means that the device produced data faster than the application
221 * could read, and various buffers filled up leaving no more space for the
222 * device to write data to. Note that an overrun on the device will not
223 * immediatiely show up when calling recv(). Depending on the device
224 * implementation, there may be many more valid samples available before the
225 * device had to stop writing samples to the FIFO. Only when all valid
226 * samples are returned to the call site will the error code be set to
227 * "overrun". When this happens, all valid samples have been returned to
228 * application where recv() was called.
229 * If the device is streaming continuously, it will reset itself when the
230 * FIFO is cleared, and recv() can be called again to retrieve new, valid data.
231 *
232 * \param buffs a vector of writable memory to fill with samples
233 * \param nsamps_per_buff the size of each buffer in number of samples
234 * \param[out] metadata data to fill describing the buffer
235 * \param timeout the timeout in seconds to wait for a packet
236 * \param one_packet return after the first packet is received
237 * \return the number of samples received or 0 on error
238 */
239 virtual size_t recv(const buffs_type& buffs,
240 const size_t nsamps_per_buff,
241 rx_metadata_t& metadata,
242 const double timeout = 0.1,
243 const bool one_packet = false) = 0;
244
245 /*!
246 * Issue a stream command to the usrp device.
247 * This tells the usrp to send samples into the host.
248 * See the documentation for stream_cmd_t for more info.
249 *
250 * With multiple devices, the first stream command in a chain of commands
251 * should have a time spec in the near future and stream_now = false;
252 * to ensure that the packets can be aligned by their time specs.
253 *
254 * \param stream_cmd the stream command to issue
255 */
256 virtual void issue_stream_cmd(const stream_cmd_t& stream_cmd) = 0;
257};
258
259/*!
260 * The TX streamer is the host interface to transmitting samples.
261 * It represents the layer between the samples on the host
262 * and samples inside the device's transmit DSP processing.
263 */
264class UHD_API tx_streamer : uhd::noncopyable
265{
266public:
267 typedef std::shared_ptr<tx_streamer> sptr;
268
269 virtual ~tx_streamer(void);
270
271 //! Get the number of channels associated with this streamer
272 virtual size_t get_num_channels(void) const = 0;
273
274 //! Get the max number of samples per buffer per packet
275 virtual size_t get_max_num_samps(void) const = 0;
276
277 //! Typedef for a pointer to a single, or a collection of send buffers
278 typedef ref_vector<const void*> buffs_type;
279
280 /*!
281 * Send buffers containing samples described by the metadata.
282 *
283 * Send handles fragmentation as follows:
284 * If the buffer has more items than the maximum per packet,
285 * the send method will fragment the samples across several packets.
286 * Send will respect the burst flags when fragmenting to ensure
287 * that start of burst can only be set on the first fragment and
288 * that end of burst can only be set on the final fragment.
289 *
290 * This is a blocking call and will not return until the number
291 * of samples returned have been read out of each buffer.
292 * Under a timeout condition, the number of samples returned
293 * may be less than the number of samples specified.
294 *
295 * Note on threading: send() is *not* thread-safe, to avoid locking
296 * overhead. The application calling send() is responsible for making
297 * sure that not more than one thread can call send() on the same streamer
298 * at the same time. If there are multiple streamers, transmitting to
299 * different destinations, then those may be called from different threads
300 * simultaneously.
301 *
302 * \param buffs a vector of read-only memory containing samples
303 * \param nsamps_per_buff the number of samples to send, per buffer
304 * \param metadata data describing the buffer's contents
305 * \param timeout the timeout in seconds to wait on a packet
306 * \return the number of samples sent
307 */
308 virtual size_t send(const buffs_type& buffs,
309 const size_t nsamps_per_buff,
310 const tx_metadata_t& metadata,
311 const double timeout = 0.1) = 0;
312
313 /*!
314 * Receive an asynchronous message from this TX stream.
315 * \param async_metadata the metadata to be filled in
316 * \param timeout the timeout in seconds to wait for a message
317 * \return true when the async_metadata is valid, false for timeout
318 */
319 virtual bool recv_async_msg(
320 async_metadata_t& async_metadata, double timeout = 0.1) = 0;
321};
322
323} // namespace uhd