stream: Add get_stream_info() API calls

This is a new API call for streamers. It will allow peeking into the
internal settings of streamers.

The exact content of this call is device-specific. It varies a lot
between RFNoC- and non-RFNoC devices.

It can be used as follows:

```python
my_usrp = uhd.usrp.MultiUSRP(device_args)
rx_streamer = my_usrp.get_rx_stream(stream_args)
print(rx_streamer.get_stream_info(0)) # Shows info about the streamer
```

For most use cases, this API is not required. It can help for debugging
(e.g., compare flow control parameters with expectations, or identify
EPIDs for a better understanding of Wireshark output), or it can be used
for remote streaming, when a 3rd party application needs to know
internal settings of streamers.
This commit is contained in:
Martin Braun 2026-03-09 14:44:05 +01:00 committed by Martin Braun
parent 2af4ddb962
commit 2378080dbe
21 changed files with 477 additions and 12 deletions

View File

@ -350,6 +350,16 @@ public:
*/
virtual void post_input_action(
const std::shared_ptr<uhd::rfnoc::action_info>& action, const size_t port) = 0;
/*! Return information about the current streamer object
*
* The contents of the returned dictionary depend on the current hardware
* setup, the arguments with which this streamer was created, etc.
*
* \param chan The channel number for which to get stream info
* \returns dictionary of streamer information
*/
virtual device_addr_t get_stream_info(const size_t chan = 0) const = 0;
};
/*!
@ -422,6 +432,16 @@ public:
*/
virtual void post_output_action(
const std::shared_ptr<uhd::rfnoc::action_info>& action, const size_t port) = 0;
/*! Return information about the current streamer object
*
* The contents of the returned dictionary depend on the current hardware
* setup, the arguments with which this streamer was created, etc.
*
* \param chan The channel number for which to get stream info
* \returns dictionary of streamer information
*/
virtual device_addr_t get_stream_info(const size_t chan = 0) const = 0;
};
} // namespace uhd

View File

@ -166,6 +166,7 @@ public:
* \param epids Source and destination endpoint IDs
* \param num_recv_frames Num frames to reserve from the recv link
* \param fc_params Parameters for flow control
* \param xport_args Stream transport arguments
* \param disconnect Callback function to disconnect the links
*/
chdr_rx_data_xport(uhd::transport::io_service::sptr io_srv,
@ -175,6 +176,7 @@ public:
const uhd::rfnoc::sep_id_pair_t& epids,
const size_t num_recv_frames,
const fc_params_t& fc_params,
const uhd::device_addr_t& xport_args,
disconnect_callback_t disconnect);
/*! Destructor
@ -218,6 +220,37 @@ public:
return _mtu - _hdr_len;
}
/*!
* Get transport configuration information
*
* \return device_addr_t containing transport configuration parameters
*/
uhd::device_addr_t get_xport_info() const
{
uhd::device_addr_t info = _xport_args;
info["fc_freq_bytes"] = std::to_string(_fc_params.freq.bytes);
info["fc_freq_packets"] = std::to_string(_fc_params.freq.packets);
info["recv_capacity_bytes"] = std::to_string(_fc_params.buff_capacity.bytes);
info["recv_capacity_packets"] = std::to_string(_fc_params.buff_capacity.packets);
info["mtu"] = std::to_string(_mtu);
info["chdr_hdr_len"] = std::to_string(_hdr_len);
info["src_epid"] = std::to_string(_remote_epid);
info["dst_epid"] = std::to_string(_epid);
// Determine flow control mode
if (_fc_params.freq.bytes == 0 && _fc_params.freq.packets == 0) {
info["fc_mode"] = "off";
} else if (_fc_params.freq.packets == MAX_FC_FREQ_PKTS) {
info["fc_mode"] = "byte";
} else if (_fc_params.freq.bytes == MAX_FC_FREQ_BYTES) {
info["fc_mode"] = "packet";
} else {
info["fc_mode"] = "mixed";
}
return info;
}
/*!
* Gets an RX frame buffer containing a recv packet
*
@ -442,9 +475,18 @@ private:
// Local / Sink EPID
sep_id_t _epid;
// Remote / Source EPID
sep_id_t _remote_epid;
//! The CHDR width in bytes.
size_t _chdr_w_bytes;
// Flow control parameters
fc_params_t _fc_params;
// Transport arguments
uhd::device_addr_t _xport_args;
// Disconnect callback
disconnect_callback_t _disconnect;
};

View File

@ -298,6 +298,13 @@ public:
_send_io->wait_for_dest_ready(UINT_MAX, 0);
}
/*!
* Get transport configuration information
*
* \return device_addr_t containing transport configuration parameters
*/
uhd::device_addr_t get_xport_info() const;
/*!
* Writes header into frame buffer and returns payload pointer
*
@ -492,6 +499,9 @@ private:
//! The size of the send frame
size_t _frame_size;
// Flow control parameters
fc_params_t _fc_params;
// Disconnect callback
disconnect_callback_t _disconnect;
};

View File

@ -10,6 +10,7 @@
#include <uhd/convert.hpp>
#include <uhd/exception.hpp>
#include <uhd/stream.hpp>
#include <uhd/types/device_addr.hpp>
#include <uhd/types/endianness.hpp>
#include <uhd/utils/log.hpp>
#include <uhdlib/transport/rx_streamer_zero_copy.hpp>
@ -79,6 +80,7 @@ public:
: _zero_copy_streamer(num_ports)
, _in_buffs(num_ports)
, _chans_connected(num_ports, false)
, _stream_info(num_ports, stream_args.args)
{
if (stream_args.cpu_format.empty()) {
throw uhd::value_error("[rx_stream] Must provide a cpu_format!");
@ -127,6 +129,49 @@ public:
return _spp;
}
//! Get stream info
uhd::device_addr_t get_stream_info(const size_t chan) const override
{
if (chan >= _stream_info.size()) {
throw uhd::index_error("Invalid channel index for get_stream_info");
}
// Start with filtered base stream info (exclude underscore keys)
uhd::device_addr_t stream_info;
for (const auto& key : _stream_info[chan].keys()) {
if (!key.empty() && key[0] != '_') {
stream_info[key] = _stream_info[chan][key];
}
}
// Try to get transport info and merge it
try {
uhd::device_addr_t xport_info = _zero_copy_streamer.get_xport_info(chan);
// Merge transport info into stream info, filtering out keys starting with
// underscore
for (const auto& key : xport_info.keys()) {
if (!key.empty() && key[0] != '_') {
stream_info[key] = xport_info[key];
}
}
} catch (const std::exception&) {
// If getting transport info fails, just return the basic stream info
// This handles cases where transport doesn't support get_xport_info()
}
return stream_info;
}
//! Update stream info
void update_stream_info(
const size_t chan, const std::string& key, const std::string& value)
{
if (chan >= _stream_info.size()) {
throw uhd::index_error("Invalid channel index for update_stream_info");
}
_stream_info[chan][key] = value;
}
/*! Get width of each over-the-wire item component. For complex items,
* returns the width of one component only (real or imaginary).
*/
@ -208,6 +253,7 @@ protected:
void set_scale_factor(const size_t chan, const double scale_factor)
{
_converters[chan]->set_scalar(scale_factor);
_stream_info[chan]["scale_factor"] = std::to_string(scale_factor);
}
//! set maximum number of sample (per packet)
@ -404,6 +450,8 @@ private:
for (size_t i = 0; i < num_ports; i++) {
_converters.push_back(convert::get_converter(id)());
_converters.back()->set_scalar(1 / 32767.0);
_stream_info[i]["cpu_format"] = stream_args.cpu_format;
_stream_info[i]["otw_format"] = stream_args.otw_format;
}
}
@ -452,6 +500,9 @@ private:
// Flag to store if all channels are connected. This is to speed up the lookup
// of all channels' connected-status.
bool _all_chans_connected = false;
// Stream information storage
std::vector<uhd::device_addr_t> _stream_info;
};
}} // namespace uhd::transport

View File

@ -8,6 +8,7 @@
#include <uhd/config.hpp>
#include <uhd/exception.hpp>
#include <uhd/types/device_addr.hpp>
#include <uhd/types/metadata.hpp>
#include <uhd/utils/log.hpp>
#include <uhdlib/transport/get_aligned_buffs.hpp>
@ -127,6 +128,21 @@ public:
return _xports.size();
}
//! Get transport info for a specific channel
uhd::device_addr_t get_xport_info(const size_t chan) const
{
if (chan >= _xports.size()) {
throw uhd::index_error("Invalid channel index for get_xport_info");
}
if (!_xports[chan]) {
return uhd::device_addr_t(); // Return empty if no transport connected
}
// Call get_xport_info() on the underlying transport
return _xports[chan]->get_xport_info();
}
//! Configures tick rate for conversion of timestamp
void set_tick_rate(const double rate)
{

View File

@ -110,6 +110,7 @@ public:
, _zero_buffs(num_chans, &_zero)
, _out_buffs(num_chans)
, _chans_connected(num_chans, false)
, _stream_info(num_chans, stream_args.args)
{
if ((stream_args.args.cast<std::string>("transmit_policy", "default")
== "stop_on_seq_error")
@ -154,6 +155,49 @@ public:
return _spp;
}
//! Get stream info
uhd::device_addr_t get_stream_info(const size_t chan) const override
{
if (chan >= _stream_info.size()) {
throw uhd::index_error("Invalid channel index for get_stream_info");
}
// Start with filtered base stream info (exclude underscore keys)
uhd::device_addr_t stream_info;
for (const auto& key : _stream_info[chan].keys()) {
if (!key.empty() && key[0] != '_') {
stream_info[key] = _stream_info[chan][key];
}
}
// Try to get transport info and merge it
try {
uhd::device_addr_t xport_info = _zero_copy_streamer.get_xport_info(chan);
// Merge transport info into stream info, filtering out keys starting with
// underscore
for (const auto& key : xport_info.keys()) {
if (!key.empty() && key[0] != '_') {
stream_info[key] = xport_info[key];
}
}
} catch (const std::exception&) {
// If getting transport info fails, just return the basic stream info
// This handles cases where transport doesn't support get_xport_info()
}
return stream_info;
}
//! Update stream info
void update_stream_info(
const size_t chan, const std::string& key, const std::string& value)
{
if (chan >= _stream_info.size()) {
throw uhd::index_error("Invalid channel index for update_stream_info");
}
_stream_info[chan][key] = value;
}
/*! Get width of each over-the-wire item component. For complex items,
* returns the width of one component only (real or imaginary).
*/
@ -391,6 +435,7 @@ protected:
void set_scale_factor(const size_t chan, const double scale_factor)
{
_converters[chan]->set_scalar(scale_factor);
_stream_info[chan]["scale_factor"] = std::to_string(scale_factor);
}
//! Configures sample rate for conversion of timestamp
@ -483,6 +528,8 @@ private:
for (size_t i = 0; i < num_chans; i++) {
_converters.push_back(convert::get_converter(id)());
_converters.back()->set_scalar(32767.0);
_stream_info[i]["cpu_format"] = stream_args.cpu_format;
_stream_info[i]["otw_format"] = stream_args.otw_format;
}
}
@ -529,6 +576,9 @@ private:
// Flag to store if all channels are connected. This is to speed up the lookup
// of all channels' connected-status.
bool _all_chans_connected = false;
// Stream information storage
std::vector<uhd::device_addr_t> _stream_info;
};
}} // namespace uhd::transport

View File

@ -7,6 +7,7 @@
#pragma once
#include <uhd/config.hpp>
#include <uhd/exception.hpp>
#include <uhd/stream.hpp>
#include <uhd/types/metadata.hpp>
#include <vector>
@ -76,6 +77,18 @@ public:
_bytes_per_item = bpi;
}
//! Get transport configuration information for the specified channel
uhd::device_addr_t get_xport_info(const size_t channel) const
{
if (channel >= _xports.size()) {
throw uhd::index_error("Invalid channel index for get_xport_info");
}
if (!_xports[channel]) {
throw uhd::runtime_error("Channel not connected");
}
return _xports[channel]->get_xport_info();
}
/*!
* Gets a set of frame buffers, one per channel.
*

View File

@ -38,12 +38,16 @@ chdr_rx_data_xport::chdr_rx_data_xport(uhd::transport::io_service::sptr io_srv,
const uhd::rfnoc::sep_id_pair_t& epids,
const size_t num_recv_frames,
const fc_params_t& fc_params,
const uhd::device_addr_t& xport_args,
disconnect_callback_t disconnect)
: _fc_state(epids, fc_params.freq)
, _mtu(recv_link->get_recv_frame_size())
, _fc_sender(pkt_factory, epids)
, _epid(epids.second)
, _remote_epid(epids.first)
, _chdr_w_bytes(chdr_w_to_bits(pkt_factory.get_chdr_w()) / 8)
, _fc_params(fc_params)
, _xport_args(xport_args)
, _disconnect(disconnect)
{
UHD_LOG_TRACE("XPORT::RX_DATA_XPORT",

View File

@ -42,6 +42,7 @@ chdr_tx_data_xport::chdr_tx_data_xport(uhd::transport::io_service::sptr io_srv,
, _epid(epids.first)
, _chdr_w_bytes(chdr_w_to_bits(pkt_factory.get_chdr_w()) / 8)
, _frame_size(send_link->get_send_frame_size())
, _fc_params(fc_params)
, _disconnect(disconnect)
{
UHD_LOG_TRACE("XPORT::TX_DATA_XPORT",
@ -299,3 +300,33 @@ chdr_tx_data_xport::configure_sep(io_service::sptr io_srv,
fc_freq_ratio,
fc_headroom_ratio);
}
uhd::device_addr_t chdr_tx_data_xport::get_xport_info() const
{
uhd::device_addr_t info;
info["mtu"] = std::to_string(_mtu);
info["frame_size"] = std::to_string(_frame_size);
info["src_epid"] = std::to_string(_epid);
info["dst_epid"] = std::to_string(_send_header.get_dst_epid());
info["send_capacity_bytes"] = std::to_string(_fc_params.buff_capacity.bytes);
info["send_capacity_packets"] = std::to_string(_fc_params.buff_capacity.packets);
// For TX transport, flow control mode is determined by buffer capacity
// TX sends strc packets for resync, so if buff_capacity is non-zero, FC is enabled
if (_fc_params.buff_capacity.bytes == 0 && _fc_params.buff_capacity.packets == 0) {
info["fc_mode"] = "off";
} else {
// TX uses buffer-based flow control - mode depends on non-zero capacity field
if (_fc_params.buff_capacity.packets > 0
&& _fc_params.buff_capacity.bytes == MAX_FC_CAPACITY_BYTES) {
info["fc_mode"] = "packet";
} else if (_fc_params.buff_capacity.bytes > 0
&& _fc_params.buff_capacity.packets == MAX_FC_CAPACITY_PKTS) {
info["fc_mode"] = "byte";
} else {
info["fc_mode"] = "mixed";
}
}
return info;
}

View File

@ -134,6 +134,7 @@ void export_stream(py::module& m)
py::arg("timeout") = 0.1)
.def("get_num_channels", &uhd::rx_streamer::get_num_channels)
.def("get_max_num_samps", &uhd::rx_streamer::get_max_num_samps)
.def("get_stream_info", &uhd::rx_streamer::get_stream_info, py::arg("chan") = 0)
.def("issue_stream_cmd", &uhd::rx_streamer::issue_stream_cmd);
py::class_<tx_streamer, tx_streamer::sptr>(m, "tx_streamer", "See: uhd::tx_streamer")
@ -145,6 +146,7 @@ void export_stream(py::module& m)
py::arg("timeout") = 0.1)
.def("get_num_channels", &tx_streamer::get_num_channels)
.def("get_max_num_samps", &tx_streamer::get_max_num_samps)
.def("get_stream_info", &uhd::tx_streamer::get_stream_info, py::arg("chan") = 0)
.def("recv_async_msg",
&wrap_recv_async_msg,
py::arg("async_metadata"),

View File

@ -14,6 +14,7 @@
#include <uhd/stream.hpp>
#include <uhd/transport/vrt_if_packet.hpp>
#include <uhd/transport/zero_copy.hpp>
#include <uhd/types/device_addr.hpp>
#include <uhd/types/metadata.hpp>
#include <uhd/utils/byteswap.hpp>
#include <uhd/utils/log.hpp>
@ -802,9 +803,18 @@ private:
class recv_packet_streamer : public recv_packet_handler, public rx_streamer
{
public:
recv_packet_streamer(const size_t max_num_samps)
recv_packet_streamer(const size_t max_num_samps, uhd::device_addr_t stream_args)
: _max_num_samps(max_num_samps)
, _base_stream_args(stream_args)
, _stream_info(this->size(), stream_args)
{
_max_num_samps = max_num_samps;
}
//! Override resize to also resize stream info
void resize(const size_t size)
{
recv_packet_handler::resize(size);
_stream_info.resize(size, _base_stream_args);
}
size_t get_num_channels(void) const override
@ -839,8 +849,28 @@ public:
throw uhd::not_implemented_error("post_input_action is not implemented here!");
}
uhd::device_addr_t get_stream_info(const size_t chan) const override
{
if (chan >= _stream_info.size()) {
throw uhd::index_error("Invalid channel index for get_stream_info");
}
return _stream_info[chan];
}
void update_stream_info(
const size_t chan, const std::string& key, const std::string& value)
{
if (chan >= _stream_info.size()) {
throw uhd::index_error("Invalid channel index for update_stream_info");
}
_stream_info[chan][key] = value;
}
private:
size_t _max_num_samps;
uhd::device_addr_t _base_stream_args;
std::vector<uhd::device_addr_t> _stream_info;
};
}}} // namespace uhd::transport::sph

View File

@ -14,6 +14,7 @@
#include <uhd/stream.hpp>
#include <uhd/transport/vrt_if_packet.hpp>
#include <uhd/transport/zero_copy.hpp>
#include <uhd/types/device_addr.hpp>
#include <uhd/types/metadata.hpp>
#include <uhd/utils/byteswap.hpp>
#include <uhd/utils/tasks.hpp>
@ -389,12 +390,21 @@ private:
class send_packet_streamer : public send_packet_handler, public tx_streamer
{
public:
send_packet_streamer(const size_t max_num_samps)
send_packet_streamer(const size_t max_num_samps, uhd::device_addr_t stream_args)
: _max_num_samps(max_num_samps)
, _base_stream_args(stream_args)
, _stream_info(this->size(), stream_args)
{
_max_num_samps = max_num_samps;
this->set_max_samples_per_packet(_max_num_samps);
}
//! Override resize to also resize stream info
void resize(const size_t size)
{
send_packet_handler::resize(size);
_stream_info.resize(size, _base_stream_args);
}
size_t get_num_channels(void) const override
{
return this->size();
@ -425,8 +435,28 @@ public:
throw uhd::not_implemented_error("post_output_action is not implemented here!");
}
uhd::device_addr_t get_stream_info(const size_t chan) const override
{
if (chan >= _stream_info.size()) {
throw uhd::index_error("Invalid channel index for get_stream_info");
}
return _stream_info[chan];
}
void update_stream_info(
const size_t chan, const std::string& key, const std::string& value)
{
if (chan >= _stream_info.size()) {
throw uhd::index_error("Invalid channel index for update_stream_info");
}
_stream_info[chan][key] = value;
}
private:
size_t _max_num_samps;
uhd::device_addr_t _base_stream_args;
std::vector<uhd::device_addr_t> _stream_info;
};
}}} // namespace uhd::transport::sph

View File

@ -144,7 +144,7 @@ rx_streamer::sptr b100_impl::get_rx_stream(const uhd::stream_args_t& args_)
// make the new streamer given the samples per packet
std::shared_ptr<sph::recv_packet_streamer> my_streamer =
std::make_shared<sph::recv_packet_streamer>(spp);
std::make_shared<sph::recv_packet_streamer>(spp, args.args);
// init some streamer stuff
my_streamer->resize(args.channels.size());
@ -209,7 +209,7 @@ tx_streamer::sptr b100_impl::get_tx_stream(const uhd::stream_args_t& args_)
// make the new streamer given the samples per packet
std::shared_ptr<sph::send_packet_streamer> my_streamer =
std::make_shared<sph::send_packet_streamer>(spp);
std::make_shared<sph::send_packet_streamer>(spp, args.args);
// init some streamer stuff
my_streamer->resize(args.channels.size());

View File

@ -9,6 +9,7 @@
#include "../../transport/super_send_packet_handler.hpp"
#include "b200_impl.hpp"
#include "b200_regs.hpp"
#include <uhd/utils/cast.hpp>
#include <uhd/utils/math.hpp>
#include <uhdlib/usrp/common/async_packet_handler.hpp>
#include <uhdlib/usrp/common/validate_subdev_spec.hpp>
@ -213,6 +214,10 @@ void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate)
my_streamer->set_samp_rate(rate);
const double adj = _radio_perifs[dspno].ddc->get_scaling_adjustment();
my_streamer->set_scale_factor(adj);
// Update scale factor for all channels
for (size_t chan = 0; chan < my_streamer->get_num_channels(); chan++) {
my_streamer->update_stream_info(chan, "scale_factor", uhd::cast::to_str(adj));
}
_codec_mgr->check_bandwidth(rate, "Rx");
}
@ -240,6 +245,10 @@ void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate)
my_streamer->set_samp_rate(rate);
const double adj = _radio_perifs[dspno].duc->get_scaling_adjustment();
my_streamer->set_scale_factor(adj);
// Update scale factor for all channels
for (size_t chan = 0; chan < my_streamer->get_num_channels(); chan++) {
my_streamer->update_stream_info(chan, "scale_factor", uhd::cast::to_str(adj));
}
_codec_mgr->check_bandwidth(rate, "Tx");
}
@ -440,7 +449,7 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t& args_)
// make the new streamer given the samples per packet
if (not my_streamer)
my_streamer = std::make_shared<sph::recv_packet_streamer>(spp);
my_streamer = std::make_shared<sph::recv_packet_streamer>(spp, args.args);
my_streamer->resize(args.channels.size());
// init some streamer stuff
@ -453,6 +462,8 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t& args_)
id.output_format = args.cpu_format;
id.num_outputs = 1;
my_streamer->set_converter(id);
my_streamer->update_stream_info(stream_i, "cpu_format", args.cpu_format);
my_streamer->update_stream_info(stream_i, "otw_format", args.otw_format);
perif.framer->clear();
perif.framer->set_nsamps_per_packet(spp);
@ -472,6 +483,7 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t& args_)
std::bind(&rx_vita_core_3000::issue_stream_command,
perif.framer,
std::placeholders::_1));
my_streamer->update_stream_info(stream_i, "sid", uhd::cast::to_str(sid));
perif.rx_streamer = my_streamer; // store weak pointer
// sets all tick and samp rates on this streamer
@ -569,7 +581,7 @@ tx_streamer::sptr b200_impl::get_tx_stream(const uhd::stream_args_t& args_)
// make the new streamer given the samples per packet
if (not my_streamer)
my_streamer = std::make_shared<sph::send_packet_streamer>(spp);
my_streamer = std::make_shared<sph::send_packet_streamer>(spp, args.args);
my_streamer->resize(args.channels.size());
// init some streamer stuff
@ -582,6 +594,8 @@ tx_streamer::sptr b200_impl::get_tx_stream(const uhd::stream_args_t& args_)
id.output_format = args.otw_format + "_item32_le";
id.num_outputs = 1;
my_streamer->set_converter(id);
my_streamer->update_stream_info(stream_i, "cpu_format", args.cpu_format);
my_streamer->update_stream_info(stream_i, "otw_format", args.otw_format);
perif.deframer->clear();
perif.deframer->setup(args);
@ -594,8 +608,9 @@ tx_streamer::sptr b200_impl::get_tx_stream(const uhd::stream_args_t& args_)
_async_task_data->async_md,
std::placeholders::_1,
std::placeholders::_2));
my_streamer->set_xport_chan_sid(
stream_i, true, radio_index ? B200_TX_DATA1_SID : B200_TX_DATA0_SID);
const auto sid = radio_index ? B200_TX_DATA1_SID : B200_TX_DATA0_SID;
my_streamer->set_xport_chan_sid(stream_i, true, sid);
my_streamer->update_stream_info(stream_i, "sid", uhd::cast::to_str(sid));
my_streamer->set_enable_trailer(false); // TODO not implemented trailer support
// yet
perif.tx_streamer = my_streamer; // store weak pointer

View File

@ -332,6 +332,7 @@ mpmd_mboard_impl::mpmd_mb_iface::make_rx_data_transport(
epids,
recv_link->get_num_recv_frames(),
fc_params,
xport_args,
[io_srv_mgr, recv_link, send_link]() {
io_srv_mgr->disconnect_links(recv_link, send_link);
});

View File

@ -375,6 +375,13 @@ public:
"post_input_action is currently not implemented here!");
}
uhd::device_addr_t get_stream_info(const size_t /*chan*/) const override
{
uhd::device_addr_t info;
info["max_num_samps"] = std::to_string(_max_num_samps);
return info;
}
private:
size_t _max_num_samps;
soft_time_ctrl::sptr _stc;
@ -445,6 +452,13 @@ public:
"post_output_action is currently not implemented here!");
}
uhd::device_addr_t get_stream_info(const size_t /*chan*/) const override
{
uhd::device_addr_t info;
info["max_num_samps"] = std::to_string(_max_num_samps);
return info;
}
private:
size_t _max_num_samps;
soft_time_ctrl::sptr _stc;

View File

@ -462,7 +462,7 @@ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t& args_)
// make the new streamer given the samples per packet
std::shared_ptr<sph::recv_packet_streamer> my_streamer =
std::make_shared<sph::recv_packet_streamer>(spp);
std::make_shared<sph::recv_packet_streamer>(spp, args.args);
// init some streamer stuff
my_streamer->resize(args.channels.size());
@ -540,7 +540,7 @@ tx_streamer::sptr usrp2_impl::get_tx_stream(const uhd::stream_args_t& args_)
// make the new streamer given the samples per packet
std::shared_ptr<sph::send_packet_streamer> my_streamer =
std::make_shared<sph::send_packet_streamer>(spp);
std::make_shared<sph::send_packet_streamer>(spp, args.args);
// init some streamer stuff
my_streamer->resize(args.channels.size());

View File

@ -277,6 +277,7 @@ uhd::rfnoc::chdr_rx_data_xport::uptr x300_impl::x300_mb_iface::make_rx_data_tran
epids,
recv_link->get_num_recv_frames(),
fc_params,
xport_args,
[io_srv_mgr, recv_link, send_link]() {
io_srv_mgr->disconnect_links(recv_link, send_link);
});

View File

@ -109,6 +109,14 @@ public:
return get_mtu() - get_chdr_hdr_len();
}
uhd::device_addr_t get_xport_info() const
{
uhd::device_addr_t info;
info["type"] = "mock_rx_data_xport";
info["mtu"] = std::to_string(get_mtu());
return info;
}
private:
mock_recv_link::sptr _recv_link;
size_t _seq_num = 0;
@ -901,3 +909,53 @@ BOOST_AUTO_TEST_CASE(test_recv_zero_samples)
// 1 seconds
BOOST_CHECK_LE(elapsed_time.count(), 1.5);
}
BOOST_AUTO_TEST_CASE(test_get_stream_info)
{
const std::string cpu_format("fc32");
const std::string otw_format("sc16");
const size_t num_chans = 2;
auto recv_links = make_links(num_chans);
// Create stream args with both formats
uhd::stream_args_t stream_args(cpu_format, otw_format);
auto streamer = std::make_shared<mock_rx_streamer>(num_chans, stream_args);
streamer->set_tick_rate(TICK_RATE);
streamer->set_samp_rate(SAMP_RATE);
// Connect channels
for (size_t i = 0; i < num_chans; i++) {
mock_rx_data_xport::uptr xport(
std::make_unique<mock_rx_data_xport>(recv_links[i]));
streamer->set_scale_factor(i, SCALE_FACTOR);
streamer->connect_channel(i, std::move(xport));
}
// Test stream info for each channel
for (size_t chan = 0; chan < num_chans; chan++) {
uhd::device_addr_t stream_info = streamer->get_stream_info(chan);
// Check basic stream info
BOOST_CHECK_EQUAL(stream_info["cpu_format"], cpu_format);
BOOST_CHECK_EQUAL(stream_info["otw_format"], otw_format);
BOOST_CHECK_EQUAL(stream_info["scale_factor"], std::to_string(SCALE_FACTOR));
// Check transport info is merged
BOOST_CHECK_EQUAL(stream_info["type"], "mock_rx_data_xport");
BOOST_CHECK_EQUAL(stream_info["mtu"], std::to_string(FRAME_SIZE));
// Test that no underscore-prefixed keys are present
for (const auto& key : stream_info.keys()) {
BOOST_CHECK(key.empty() || key[0] != '_');
}
std::cout << "RX Channel " << chan << " stream info:" << std::endl;
for (const auto& key : stream_info.keys()) {
std::cout << " " << key << " = " << stream_info[key] << std::endl;
}
}
// Test invalid channel index
BOOST_CHECK_THROW(streamer->get_stream_info(num_chans), uhd::index_error);
}

View File

@ -91,6 +91,14 @@ public:
return get_mtu() - sizeof(packet_info_t);
}
uhd::device_addr_t get_xport_info() const
{
uhd::device_addr_t info;
info["type"] = "mock_rx_data_xport";
info["mtu"] = std::to_string(get_mtu());
return info;
}
private:
size_t _buff_size;
buff_t::uptr _buff;
@ -164,6 +172,14 @@ public:
return get_mtu() - sizeof(packet_info_t);
}
uhd::device_addr_t get_xport_info() const
{
uhd::device_addr_t info;
info["type"] = "mock_tx_data_xport";
info["mtu"] = std::to_string(get_mtu());
return info;
}
private:
size_t _buff_size;
@ -335,6 +351,7 @@ static std::shared_ptr<rx_streamer_mock_link> make_rx_streamer_mock_link(
epids,
send_link->get_num_send_frames(),
fc_params,
uhd::device_addr_t(), // Empty xport_args for test
[io_srv = io_srv, recv_link, send_link]() {
io_srv->detach_recv_link(recv_link);
io_srv->detach_send_link(send_link);

View File

@ -70,6 +70,14 @@ public:
return get_mtu() - get_chdr_hdr_len();
}
uhd::device_addr_t get_xport_info() const
{
uhd::device_addr_t info;
info["type"] = "mock_tx_data_xport";
info["mtu"] = std::to_string(get_mtu());
return info;
}
private:
mock_send_link::sptr _send_link;
@ -699,3 +707,55 @@ BOOST_AUTO_TEST_CASE(test_spp)
streamer->get_max_num_samps(), max_pyld / sizeof(std::complex<uint16_t>));
}
}
BOOST_AUTO_TEST_CASE(test_get_stream_info)
{
const std::string cpu_format("fc32");
const std::string otw_format("sc16");
const size_t num_chans = 3;
auto send_links = make_links(num_chans);
// Create stream args with both formats
uhd::stream_args_t stream_args(cpu_format, otw_format);
auto streamer = std::make_shared<mock_tx_streamer>(num_chans, stream_args);
streamer->set_tick_rate(TICK_RATE);
streamer->set_samp_rate(SAMP_RATE);
// Connect channels with different scale factors
for (size_t i = 0; i < num_chans; i++) {
mock_tx_data_xport::uptr xport(
std::make_unique<mock_tx_data_xport>(send_links[i]));
const double scale = SCALE_FACTOR * (i + 1); // Different scale per channel
streamer->set_scale_factor(i, scale);
streamer->connect_channel(i, std::move(xport));
}
// Test stream info for each channel
for (size_t chan = 0; chan < num_chans; chan++) {
uhd::device_addr_t stream_info = streamer->get_stream_info(chan);
// Check basic stream info
BOOST_CHECK_EQUAL(stream_info["cpu_format"], cpu_format);
BOOST_CHECK_EQUAL(stream_info["otw_format"], otw_format);
const double expected_scale = SCALE_FACTOR * (chan + 1);
BOOST_CHECK_EQUAL(stream_info["scale_factor"], std::to_string(expected_scale));
// Check transport info is merged
BOOST_CHECK_EQUAL(stream_info["type"], "mock_tx_data_xport");
BOOST_CHECK_EQUAL(stream_info["mtu"], std::to_string(FRAME_SIZE));
// Test that no underscore-prefixed keys are present
for (const auto& key : stream_info.keys()) {
BOOST_CHECK(key.empty() || key[0] != '_');
}
std::cout << "TX Channel " << chan << " stream info:" << std::endl;
for (const auto& key : stream_info.keys()) {
std::cout << " " << key << " = " << stream_info[key] << std::endl;
}
}
// Test invalid channel index
BOOST_CHECK_THROW(streamer->get_stream_info(num_chans), uhd::index_error);
}