Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement GPIO signalling for when transmitting/not. #983

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/examples/radio/radio_util_sample.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ int main(int argc, char** argv)
config.clock.clock = radio_configuration::clock_sources::source::DEFAULT;
config.sampling_rate_hz = sampling_rate_hz;
config.otw_format = otw_format;
config.tx_mode = enable_discontinuous_tx ? radio_configuration::transmission_mode::discontinuous
config.tx_mode = enable_discontinuous_tx ? radio_configuration::transmission_mode::discontinuous_idle
: radio_configuration::transmission_mode::continuous;
config.power_ramping_us = power_ramping_us;
config.args = device_arguments;
Expand Down
36 changes: 34 additions & 2 deletions apps/units/flexible_du/split_8/helpers/ru_sdr_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,27 @@

namespace srsran {

/// GPIO TX indication sector configuration.
struct ru_sdr_unit_expert_config_gpio_tx_sector {
/// \brief GPIO pin to indicate TX status on (optional)
std::optional<unsigned> gpio_index;
/// \brief Sense of the GPIO pin for indicating TX status
///
/// True outputs a high when transmitting, false outputs a low when
/// transmitting.
bool sense = false;
/// \brief Source of the GPIO signal, either "idle" or "config"
std::string source = "idle";
/// \brief Amount of time to put GPIO in TX mode early in microseconds.
float prelude = 0.0F;
};

/// GPIO TX indication cell configuration.
struct ru_sdr_unit_expert_config_gpio_tx_cell {
// Per sector config
std::vector<ru_sdr_unit_expert_config_gpio_tx_sector> sectors;
};

/// Expert SDR Radio Unit configuration.
struct ru_sdr_unit_expert_config {
/// System time-based throttling. See \ref lower_phy_configuration::system_time_throttling for more information.
Expand All @@ -36,8 +57,10 @@ struct ru_sdr_unit_expert_config {
///
/// Selects the radio transmission mode between the available options:
/// - continuous: The radio keeps the transmitter chain active, even when there are no transmission requests.
/// - discontinuous: The transmitter stops when there is no data to transmit.
/// - same-port: like discontinuous mode, but using the same port to transmit and receive.
/// - discontinuous-idle: The transmitter stops when there is no data to transmit.
/// - discontinuous-config: The transmitter stops when not in TX portion of TDD config.
/// - same-port-idle: like discontinuous-idle mode, but using the same port to transmit and receive.
/// - same-port-config: like discontinuous-config mode, but using the same port to transmit and receive.
///
/// \remark The discontinuous and same-port transmission modes may not be supported for some radio devices.
std::string transmission_mode = "continuous";
Expand All @@ -58,6 +81,15 @@ struct ru_sdr_unit_expert_config {
/// \note Powering up the transmitter ahead of time requires starting the transmission earlier, and reduces the time
/// window for the radio to transmit the provided samples.
float power_ramping_time_us = 0.0F;
/// \brief Time the PPS goes high. Usually 0.0, but on some radios later.
///
/// E.g. on an N310 this can be 101.3 us.
float pps_time_offset_us = 0.0f;
/// Number of samples to offset the tx/rx time by, used to compensate for
/// radio offsets. Positive means start tx later/report rx as being later.
int sample_offset = 0;
/// \brief The per sector per cell configuration of the TX GPIOs
std::vector<ru_sdr_unit_expert_config_gpio_tx_cell> gpio_tx_cells;
/// \brief Lower PHY downlink baseband buffer size policy.
///
/// Selects the size policy of the baseband buffers that pass DL samples from the lower PHY to the radio.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,55 @@ static void configure_cli11_amplitude_control_args(CLI::App& app, amplitude_cont
->capture_default_str();
}

static void configure_cli11_ru_sdr_expert_gpio_tx_sector_args(CLI::App& app, ru_sdr_unit_expert_config_gpio_tx_sector& config) {
add_option(app,
"--gpio_index",
config.gpio_index,
"GPIO pin to indicate TX status on (optional)")
->capture_default_str();

add_option(app,
"--sense",
config.sense,
"Sense of the GPIO pin for indicating TX status.\n"
"True outputs a high when transmitting, false outputs a low when "
"transmitting.")
->capture_default_str();

add_option(app,
"--source",
config.source,
"Source of the GPIO value, either 'idle' or 'config'.")
->capture_default_str();

add_option(app,
"--prelude",
config.prelude,
"Amount of time to put GPIO in TX mode early in microseconds.")
->capture_default_str();
}

static void configure_cli11_ru_sdr_expert_gpio_tx_cell_args(CLI::App& app, ru_sdr_unit_expert_config_gpio_tx_cell& config) {
add_option_cell(app,
"--sectors",
[&config](const std::vector<std::string>& values) {
config.sectors.resize(values.size());

for (unsigned i = 0, e = values.size(); i != e; ++i) {
CLI::App subapp("SDR Expert GPIO TX Cell Sector Config",
"SDR Expert GPIO TX Cell Sector Config, item #"
+ std::to_string(i));
subapp.config_formatter(create_yaml_config_parser());
subapp.allow_config_extras();
configure_cli11_ru_sdr_expert_gpio_tx_sector_args(subapp, config.sectors[i]);
std::istringstream ss(values[i]);
subapp.parse_from_stream(ss);
}
},
"GPIO TX sector description")
->capture_default_str();
}

static void configure_cli11_ru_sdr_expert_args(CLI::App& app, ru_sdr_unit_expert_config& config)
{
auto buffer_size_policy_check = [](const std::string& value) -> std::string {
Expand All @@ -52,10 +101,10 @@ static void configure_cli11_ru_sdr_expert_args(CLI::App& app, ru_sdr_unit_expert
};

auto tx_mode_check = [](const std::string& value) -> std::string {
if (value == "continuous" || value == "discontinuous" || value == "same-port") {
if (value == "continuous" || value == "discontinuous-idle" || value == "discontinuous-config" || value == "same-port") {
return {};
}
return "Invalid transmission mode. Accepted values [continuous,discontinuous,same-port]";
return "Invalid transmission mode. Accepted values [continuous,discontinuous-idle,discontinuous-config,same-port-idle,same-port-config]";
};

add_option(app,
Expand All @@ -67,9 +116,11 @@ static void configure_cli11_ru_sdr_expert_args(CLI::App& app, ru_sdr_unit_expert
"--tx_mode",
config.transmission_mode,
"Selects a radio transmission mode. Discontinuous modes are not supported by all radios.\n"
" continuous: the TX chain is always active.\n"
" discontinuous: the transmitter stops when there is no data to transmit.\n"
" same-port: the radio transmits and receives from the same antenna port.\n")
" continuous: the TX chain is always active.\n"
" discontinuous-idle: the transmitter stops when there is no data to transmit.\n"
" discontinuous-config: the transmitter stops when not in TX of TDD config.\n"
" same-port-idle: the radio transmits and receives from the same antenna port, switching based on transmit data.\n"
" same-port-config: the radio transmits and receives from the same antenna port, switching based on TDD config.\n")
->capture_default_str()
->check(tx_mode_check);

Expand All @@ -85,6 +136,36 @@ static void configure_cli11_ru_sdr_expert_args(CLI::App& app, ru_sdr_unit_expert
"Selects the size policy of the baseband buffers that pass DL samples from the lower PHY to the radio.")
->capture_default_str()
->check(buffer_size_policy_check);

add_option(app,
"--pps_time_offset_us",
config.pps_time_offset_us,
"Time at which the radio sense the PPS going high. For radios with alignment problems.")
->capture_default_str();

add_option(app,
"--sample_offset",
config.sample_offset,
"Number of samples to offset the tx/rx time by, used to compensate for radio offsets. Positive means start tx later/report rx as being later.")
->capture_default_str();

add_option_cell(
app,
"--gpio_tx_cells",
[&config](const std::vector<std::string>& values) {
config.gpio_tx_cells.resize(values.size());

for (unsigned i = 0, e = values.size(); i != e; ++i) {
CLI::App subapp("SDR Expert GPIO TX Cell Config",
"SDR Expert GPIO TX Cel Config, item #" + std::to_string(i));
subapp.config_formatter(create_yaml_config_parser());
subapp.allow_config_extras();
configure_cli11_ru_sdr_expert_gpio_tx_cell_args(subapp, config.gpio_tx_cells[i]);
std::istringstream ss(values[i]);
subapp.parse_from_stream(ss);
}
},
"Set the GPIO TX configuration on a per cell basis");
}

static void configure_cli11_ru_sdr_args(CLI::App& app, ru_sdr_unit_config& config)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "apps/units/flexible_du/du_low/du_low_config.h"
#include "ru_sdr_config.h"
#include "srsran/du/du_cell_config.h"
#include "srsran/ran/duplex_mode.h"

using namespace srsran;

Expand Down Expand Up @@ -112,6 +113,9 @@ static lower_phy_configuration generate_low_phy_config(const srs_du::du_cell_con
sector_config.ul_freq_hz = band_helper::nr_arfcn_to_freq(config.ul_carrier.arfcn_f_ref);
sector_config.nof_rx_ports = config.ul_carrier.nof_ant;
sector_config.nof_tx_ports = config.dl_carrier.nof_ant;
if (band_helper::get_duplex_mode(config.dl_carrier.band) == srsran::duplex_mode::TDD) {
sector_config.tdd_config.emplace(config.tdd_ul_dl_cfg_common.value());
}
out_cfg.sectors.push_back(sector_config);

if (!is_valid_lower_phy_config(out_cfg)) {
Expand Down Expand Up @@ -169,14 +173,16 @@ static void generate_radio_config(radio_configuration::radio& out_cfg,
const ru_sdr_unit_config& ru_cfg,
span<const srs_du::du_cell_config> du_cells)
{
out_cfg.args = ru_cfg.device_arguments;
out_cfg.log_level = ru_cfg.loggers.radio_level;
out_cfg.sampling_rate_hz = ru_cfg.srate_MHz * 1e6;
out_cfg.otw_format = radio_configuration::to_otw_format(ru_cfg.otw_format);
out_cfg.clock.clock = radio_configuration::to_clock_source(ru_cfg.clock_source);
out_cfg.clock.sync = radio_configuration::to_clock_source(ru_cfg.synch_source);
out_cfg.tx_mode = radio_configuration::to_transmission_mode(ru_cfg.expert_cfg.transmission_mode);
out_cfg.power_ramping_us = ru_cfg.expert_cfg.power_ramping_time_us;
out_cfg.args = ru_cfg.device_arguments;
out_cfg.log_level = ru_cfg.loggers.radio_level;
out_cfg.sampling_rate_hz = ru_cfg.srate_MHz * 1e6;
out_cfg.otw_format = radio_configuration::to_otw_format(ru_cfg.otw_format);
out_cfg.clock.clock = radio_configuration::to_clock_source(ru_cfg.clock_source);
out_cfg.clock.sync = radio_configuration::to_clock_source(ru_cfg.synch_source);
out_cfg.tx_mode = radio_configuration::to_transmission_mode(ru_cfg.expert_cfg.transmission_mode);
out_cfg.power_ramping_us = ru_cfg.expert_cfg.power_ramping_time_us;
out_cfg.pps_time_offset_us = ru_cfg.expert_cfg.pps_time_offset_us;
out_cfg.sample_offset = ru_cfg.expert_cfg.sample_offset;

const std::vector<std::string>& zmq_tx_addr = extract_zmq_ports(ru_cfg.device_arguments, "tx_port");
const std::vector<std::string>& zmq_rx_addr = extract_zmq_ports(ru_cfg.device_arguments, "rx_port");
Expand All @@ -190,6 +196,15 @@ static void generate_radio_config(radio_configuration::radio& out_cfg,
radio_configuration::stream tx_stream_config;
radio_configuration::stream rx_stream_config;

std::vector<ru_sdr_unit_expert_config_gpio_tx_sector> gpio_tx_sectors;
if (sector_id < ru_cfg.expert_cfg.gpio_tx_cells.size()) {
gpio_tx_sectors = ru_cfg.expert_cfg.gpio_tx_cells[sector_id].sectors;
}

// When multiple sectors are supported outside of lower phy (see
// generate_low_phy_config) change this to support that
gpio_tx_sectors.resize(1);

// Deduce center frequencies.
const double cell_tx_freq_Hz = band_helper::nr_arfcn_to_freq(cell.dl_carrier.arfcn_f_ref);
const double cell_rx_freq_Hz = band_helper::nr_arfcn_to_freq(cell.ul_carrier.arfcn_f_ref);
Expand Down Expand Up @@ -218,6 +233,15 @@ static void generate_radio_config(radio_configuration::radio& out_cfg,
}
tx_ch_config.gain_dB = ru_cfg.tx_gain_dB;

// For now there's only one sector, in future have to work out sector to
// stream map
tx_stream_config.gpio_tx_index = gpio_tx_sectors[0].gpio_index;
tx_stream_config.gpio_tx_sense = gpio_tx_sectors[0].sense;
tx_stream_config.gpio_tx_source =
srsran::radio_configuration::
to_gpio_source(gpio_tx_sectors[0].source);
tx_stream_config.gpio_tx_prelude = gpio_tx_sectors[0].prelude;

// Add the TX ports.
if (ru_cfg.device_driver == "zmq") {
if (sector_id * cell.dl_carrier.nof_ant + port_id >= zmq_tx_addr.size()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ static bool validate_ru_sdr_appconfig(const ru_sdr_unit_config&

const bool discontinuous_transmission = (config.expert_cfg.transmission_mode != "continuous");
for (const auto& cell : cell_config) {
if ((config.expert_cfg.transmission_mode == "same-port") && (cell.dplx_mode == duplex_mode::FDD)) {
if ((config.expert_cfg.transmission_mode == "same-port-idle"
|| config.expert_cfg.transmission_mode == "same-port-config")
&& (cell.dplx_mode == duplex_mode::FDD)) {
fmt::print("same-port transmission mode cannot be used with FDD cell configurations.\n");
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,31 @@ static void fill_ru_sdr_section(YAML::Node node, const ru_sdr_unit_config& confi
expert_node["low_phy_dl_throttling"] = config.expert_cfg.lphy_dl_throttling;
expert_node["tx_mode"] = config.expert_cfg.transmission_mode;
expert_node["power_ramping_time_us"] = config.expert_cfg.power_ramping_time_us;
expert_node["pps_time_offset_us"] = config.expert_cfg.pps_time_offset_us;
expert_node["sample_offset"] = config.expert_cfg.sample_offset;
auto gpio_tx_cells = expert_node["gpio_tx_cells"];
while (config.expert_cfg.gpio_tx_cells.size() > gpio_tx_cells.size()) {
gpio_tx_cells.push_back(YAML::Node());
}
for (unsigned i = 0; i != config.expert_cfg.gpio_tx_cells.size(); ++i) {
auto gpio_tx_sectors = gpio_tx_cells[i]["sectors"];
auto config_gpio_tx_sectors =
config.expert_cfg.gpio_tx_cells[i].sectors;

while (config_gpio_tx_sectors.size() > gpio_tx_sectors.size()) {
gpio_tx_cells.push_back(YAML::Node());
}

for (unsigned j = 0; j != config_gpio_tx_sectors.size(); ++j) {
if (config_gpio_tx_sectors[j].gpio_index.has_value()) {
gpio_tx_sectors[j]["gpio_index"] =
config_gpio_tx_sectors[j].gpio_index.value();
gpio_tx_sectors[j]["sense"] = config_gpio_tx_sectors[j].sense;
gpio_tx_sectors[j]["source"] = config_gpio_tx_sectors[j].source;
gpio_tx_sectors[j]["prelude"] = config_gpio_tx_sectors[j].prelude;
}
}
}
expert_node["dl_buffer_size_policy"] = config.expert_cfg.dl_buffer_size_policy;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ struct baseband_gateway_transmitter_metadata {
/// If present, it is the sample index in which there is no signal until the end of the buffer. Otherwise, the
/// baseband buffer contains transmit signal until the last sample.
std::optional<unsigned> tx_end;
/// \brief Downlink period start according to the TDD config in samples.
///
/// If present, sample previous it was not part of the downlink period and it
/// is part of the downlink period.
std::optional<unsigned> dl_config_start;
/// \brief Downlink period end according to the TDD config in samples.
///
/// If present, sample previous it was part of the downlink period and it is
/// not part of the downlink period.
std::optional<unsigned> dl_config_end;
};

} // namespace srsran
3 changes: 3 additions & 0 deletions include/srsran/phy/lower/lower_phy_configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "srsran/ran/cyclic_prefix.h"
#include "srsran/ran/n_ta_offset.h"
#include "srsran/ran/subcarrier_spacing.h"
#include "srsran/ran/tdd/tdd_ul_dl_config.h"
#include "srsran/srslog/srslog.h"
#include "srsran/support/executors/task_executor.h"

Expand All @@ -52,6 +53,8 @@ struct lower_phy_sector_description {
unsigned nof_tx_ports;
/// Number of receive ports.
unsigned nof_rx_ports;
/// TDD Configuration
std::optional<tdd_ul_dl_config_common> tdd_config;
Comment on lines +56 to +57
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current architecture (without TDD pattern knowledge) allows the lower PHY to support dynamic TDD patterns. I prefer keeping the lower PHY flexibility.

};

/// \brief Lower physical layer baseband gateway buffer size policy.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "srsran/phy/lower/processors/downlink/downlink_processor.h"
#include "srsran/phy/lower/processors/downlink/pdxch/pdxch_processor_factories.h"
#include "srsran/phy/lower/sampling_rate.h"
#include "srsran/ran/tdd/tdd_ul_dl_config.h"
#include <memory>

namespace srsran {
Expand All @@ -40,6 +41,8 @@ struct downlink_processor_configuration {
cyclic_prefix cp;
/// Baseband sampling rate.
sampling_rate rate;
/// TDD Config
std::optional<tdd_ul_dl_config_common> tdd_config;
/// Bandwidth in PRB.
unsigned bandwidth_prb;
/// Center frequency in Hz.
Expand All @@ -66,4 +69,4 @@ std::shared_ptr<lower_phy_downlink_processor_factory>
create_downlink_processor_factory_sw(std::shared_ptr<pdxch_processor_factory> pdxch_proc_factory,
std::shared_ptr<amplitude_controller_factory> amplitude_control_factory);

} // namespace srsran
} // namespace srsran
Loading