1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
|
/*! \page page_stream Device streaming
\tableofcontents
\section stream_intro Introduction to Streaming
The concept of streaming refers to the transportation of samples or other data
between host and device. A streamer is an object that facilitates such streaming.
An RX streamer (uhd::rx_streamer) allows the user to
receive data from the device. A TX streamer (uhd::tx_streamer) allows the user to transmit
data to the device.
For RX streaming, the following actions need to be taken:
- A streamer needs to be created (e.g., using multi_usrp::get_rx_stream() or
rfnoc_graph::get_rx_stream(), depending on what API is being used). Upon
creation of the streamer, typically all connections between the host and the
device are configured (e.g., how to send UDP data packets).
- When using the RFNoC API, it is necessary to manually connect the streamer
to the desired endpoint.
- When using the multi_usrp API, the RX streamer is automatically connected
to the radio/DSP chain.
- When creating an RX streamer, a uhd::stream_args_t object must be passed in
to configure the data types used on the link layer and in the host software,
as well as any other configurations that are required.
- To initiate streaming, typically a stream command needs to be issued to the
device to indicate that the host application is ready to receive samples. The
uhd::rx_streamer::issue_stream_cmd() API call can typically be used for this.
- As soon as streaming starts, the uhd::rx_streamer::recv() call needs to be
called regularly to accept the incoming data. If recv() is not called often
enough, the device can overrun and stop streaming.
In Python, the steps could look like this:
~~~{.py}
import uhd
import numpy as np
usrp = uhd.usrp.MultiUSRP("type=x300")
stream_args = uhd.usrp.StreamArgs("fc32", "sc16")
stream_args.args = "spp=200" # Note this setting is not valid for all USRPs
rx_streamer = usrp.get_rx_stream(stream_args)
rx_metadata = uhd.types.RXMetadata()
recv_buffer = np.zeros(rx_streamer.get_max_num_samps(), dtype=np.complex64)
stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.start_cont)
stream_cmd.stream_now = True
rx_streamer.issue_stream_cmd(stream_cmd)
while run_condition:
samps = rx_streamer.recv(recv_buffer, rx_metadata)
stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.stop_cont)
rx_streamer.issue_stream_cmd(stream_cmd)
~~~
For TX streaming, the following actions need to be taken:
- A streamer needs to be created (e.g., using multi_usrp::get_tx_stream() or
rfnoc_graph::get_tx_stream(), depending on what API is being used). Upon
creation of the streamer, typically all connections between the host and the
device are configured (e.g., how to send UDP data packets).
- When using the RFNoC API, it is necessary to manually connect the streamer
to the desired endpoint.
- When using the multi_usrp API, the TX streamer is automatically connected
to the radio/DSP chain.
- When creating an TX streamer, a uhd::stream_args_t object must be passed in
to configure the data types used on the link layer and in the host software,
as well as any other configurations that are required.
- To initiate streaming, use the uhd::tx_streamer::send() API call to pass data
to UHD for transmission to the device.
- It is up to the host application to call send() often enough to keep up with
the device. If the device runs out of data from the host, it will underrun.
In Python, the steps could look like this:
~~~{.py}
import uhd
import numpy as np
usrp = uhd.usrp.MultiUSRP("type=x300")
stream_args = uhd.usrp.StreamArgs("fc32", "sc16")
stream_args.args = "spp=200" # Note this setting is not valid for all USRPs
tx_streamer = usrp.get_tx_stream(stream_args)
tx_metadata = uhd.types.TXMetadata()
tx_buffer = np.zeros(1000000, dtype=np.complex64)
while True:
samps = tx_streamer.send(tx_buffer, tx_metadata)
~~~
For more details on configuring streamers, cf. \ref config_stream_args.
For more details on overruns/underruns, cf. \ref general_ounotes.
\section stream_lle Link Layer Encapsulation
Between the host and the device, data (such as I/Q samples) are packetized and
encapsulated. Refer to \ref page_rtp for more details on the protocols used.
The length of an IF data packet can be limited by several factors:
- **MTU of the link layer:** network card, network switch
- **Buffering on the host:** frame size in a ring buffer
- **Buffering on the device:** size of BRAM FIFOs
\section stream_datatypes Data Types
There are two important data types to consider when streaming. They are referred
to as arguments in the uhd::stream_args_t object:
- The data type of the samples used on the host for processing (`cpu` argument)
- The data type of the samples sent through the link-layer (`otw` argument)
\subsection stream_datatypes_cpu The host/CPU data type
The host data type refers to the format of samples used in the host for
baseband processing. Typically, the data type is complex baseband such
as normalized **complex-float32** or **complex-int16**.
\subsection stream_datatypes_otw The link-layer data type
The link-layer or "over-the-wire" data type refers to the format of the
samples sent through the link. Typically, this data type is **complex-int16**.
However, to increase throughput over the link-layer,
at the expense of precision, **complex-int8** may be used.
\subsection stream_datatypes_conv Conversion
The user may request arbitrary combinations of host and link data types;
however, not all combinations are supported. The user may register
custom data type formats and conversion routines. See
convert.hpp and \ref page_converters for further documentation.
\section stream_remote Remote streaming
Ethernet-based devices allow sending data to an alternative destination instead
of back to the controlling UHD session.
\subsection stream_remote_rfnoc RFNoC Devices (X440, X410, X3x0, N3xx Series, E320)
Starting with UHD 4.4, these devices allow streaming data to alternative
locations from their QSFP/SFP connectors (streaming data to alternative locations
from the RJ45 connector is not possible).
To enable remote streaming, create a regular RX streamer. This will work as a
proxy for UHD, and an object that will accept stream commands.
Consider the following example: A UHD host controller is running on a computer
with IP address 192.168.40.1. It is opening a session with a USRP with IP
address 192.168.40.2. It configures the USRP, sets the desired frequency and
gain, and any other settings that might be required. Then, it initiates a data
stream from the USRP to another computer with IP address 192.168.40.5.
```
┌─────────────┐ ┌──────────────┐
│ │ │ │
│ <───────────┤ UHD Host │
│ USRP │ │ 192.168.40.1 │
│ 192.168.40.2│ └──────────────┘
│ ├─────┐
└─────────────┘ │ ┌──────────────┐
│ │ │
│ │ Remote │
└─────> Streaming │
│ Destination │
│ 192.168.40.5 │
│ │
└──────────────┘
```
The sequence of events to enable this feature is illustrated with the following
Python snippet:
~~~{.py}
import uhd
import numpy as np
usrp = uhd.usrp.MultiUSRP("type=x4xx")
stream_args = uhd.usrp.StreamArgs("fc32", "sc16")
# Here, we program the remote computer's IP address and a destination UDP port:
stream_args.args = "dest_addr=192.168.40.5,dest_port=1234"
rx_streamer = usrp.get_rx_stream(stream_args)
rx_metadata = uhd.types.RXMetadata()
stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.start_cont)
stream_cmd.stream_now = True
rx_streamer.issue_stream_cmd(stream_cmd)
# Calling recv() now will do nothing and return a timeout, because samples have
# been diverted to the remote destination:
recv_buffer = np.zeros(rx_streamer.get_max_num_samps(), dtype=np.complex64)
samps = rx_streamer.recv(recv_buffer, rx_metadata)
assert samps == 0
# However, we can still use the streamer to stop the stream:
stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.stop_cont)
rx_streamer.issue_stream_cmd(stream_cmd)
~~~
The ability to call recv() without a fatal error condition allows using this API
with some preexisting applications.
The streamer objects accepts the following arguments:
- `dest_addr`, `dest_port`: The remote destination IP address and port. Both must be
provided.
- `dest_mac_addr`: If provided, this value is used as a MAC address. Must be in
AA:BB:CC:DD:EE:FF format. If not provided, the device uses ARP to identify the
MAC address based on the IP address. When given, there are no further checks
that the IP address matches the MAC address.
- `adapter`: The adapter that is used to stream data out of. The adapter names
match the interface names as listed on the command line (e.g., `sfp0`, `sfp1`).
This allows connecting to one interface and streaming out of another. It also
allows running UHD on the device itself (for MPM devices, i.e. X410, E320,
N3xx series) and streaming to a remote destination at a high rate.
Note that for X3x0, the available adapter names are hardcoded as `sfp0` and
`sfp1`, respectively.
- `stream_mode`: This key allows two options: `raw_payload` (the default) and
`full_packet`. When `full_packet` is selected, the full CHDR packet is streamed
and the remote destination needs to dissect or remove the header. With
`raw_payload`, only the data is sent as a UDP packet (e.g., only IQ samples).
See \ref page_rtp for more details on CHDR.
- `enable_fc`: Either "0" (default) or "1". Set to "1" to enable flow control.
In that case, `stream_mode` must also be set to `full_packet` in order to be
able to handle flow control responses. See the following section for more
information.
\subsubsection stream_remote_rfnoc_fc Flow Control
By default, the USRP will stream data to the remote streaming destination at
whatever data rate it is set to, and there are no checks to ensure the destination
can keep up. This is different from streaming to UHD, where flow control is used
to match rates between device and host computer.
If flow control is desired, then using `enable_fc=1` as a stream argument will
enable flow control. However, the remote destination must now unpack the data
packets and send flow control responses to the USRP in order for it to keep
streaming. Refer to the RFNoC specification for how to format flow control
response packets.
\subsubsection stream_remote_rfnoc_limits Limitations
The following limitations apply when using remote streaming:
- The streaming rates must not exceed the link speed (e.g., a 10 GbE link cannot
handle more than approx. 300 Msps when using 16-bit complex samples). If the
link speed is exceeded, the data will back up in the device and will cause an
overrun. The radios will then stop producing data. The overrun will be reported
to UHD, but the remote streaming destination will not automatically be
notified.
- When identifying the adapter to stream out of, USRPs will *not* verify that the
remote destination is actually available on this connector. For example, if a
USRP X310 were connected to a host with IP address 192.168.30.1 on sfp0, and
another host with IP 192.168.40.1 on sfp1, it is possible to request sending
packets to 192.168.40.1 on sfp0, even though there is no connection that way.
The USRP will simply "do as it is told" and stream packets with the destination
address 192.168.40.1 out of sfp0; those packets would then likely not be
processed by any network interface.
\subsection stream_remote_usrp2 USRP N200/N210/USRP2
The N200 Series of USRPs supports alternative stream destinations starting with
UHD 3.5.
The sequence to activate remote destination streaming is identical to that in Section
\ref stream_remote_rfnoc, with the following differences:
- Only the `addr` and `port` arguments are supported.
- Data is always sent in the device's VITA49 format (see \ref page_rtp).
- Flow control management is always required.
*/
// vim:ft=doxygen:
|