Merge branch 'next' into agpl_next

master
Codebot 3 years ago committed by SRS codebot
commit 123f766935

@ -30,7 +30,7 @@ endif(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
########################################################################
# Project setup
########################################################################
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.5)
project( SRSRAN )
message( STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM} )
message( STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR} )
@ -69,6 +69,7 @@ option(DISABLE_SIMD "Disable SIMD instructions" OFF)
option(AUTO_DETECT_ISA "Autodetect supported ISA extensions" ON)
option(ENABLE_GUI "Enable GUI (using srsGUI)" ON)
option(ENABLE_RF_PLUGINS "Enable RF plugins" ON)
option(ENABLE_UHD "Enable UHD" ON)
option(ENABLE_BLADERF "Enable BladeRF" ON)
option(ENABLE_SOAPYSDR "Enable SoapySDR" ON)
@ -509,8 +510,12 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
endif ()
if (ENABLE_ASAN)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
# Note: When using ASAN, we need to ensure the use of RPATH instead of RUNPATH via "-Wl,--disable-new-dtags"
# While RPATH is default, some systems (e.g. Ubuntu 18.04 and 20.04) use RUNPATH by default, which is non-transitive.
# Since ASAN intercepts dlopen(), by which it replaces the dynamic string token "$ORIGIN" to its own location,
# the RF plugins won't be found by "libsrsran_rf.so" when using ASAN + RUNPATH in the top-level executable.
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -Wl,--disable-new-dtags")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -Wl,--disable-new-dtags")
endif (ENABLE_ASAN)
if (ENABLE_TSAN)

@ -28,31 +28,45 @@
find_path(MKL_INCLUDE_DIR
NAMES mkl.h
HINTS $ENV{MKL_DIR}/include
/opt/intel/oneapi/mkl/latest/include
/opt/intel/mkl/include
/usr/include/mkl
PATHS)
find_path(MKL_FFTW_INCLUDE_DIR
NAMES fftw3.h
HINTS $ENV{MKL_DIR}/include/fftw
/opt/intel/oneapi/mkl/latest/include/fftw
/opt/intel/mkl/include/fftw
/usr/include/mkl/fftw
PATHS)
find_library(MKL_LIBRARIES
NAMES mkl_rt
HINTS $ENV{MKL_DIR}/lib/intel64
/opt/intel/oneapi/mkl/latest/lib/intel64
/opt/intel/mkl/lib/intel64
PATHS)
find_library(MKL_CORE
NAMES libmkl_core.a
HINTS $ENV{MKL_DIR}/lib/intel64
/opt/intel/oneapi/mkl/latest/lib/intel64/
/opt/intel/mkl/lib/intel64
PATHS)
find_library(MKL_ILP
NAMES libmkl_intel_ilp64.a
HINTS $ENV{MKL_DIR}/lib/intel64
/opt/intel/oneapi/mkl/latest/lib/intel64/
/opt/intel/mkl/lib/intel64
PATHS)
find_library(MKL_SEQ
NAMES libmkl_sequential.a
HINTS $ENV{MKL_DIR}/lib/intel64
/opt/intel/oneapi/mkl/latest/lib/intel64/
/opt/intel/mkl/lib/intel64
PATHS)
set(MKL_STATIC_LIBRARIES -Wl,--start-group ${MKL_CORE} ${MKL_ILP} ${MKL_SEQ} -Wl,--end-group -lpthread -lm -ldl)
@ -61,7 +75,7 @@ set(MKL_INCLUDE_DIRS ${MKL_INCLUDE_DIR} ${MKL_FFTW_INCLUDE_DIR})
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set MKL_FOUND to TRUE
# if all listed variables are TRUE
find_package_handle_standard_args(mkl DEFAULT_MSG
find_package_handle_standard_args(MKL DEFAULT_MSG
MKL_LIBRARIES MKL_CORE MKL_ILP MKL_SEQ MKL_INCLUDE_DIRS)
if(MKL_FOUND)

@ -18,7 +18,7 @@
# and at http://www.gnu.org/licenses/.
#
INCLUDE(FindPkgConfig)
FIND_PACKAGE(PkgConfig REQUIRED)
#PKG_CHECK_MODULES(SKIQ SKIQ)
IF(NOT SKIQ_FOUND)
@ -26,7 +26,7 @@ FIND_PATH(
SKIQ_INCLUDE_DIRS
NAMES sidekiq_api.h
HINTS $ENV{SKIQ_DIR}/inc
$ENV{SKIQ_DIR}/sidekiq_core/inc
$ENV{SKIQ_DIR}/sidekiq_core/inc
PATHS /usr/local/include
/usr/include
)

@ -23,11 +23,11 @@ if(NOT SOAPYSDR_FOUND)
pkg_check_modules (SOAPYSDR_PKG SoapySDR)
find_path(SOAPYSDR_INCLUDE_DIRS
NAMES Device.h
NAMES SoapySDR/Device.h
HINTS $ENV{SOAPY_DIR}/include
PATHS ${SOAPYSDR_PKG_INCLUDE_DIRS}
/usr/include/SoapySDR
/usr/local/include/SoapySDR
/usr/include
/usr/local/include
)
find_library(SOAPYSDR_LIBRARIES

@ -1,4 +1,3 @@
cmake_minimum_required(VERSION 2.6)
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD

@ -66,7 +66,7 @@ endif(SRSGUI_FOUND)
if (ZEROMQ_FOUND)
add_executable(zmq_remote_rx zmq_remote_rx.c)
target_link_libraries(zmq_remote_rx srsran_phy srsran_rf)
target_link_libraries(zmq_remote_rx srsran_phy srsran_rf ${ZEROMQ_LIBRARIES})
endif (ZEROMQ_FOUND)
#################################################################

@ -86,10 +86,6 @@ static bool enable_256qam = false;
static float output_file_snr = +INFINITY;
static bool use_standard_lte_rate = false;
// CFR type test args
static char cfr_manual_str[] = "manual";
static char cfr_auto_cma_str[] = "auto_cma";
static char cfr_auto_ema_str[] = "auto_ema";
// CFR runtime control flags
static bool cfr_thr_inc = false;
@ -105,7 +101,7 @@ typedef struct {
} cfr_args_t;
static cfr_args_t cfr_args = {.enable = 0,
.mode = cfr_manual_str,
.mode = "manual",
.manual_thres = 1.0f,
.strength = 1.0f,
.auto_target_papr = 8.0f,
@ -301,14 +297,9 @@ static int parse_cfr_args()
cfr_config.alpha = cfr_args.strength;
cfr_config.ema_alpha = cfr_args.ema_alpha;
if (!strcmp(cfr_args.mode, cfr_manual_str)) {
cfr_config.cfr_mode = SRSRAN_CFR_THR_MANUAL;
} else if (!strcmp(cfr_args.mode, cfr_auto_cma_str)) {
cfr_config.cfr_mode = SRSRAN_CFR_THR_AUTO_CMA;
} else if (!strcmp(cfr_args.mode, cfr_auto_ema_str)) {
cfr_config.cfr_mode = SRSRAN_CFR_THR_AUTO_EMA;
} else {
ERROR("CFR mode is not recognised");
cfr_config.cfr_mode = srsran_cfr_str2mode(cfr_args.mode);
if (cfr_config.cfr_mode == SRSRAN_CFR_THR_INVALID) {
ERROR("CFR mode not recognised");
return SRSRAN_ERROR;
}

@ -303,7 +303,7 @@ int main(int argc, char** argv)
cf_t* equalized_sf_buffer = srsran_vec_malloc(sizeof(cf_t) * sf_n_re);
// RX
srsran_ofdm_t fft[SRSRAN_MAX_PORTS];
srsran_ofdm_t fft[SRSRAN_MAX_PORTS] = {};
srsran_ofdm_cfg_t ofdm_cfg = {};
ofdm_cfg.nof_prb = cell_sl.nof_prb;
ofdm_cfg.cp = SRSRAN_CP_NORM;

@ -50,7 +50,7 @@ public:
reserved = 0b111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<registration_type_type_, 3> registration_type_type;
@ -60,7 +60,7 @@ public:
follow_on_request_pending = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<follow_on_request_bit_type_, 1> follow_on_request_bit_type;
@ -83,7 +83,7 @@ public:
mapped_security_context = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<security_context_flag_type_, 1> security_context_flag_type;
@ -92,7 +92,7 @@ public:
no_key_is_available_or_reserved = 0b111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<nas_key_set_identifier_type_, 3> nas_key_set_identifier_type;
@ -122,7 +122,7 @@ public:
eui_64 = 0b111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<identity_types_, 3> identity_types;
@ -140,7 +140,7 @@ public:
gli = 0b100,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<supi_format_type_, 3> supi_format_type;
@ -151,7 +151,7 @@ public:
ecies_scheme_profile_b = 0b0010,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<protection_scheme_id_type_, 4> protection_scheme_id_type;
@ -424,7 +424,7 @@ public:
sst_sd_mapped_hplmn_sst_and_mapped_hplmn_sd = 0b00001000,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<SST_type_, 8> SST_type;
@ -655,7 +655,7 @@ public:
data_centric = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<UE_usage_setting_type_, 1> UE_usage_setting_type;
@ -680,7 +680,7 @@ public:
drx_cycle_parameter_t_256 = 0b0100,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<drx_value_type_, 4> drx_value_type;
@ -746,7 +746,7 @@ public:
multiple_payloads = 0b1111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<Payload_container_type_type_, 4> Payload_container_type_type;
@ -793,7 +793,7 @@ public:
sms_over_nas_supported = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<SMS_requested_type_, 1> SMS_requested_type;
@ -803,7 +803,7 @@ public:
ue_radio_capability_update_needed = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<NG_RAN_RCU_type_, 1> NG_RAN_RCU_type;
@ -815,7 +815,7 @@ public:
reserved = 0b11,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<PNB_5GS_CIoT_type_, 2> PNB_5GS_CIoT_type;
@ -827,7 +827,7 @@ public:
reserved = 0b11,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<PNB_EPS_CIoT_type_, 2> PNB_EPS_CIoT_type;
@ -925,7 +925,7 @@ public:
seconds_20 = 0b1111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<Paging_Time_Window_type_, 4> Paging_Time_Window_type;
@ -949,7 +949,7 @@ public:
second_20_48 = 0b1111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<eDRX_value_type_, 4> eDRX_value_type;
@ -978,7 +978,7 @@ public:
value_indicates_that_the_timer_is_deactivated = 0b111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<Unit_type_, 3> Unit_type;
@ -1062,7 +1062,7 @@ public:
drx_cycle_parameter_t_1024 = 0b0111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<nb_n1_mode_drx_value_type_, 4> nb_n1_mode_drx_value_type;
@ -1084,7 +1084,7 @@ public:
registered_for_emergency_services = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<Emergency_registered_type_, 1> Emergency_registered_type;
@ -1094,7 +1094,7 @@ public:
nssaa_is_to_be_performed = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<NSSAA_to_be_performed_type_, 1> NSSAA_to_be_performed_type;
@ -1104,7 +1104,7 @@ public:
sms_over_nas_allowed = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<SMS_allowed_type_, 1> SMS_allowed_type;
@ -1116,7 +1116,7 @@ public:
reserved = 0b111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<registration_result_type_, 3> registration_result_type;
@ -1154,7 +1154,7 @@ public:
reserved = 0b11,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<type_of_list_type_, 2> type_of_list_type;
@ -1307,7 +1307,7 @@ public:
nssai_inclusion_mode_d = 0b11,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<NSSAI_inclusion_mode_type_, 2> NSSAI_inclusion_mode_type;
@ -1351,7 +1351,7 @@ public:
network_assigned_ue_radio_capability_i_ds_deletion_requested = 0b001,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<Deletion_request_type_, 3> Deletion_request_type;
@ -1447,7 +1447,7 @@ public:
protocol_error_unspecified = 0b01101111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<cause_5gmm_type_, 8> cause_5gmm_type;
@ -1469,7 +1469,7 @@ public:
switch_off = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<switch_off_type_, 1> switch_off_type;
@ -1479,7 +1479,7 @@ public:
re_registration_required = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<re_registration_required_type_, 1> re_registration_required_type;
@ -1490,7 +1490,7 @@ public:
access_3_gpp_and_non_3_gpp_access = 0b11,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<access_type_type_, 2> access_type_type;
@ -1535,7 +1535,7 @@ public:
unused_shall_be_interpreted_as_data_2 = 0b1011,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<Service_type_value_type_, 4> Service_type_value_type;
@ -1559,7 +1559,7 @@ public:
emergency_services_fallback = 0b100,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<control_plane_service_type_value_type_, 3> control_plane_service_type_value_type;
@ -1624,7 +1624,7 @@ public:
reserved = 0b11,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<value_type_, 3> value_type;
@ -1658,7 +1658,7 @@ public:
release_of_n1_nas_signalling_connection_not_required = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<SCMR_type_, 1> SCMR_type;
@ -1745,7 +1745,7 @@ public:
eui_64 = 0b111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<identity_types_, 3> identity_types;
@ -1773,7 +1773,7 @@ public:
ia7_5g = 0b0111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<integrity_protection_algorithm_type_, 4> integrity_protection_algorithm_type;
@ -1789,7 +1789,7 @@ public:
ea7_5g = 0b0111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<ciphering_algorithm_type_, 4> ciphering_algorithm_type;
@ -1813,7 +1813,7 @@ public:
imeisv_requested = 0b001,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<imeisv_request_type_, 3> imeisv_request_type;
@ -1841,7 +1841,7 @@ public:
eia7 = 0b111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<integrity_protection_algorithm_type_, 3> integrity_protection_algorithm_type;
@ -1857,7 +1857,7 @@ public:
eea7 = 0b111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<ciphering_algorithm_type_, 3> ciphering_algorithm_type;
@ -1943,7 +1943,7 @@ public:
non_3_gpp_access = 0b10,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<Access_type_value_type_, 2> Access_type_value_type;
@ -1982,7 +1982,7 @@ public:
reserved = 0b111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<Request_type_value_type_, 3> Request_type_value_type;
@ -2015,7 +2015,7 @@ public:
ma_pdu_session_network_upgrade_is_allowed = 0b0001,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<MA_PDU_session_information_value_type_, 4> MA_PDU_session_information_value_type;
@ -2040,7 +2040,7 @@ public:
reserved = 0b11,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<Downlink_data_expected_type_, 2> Downlink_data_expected_type;
@ -2064,7 +2064,7 @@ public:
full_data_rate = 0b11111111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<max_data_rate_UPIP_uplink_type_, 8> max_data_rate_UPIP_uplink_type;
@ -2075,7 +2075,7 @@ public:
full_data_rate = 0b11111111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<max_data_rate_UPIP_downlink_type_, 8> max_data_rate_UPIP_downlink_type;
@ -2102,7 +2102,7 @@ public:
reserved = 0b111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<PDU_session_type_value_type_, 3> PDU_session_type_value_type;
@ -2129,7 +2129,7 @@ public:
reserved = 0b111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<SSC_mode_value_type_, 3> SSC_mode_value_type;
@ -2254,7 +2254,7 @@ public:
bits_15 = 0b10,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<CID_Length_type_, 2> CID_Length_type;
@ -2277,7 +2277,7 @@ public:
ipv4v6 = 0b011,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<PDU_session_type_value_type_, 3> PDU_session_type_value_type;
@ -2340,7 +2340,7 @@ public:
inc_by_256_pbps = 0b00011001,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<unit_session_AMBR_type_, 8> unit_session_AMBR_type;
@ -2430,7 +2430,7 @@ public:
value_indicates_that_the_timer_is_deactivated = 0b111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<Unit_type_, 3> Unit_type;
@ -2485,7 +2485,7 @@ public:
ethernet_pdn_type_in_s1_mode_supported = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<EPT_S1_type_, 1> EPT_S1_type;
@ -2557,7 +2557,7 @@ public:
the_back_off_timer_is_applied_in_all_plm_ns = 0b1,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<abo_type_, 1> abo_type;

@ -103,6 +103,7 @@ bool make_phy_tdd_cfg(const srsran_duplex_config_nr_t& srsran_duplex_config
bool make_phy_harq_ack_cfg(const asn1::rrc_nr::phys_cell_group_cfg_s& phys_cell_group_cfg,
srsran_harq_ack_cfg_hl_t* srsran_ue_dl_nr_harq_ack_cfg);
bool make_phy_coreset_cfg(const asn1::rrc_nr::ctrl_res_set_s& ctrl_res_set, srsran_coreset_t* srsran_coreset);
void make_phy_search_space0_cfg(srsran_search_space_t* in_srsran_search_space);
bool make_phy_search_space_cfg(const asn1::rrc_nr::search_space_s& search_space,
srsran_search_space_t* srsran_search_space);
bool make_phy_csi_report(const asn1::rrc_nr::csi_report_cfg_s& csi_report_cfg,

@ -45,6 +45,7 @@ public:
srsran::srsran_rat_t rat;
uint32_t lcid;
uint32_t eps_bearer_id;
uint32_t five_qi = 0;
bool is_valid() const { return rat != srsran_rat_t::nulltype; }
};
static const radio_bearer_t invalid_rb;
@ -59,9 +60,11 @@ public:
bool has_active_radio_bearer(uint32_t eps_bearer_id);
radio_bearer_t get_radio_bearer(uint32_t eps_bearer_id);
radio_bearer_t get_radio_bearer(uint32_t eps_bearer_id) const;
radio_bearer_t get_eps_bearer_id_for_lcid(uint32_t lcid);
radio_bearer_t get_eps_bearer_id_for_lcid(uint32_t lcid) const;
bool set_five_qi(uint32_t eps_bearer_id, uint16_t five_qi);
private:
using eps_rb_map_t = std::map<uint32_t, radio_bearer_t>;
@ -156,7 +159,8 @@ public:
void rem_user(uint16_t rnti);
bool has_active_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id);
radio_bearer_t get_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id);
radio_bearer_t get_lcid_bearer(uint16_t rnti, uint32_t lcid);
radio_bearer_t get_lcid_bearer(uint16_t rnti, uint32_t lcid) const;
bool set_five_qi(uint16_t rnti, uint32_t eps_bearer_id, uint16_t five_qi);
private:
srslog::basic_logger& logger;

@ -89,7 +89,7 @@ inline const char* enum_to_text(const char* const array[], uint32_t nof_types, u
}
template <class ItemType>
ItemType enum_to_number(ItemType* array, uint32_t nof_types, uint32_t enum_val)
constexpr ItemType enum_to_number(ItemType* array, uint32_t nof_types, uint32_t enum_val)
{
return enum_val >= nof_types ? -1 : array[enum_val];
}

@ -54,7 +54,8 @@ struct phy_cfg_nr_t {
srsran_harq_ack_cfg_hl_t harq_ack = {};
srsran_csi_hl_cfg_t csi = {};
srsran_carrier_nr_t carrier = {};
ssb_cfg_t ssb;
ssb_cfg_t ssb = {};
uint32_t t_offset = 0; ///< n-TimingAdvanceOffset
phy_cfg_nr_t() {}

@ -43,6 +43,7 @@ struct meas_cell_cfg_t {
asn1::rrc::q_offset_range_e cell_individual_offset;
uint32_t allowed_meas_bw;
bool direct_forward_path_available;
int tac;
};
// neigh measurement Cell info

@ -89,6 +89,7 @@ public:
*/
virtual bool send_ho_required(uint16_t rnti,
uint32_t target_eci,
uint16_t target_tac,
srsran::plmn_id_t target_plmn,
srsran::span<uint32_t> fwd_erabs,
srsran::unique_byte_buffer_t rrc_container,

@ -39,6 +39,7 @@ class rrc_nr_interface_rrc
public:
struct sgnb_addition_req_params_t {
uint32_t eps_bearer_id;
uint32_t five_qi;
// add configuration check
// E-RAB Parameters, Tunnel address (IP address, TEID)
// QCI, security, etc

@ -29,17 +29,20 @@ namespace srsenb {
class rrc_interface_ngap_nr
{
public:
virtual int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) = 0;
virtual int ue_set_bitrates(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) = 0;
virtual int set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) = 0;
virtual int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap::ue_security_cap_s& caps) = 0;
virtual int start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buffer_t nas_pdu) = 0;
virtual int
establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) = 0;
virtual int allocate_lcid(uint16_t rnti) = 0;
virtual int release_bearers(uint16_t rnti) = 0;
virtual void release_user(uint16_t rnti) = 0;
virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0;
virtual int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) = 0;
virtual int ue_set_bitrates(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) = 0;
virtual int set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) = 0;
virtual int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap::ue_security_cap_s& caps) = 0;
virtual int start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buffer_t nas_pdu) = 0;
virtual int establish_rrc_bearer(uint16_t rnti,
uint16_t pdu_session_id,
srsran::const_byte_span nas_pdu,
uint32_t lcid,
uint32_t five_qi) = 0;
virtual int allocate_lcid(uint16_t rnti) = 0;
virtual int release_bearers(uint16_t rnti) = 0;
virtual void release_user(uint16_t rnti) = 0;
virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0;
};
} // namespace srsenb

@ -73,11 +73,28 @@ inline std::string to_string(const rlc_am_nr_sn_size_t& sn_size)
constexpr static const char* options[] = {"12 bits", "18 bits"};
return enum_to_text(options, (uint32_t)rlc_mode_t::nulltype, (uint32_t)sn_size);
}
inline uint16_t to_number(const rlc_am_nr_sn_size_t& sn_size)
constexpr uint16_t to_number(const rlc_am_nr_sn_size_t& sn_size)
{
constexpr static uint16_t options[] = {12, 18};
constexpr uint16_t options[] = {12, 18};
return enum_to_number(options, (uint32_t)rlc_mode_t::nulltype, (uint32_t)sn_size);
}
/**
* @brief Value range of the serial numbers
* @param sn_size Length of the serial number field in bits
* @return cardianlity
*/
constexpr uint32_t cardinality(const rlc_am_nr_sn_size_t& sn_size)
{
return (1 << to_number(sn_size));
}
/****************************************************************************
* Tx constants
* Ref: 3GPP TS 38.322 version 16.2.0 Section 7.2
***************************************************************************/
constexpr uint32_t am_window_size(const rlc_am_nr_sn_size_t& sn_size)
{
return cardinality(sn_size) / 2;
}
struct rlc_am_config_t {
/****************************************************************************
@ -228,14 +245,25 @@ public:
rlc_cnfg.am.t_poll_retx = 5;
return rlc_cnfg;
}
static rlc_config_t default_rlc_am_nr_config()
static rlc_config_t default_rlc_am_nr_config(uint32_t sn_size = 12)
{
rlc_config_t rlc_cnfg = {};
rlc_cnfg.rat = srsran_rat_t::nr;
rlc_cnfg.rlc_mode = rlc_mode_t::am;
rlc_config_t rlc_cnfg = {};
rlc_cnfg.rat = srsran_rat_t::nr;
rlc_cnfg.rlc_mode = rlc_mode_t::am;
if (sn_size == 12) {
rlc_cnfg.am_nr.tx_sn_field_length = srsran::rlc_am_nr_sn_size_t::size12bits;
rlc_cnfg.am_nr.rx_sn_field_length = srsran::rlc_am_nr_sn_size_t::size12bits;
} else if (sn_size == 18) {
rlc_cnfg.am_nr.tx_sn_field_length = srsran::rlc_am_nr_sn_size_t::size18bits;
rlc_cnfg.am_nr.rx_sn_field_length = srsran::rlc_am_nr_sn_size_t::size18bits;
} else {
return {};
}
rlc_cnfg.am_nr.t_status_prohibit = 8;
rlc_cnfg.am_nr.max_retx_thresh = 4;
rlc_cnfg.am_nr.t_reassembly = 35;
rlc_cnfg.am_nr.poll_pdu = 4;
rlc_cnfg.am_nr.t_poll_retx = 45;
return rlc_cnfg;
}
static rlc_config_t default_rlc_um_nr_config(uint32_t sn_size = 6)

@ -206,6 +206,9 @@ public:
// RRC informs MAC about new UE identity for contention-free RA
virtual bool set_crnti(const uint16_t crnti) = 0;
// RRC informs MAC to start/stop search for BCCH messages
virtual void bcch_search(bool enabled) = 0;
};
struct phy_args_nr_t {
@ -264,6 +267,12 @@ public:
const float preamble_received_target_power,
const float ta_base_sec = 0.0f) = 0;
/// Apply TA command after RAR
virtual void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) = 0;
/// Apply TA command after MAC CE
virtual void set_timeadv(uint32_t tti, uint32_t ta_cmd) = 0;
/**
* @brief Query PHY if there is a valid PUCCH SR resource configured for a given SR identifier
* @param sr_id SR identifier

@ -68,6 +68,13 @@ public:
virtual bool is_lcid_enabled(uint32_t lcid) = 0;
};
// SDAP interface
class pdcp_interface_sdap_nr
{
public:
virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
};
// STACK interface for GW (based on EPS-bearer IDs)
class stack_interface_gw
{

@ -35,6 +35,15 @@
namespace srsue {
struct cfr_args_t {
bool enable = false;
srsran_cfr_mode_t mode = SRSRAN_CFR_THR_MANUAL;
float manual_thres = 2.0f;
float strength = 1.0f;
float auto_target_papr = 7.0f;
float ema_alpha = 1.0f / (float)SRSRAN_CP_NORM_NSYMB;
};
struct phy_args_t {
std::string type = "lte";
srsran::phy_log_args_t log;
@ -105,6 +114,8 @@ struct phy_args_t {
srsran::channel::args_t dl_channel_args;
srsran::channel::args_t ul_channel_args;
cfr_args_t cfr_args; ///< Stores user-defined CFR configuration
};
/* RAT agnostic Interface MAC -> PHY */

@ -0,0 +1,47 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
/******************************************************************************
* File: ue_sdap_interfaces.h
* Description: Abstract base class interfaces for SDAP layer
*****************************************************************************/
#ifndef SRSRAN_UE_SDAP_INTERFACES_H
#define SRSRAN_UE_SDAP_INTERFACES_H
/*****************************
* SDAP INTERFACES
****************************/
class sdap_interface_pdcp_nr
{
public:
virtual void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
};
class sdap_interface_gw_nr
{
public:
virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
};
class sdap_interface_rrc
{
public:
struct bearer_cfg_t {
bool is_data;
bool add_downlink_header;
bool add_uplink_header;
uint32_t qfi;
};
virtual bool set_bearer_cfg(uint32_t lcid, const bearer_cfg_t& cfg) = 0;
};
#endif // SRSRAN_UE_SDAP_INTERFACES_H

@ -29,12 +29,14 @@
#define CFR_EMA_INIT_AVG_PWR 0.1
/**
* @brief CFR manual threshold or PAPR limiting with Moving Average or EMA power averaging
* @brief CFR manual threshold or PAPR limiting with CMA or EMA power averaging
*/
typedef enum SRSRAN_API {
SRSRAN_CFR_THR_MANUAL = 1,
SRSRAN_CFR_THR_AUTO_CMA = 2,
SRSRAN_CFR_THR_AUTO_EMA = 3
SRSRAN_CFR_THR_INVALID,
SRSRAN_CFR_THR_MANUAL,
SRSRAN_CFR_THR_AUTO_CMA,
SRSRAN_CFR_THR_AUTO_EMA,
SRSRAN_CFR_NOF_MODES
} srsran_cfr_mode_t;
/**
@ -124,4 +126,14 @@ SRSRAN_API int srsran_cfr_set_threshold(srsran_cfr_t* q, float thres);
*/
SRSRAN_API int srsran_cfr_set_papr(srsran_cfr_t* q, float papr);
/**
* @brief Converts a string representing a CFR mode from the config files into srsran_cfr_mode_t type
*
* @param[in] mode_str the cfr.mode string coming from the config file
* @return SRSRAN_CFR_THR_INVALID if mode_str is empty,
* SRSRAN_CFR_THR_INVALID if mode_str is not recognised,
* otherwise it returns the corresponding srsran_cfr_mode_t value.
*/
SRSRAN_API srsran_cfr_mode_t srsran_cfr_str2mode(const char* mode_str);
#endif // SRSRAN_CFR_H

@ -49,7 +49,7 @@ typedef struct SRSRAN_API {
cf_t* ce;
uint32_t nof_re;
float noise_estimate;
float noise_estimate_dbm;
float noise_estimate_dbFs;
float rsrp;
float rsrp_dBfs;
float epre;

@ -42,6 +42,7 @@ typedef struct {
uint32_t pid; ///< HARQ process identifier
uint16_t rnti;
uint32_t pucch_resource_id;
uint32_t n_cce;
} srsran_harq_ack_resource_t;
/**

@ -84,6 +84,8 @@ typedef struct SRSRAN_API {
srsran_uci_value_t uci_data;
float dmrs_correlation;
float snr_db;
float rssi_dbFs;
float ni_dbFs;
float correlation;
bool detected;

@ -128,11 +128,13 @@ SRSRAN_API int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier,
* @brief Selects a valid PUCCH resource for transmission
* @param pucch_cfg PUCCH configuration from upper layers
* @param uci_cfg Uplink Control information configuration (and PDCCH context)
* @param N_bwp_sz Uplink BWP size in nof_prb
* @param[out] resource Selected resource for transmitting PUCCH
* @return SRSRAN_SUCCESS if provided configuration is valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg,
const srsran_uci_cfg_nr_t* uci_cfg,
uint32_t N_bwp_sz,
srsran_pucch_nr_resource_t* resource);
/**

@ -72,6 +72,73 @@ typedef struct {
typedef void (*srsran_rf_error_handler_t)(void* arg, srsran_rf_error_t error);
/* RF frontend API */
typedef struct {
const char* name;
const char* (*srsran_rf_devname)(void* h);
int (*srsran_rf_start_rx_stream)(void* h, bool now);
int (*srsran_rf_stop_rx_stream)(void* h);
void (*srsran_rf_flush_buffer)(void* h);
bool (*srsran_rf_has_rssi)(void* h);
float (*srsran_rf_get_rssi)(void* h);
void (*srsran_rf_suppress_stdout)(void* h);
void (*srsran_rf_register_error_handler)(void* h, srsran_rf_error_handler_t error_handler, void* arg);
int (*srsran_rf_open)(char* args, void** h);
int (*srsran_rf_open_multi)(char* args, void** h, uint32_t nof_channels);
int (*srsran_rf_close)(void* h);
double (*srsran_rf_set_rx_srate)(void* h, double freq);
int (*srsran_rf_set_rx_gain)(void* h, double gain);
int (*srsran_rf_set_rx_gain_ch)(void* h, uint32_t ch, double gain);
int (*srsran_rf_set_tx_gain)(void* h, double gain);
int (*srsran_rf_set_tx_gain_ch)(void* h, uint32_t ch, double gain);
double (*srsran_rf_get_rx_gain)(void* h);
double (*srsran_rf_get_tx_gain)(void* h);
srsran_rf_info_t* (*srsran_rf_get_info)(void* h);
double (*srsran_rf_set_rx_freq)(void* h, uint32_t ch, double freq);
double (*srsran_rf_set_tx_srate)(void* h, double freq);
double (*srsran_rf_set_tx_freq)(void* h, uint32_t ch, double freq);
void (*srsran_rf_get_time)(void* h, time_t* secs, double* frac_secs);
void (*srsran_rf_sync_pps)(void* h);
int (*srsran_rf_recv_with_time)(void* h,
void* data,
uint32_t nsamples,
bool blocking,
time_t* secs,
double* frac_secs);
int (*srsran_rf_recv_with_time_multi)(void* h,
void** data,
uint32_t nsamples,
bool blocking,
time_t* secs,
double* frac_secs);
int (*srsran_rf_send_timed)(void* h,
void* data,
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst);
int (*srsran_rf_send_timed_multi)(void* h,
void** data,
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst);
} rf_dev_t;
typedef struct {
const char* plugin_name;
void* dl_handle;
rf_dev_t* rf_api;
} srsran_rf_plugin_t;
SRSRAN_API int srsran_rf_load_plugins();
SRSRAN_API int srsran_rf_open(srsran_rf_t* h, char* args);
SRSRAN_API int srsran_rf_open_multi(srsran_rf_t* h, char* args, uint32_t nof_channels);

@ -108,6 +108,8 @@ typedef struct SRSRAN_API {
srsran_ra_ul_pusch_hopping_t hopping;
srsran_cfr_cfg_t cfr_config;
cf_t* out_buffer;
cf_t* refsignal;
cf_t* srs_signal;
@ -121,6 +123,8 @@ SRSRAN_API void srsran_ue_ul_free(srsran_ue_ul_t* q);
SRSRAN_API int srsran_ue_ul_set_cell(srsran_ue_ul_t* q, srsran_cell_t cell);
SRSRAN_API int srsran_ue_ul_set_cfr(srsran_ue_ul_t* q, const srsran_cfr_cfg_t* cfr);
SRSRAN_API int srsran_ue_ul_pregen_signals(srsran_ue_ul_t* q, srsran_ue_ul_cfg_t* cfg);
SRSRAN_API int srsran_ue_ul_dci_to_pusch_grant(srsran_ue_ul_t* q,

@ -70,6 +70,15 @@ extern "C" {
// Proportional moving average
#define SRSRAN_VEC_PMA(average1, n1, average2, n2) (((average1) * (n1) + (average2) * (n2)) / ((n1) + (n2)))
// Safe Proportional moving average
#ifdef __cplusplus
#define SRSRAN_VEC_SAFE_PMA(average1, n1, average2, n2) \
(std::isnormal((n1) + (n2)) ? SRSRAN_VEC_PMA(average1, n1, average2, n2) : (0))
#else
#define SRSRAN_VEC_SAFE_PMA(average1, n1, average2, n2) \
(isnormal((n1) + (n2)) ? SRSRAN_VEC_PMA(average1, n1, average2, n2) : (0))
#endif
// Exponential moving average
#define SRSRAN_VEC_EMA(data, average, alpha) ((alpha) * (data) + (1 - alpha) * (average))

@ -143,7 +143,6 @@ public:
virtual void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) = 0;
virtual void reestablish() = 0;
virtual void empty_queue() = 0;
virtual void discard_sdu(uint32_t pdcp_sn) = 0;
virtual bool sdu_queue_is_full() = 0;
virtual bool has_data() = 0;
virtual void stop() = 0;
@ -151,6 +150,7 @@ public:
void set_bsr_callback(bsr_callback_t callback);
int write_sdu(unique_byte_buffer_t sdu);
virtual void discard_sdu(uint32_t pdcp_sn);
virtual uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0;
bool tx_enabled = false;

@ -204,25 +204,41 @@ public:
const_iterator end() const { return list.end(); }
};
template <class T>
struct rlc_ringbuffer_base {
virtual ~rlc_ringbuffer_base() = default;
virtual T& add_pdu(size_t sn) = 0;
virtual void remove_pdu(size_t sn) = 0;
virtual T& operator[](size_t sn) = 0;
virtual size_t size() const = 0;
virtual bool empty() const = 0;
virtual bool full() const = 0;
virtual void clear() = 0;
virtual bool has_sn(uint32_t sn) const = 0;
};
template <class T, std::size_t WINDOW_SIZE>
struct rlc_ringbuffer_t {
T& add_pdu(size_t sn)
struct rlc_ringbuffer_t : public rlc_ringbuffer_base<T> {
~rlc_ringbuffer_t() = default;
T& add_pdu(size_t sn) override
{
srsran_expect(not has_sn(sn), "The same SN=%zd should not be added twice", sn);
window.overwrite(sn, T(sn));
return window[sn];
}
void remove_pdu(size_t sn)
void remove_pdu(size_t sn) override
{
srsran_expect(has_sn(sn), "The removed SN=%zd is not in the window", sn);
window.erase(sn);
}
T& operator[](size_t sn) { return window[sn]; }
size_t size() const { return window.size(); }
bool empty() const { return window.empty(); }
void clear() { window.clear(); }
T& operator[](size_t sn) override { return window[sn]; }
size_t size() const override { return window.size(); }
bool full() const override { return window.full(); }
bool empty() const override { return window.empty(); }
void clear() override { window.clear(); }
bool has_sn(uint32_t sn) const { return window.contains(sn); }
bool has_sn(uint32_t sn) const override { return window.contains(sn); }
// Return the sum data bytes of all active PDUs (check PDU is non-null)
uint32_t get_buffered_bytes()
@ -309,41 +325,108 @@ private:
uint32_t count = 0;
};
struct rlc_amd_retx_t {
uint32_t sn;
bool is_segment;
uint32_t so_start;
uint32_t so_end;
uint32_t current_so;
struct rlc_amd_retx_base_t {
const static uint32_t invalid_rlc_sn = std::numeric_limits<uint32_t>::max();
uint32_t sn; ///< sequence number
bool is_segment; ///< flag whether this is a segment or not
uint32_t so_start; ///< offset to first byte of this segment
// so_end or segment_length are different for LTE and NR, hence are defined in subclasses
uint32_t current_so; ///< stores progressing SO during segmentation of this object
rlc_amd_retx_base_t() : sn(invalid_rlc_sn), is_segment(false), so_start(0), current_so(0) {}
virtual ~rlc_amd_retx_base_t() = default;
/**
* @brief overlaps implements a check whether the range of this retransmission object includes
* the given segment offset
* @param so the segment offset to check
* @return true if the segment offset is covered by the retransmission object. Otherwise false
*/
virtual bool overlaps(uint32_t so) const = 0;
};
struct rlc_amd_retx_lte_t : public rlc_amd_retx_base_t {
uint32_t so_end; ///< offset to first byte beyond the end of this segment
rlc_amd_retx_lte_t() : rlc_amd_retx_base_t(), so_end(0) {}
bool overlaps(uint32_t segment_offset) const override
{
return (segment_offset >= so_start) && (segment_offset < so_end);
}
};
struct rlc_amd_retx_nr_t : public rlc_amd_retx_base_t {
uint32_t segment_length; ///< number of bytes contained in this segment
rlc_amd_retx_nr_t() : rlc_amd_retx_base_t(), segment_length(0) {}
bool overlaps(uint32_t segment_offset) const override
{
return (segment_offset >= so_start) && (segment_offset < current_so + segment_length);
}
};
template <class T>
class pdu_retx_queue_base
{
public:
virtual ~pdu_retx_queue_base() = default;
virtual T& push() = 0;
virtual void pop() = 0;
virtual T& front() = 0;
virtual void clear() = 0;
virtual size_t size() const = 0;
virtual bool empty() const = 0;
virtual bool full() const = 0;
virtual T& operator[](size_t idx) = 0;
virtual const T& operator[](size_t idx) const = 0;
virtual bool has_sn(uint32_t sn) const = 0;
virtual bool has_sn(uint32_t sn, uint32_t so) const = 0;
};
template <std::size_t WINDOW_SIZE>
class pdu_retx_queue
template <class T, std::size_t WINDOW_SIZE>
class pdu_retx_queue : public pdu_retx_queue_base<T>
{
public:
rlc_amd_retx_t& push()
~pdu_retx_queue() = default;
T& push() override
{
assert(not full());
rlc_amd_retx_t& p = buffer[wpos];
wpos = (wpos + 1) % WINDOW_SIZE;
T& p = buffer[wpos];
wpos = (wpos + 1) % WINDOW_SIZE;
return p;
}
void pop() { rpos = (rpos + 1) % WINDOW_SIZE; }
void pop() override { rpos = (rpos + 1) % WINDOW_SIZE; }
rlc_amd_retx_t& front()
T& front() override
{
assert(not empty());
return buffer[rpos];
}
void clear()
T& operator[](size_t idx) override
{
srsran_assert(idx < size(), "Out-of-bounds access to element idx=%zd", idx);
return buffer[(rpos + idx) % WINDOW_SIZE];
}
const T& operator[](size_t idx) const override
{
srsran_assert(idx < size(), "Out-of-bounds access to element idx=%zd", idx);
return buffer[(rpos + idx) % WINDOW_SIZE];
}
void clear() override
{
wpos = 0;
rpos = 0;
}
bool has_sn(uint32_t sn) const
bool has_sn(uint32_t sn) const override
{
for (size_t i = rpos; i != wpos; i = (i + 1) % WINDOW_SIZE) {
if (buffer[i].sn == sn) {
@ -353,14 +436,26 @@ public:
return false;
}
size_t size() const { return (wpos >= rpos) ? wpos - rpos : WINDOW_SIZE + wpos - rpos; }
bool empty() const { return wpos == rpos; }
bool full() const { return size() == WINDOW_SIZE - 1; }
bool has_sn(uint32_t sn, uint32_t so) const override
{
for (size_t i = rpos; i != wpos; i = (i + 1) % WINDOW_SIZE) {
if (buffer[i].sn == sn) {
if (buffer[i].overlaps(so)) {
return true;
}
}
}
return false;
}
size_t size() const override { return (wpos >= rpos) ? wpos - rpos : WINDOW_SIZE + wpos - rpos; }
bool empty() const override { return wpos == rpos; }
bool full() const override { return size() == WINDOW_SIZE - 1; }
private:
std::array<rlc_amd_retx_t, WINDOW_SIZE> buffer;
size_t wpos = 0;
size_t rpos = 0;
std::array<T, WINDOW_SIZE> buffer;
size_t wpos = 0;
size_t rpos = 0;
};
} // namespace srsran

@ -68,7 +68,6 @@ public:
void stop();
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes);
void discard_sdu(uint32_t discard_sn);
bool sdu_queue_is_full();
bool has_data();
@ -89,11 +88,11 @@ private:
int build_status_pdu(uint8_t* payload, uint32_t nof_bytes);
int build_retx_pdu(uint8_t* payload, uint32_t nof_bytes);
int build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx);
int build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_lte_t retx);
int build_data_pdu(uint8_t* payload, uint32_t nof_bytes);
void update_notification_ack_info(uint32_t rlc_sn);
int required_buffer_size(const rlc_amd_retx_t& retx);
int required_buffer_size(const rlc_amd_retx_lte_t& retx);
void retransmit_pdu(uint32_t sn);
// Helpers
@ -147,7 +146,7 @@ private:
// Tx windows
rlc_ringbuffer_t<rlc_amd_tx_pdu<rlc_amd_pdu_header_t>, RLC_AM_WINDOW_SIZE> tx_window;
pdu_retx_queue<RLC_AM_WINDOW_SIZE> retx_queue;
pdu_retx_queue<rlc_amd_retx_lte_t, RLC_AM_WINDOW_SIZE> retx_queue;
pdcp_sn_vector_t notify_info_vec;
// Mutexes

@ -62,7 +62,7 @@ int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload);
uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header);
uint32_t rlc_am_packed_length(rlc_status_pdu_t* status);
uint32_t rlc_am_packed_length(rlc_amd_retx_t retx);
uint32_t rlc_am_packed_length(rlc_amd_retx_lte_t retx);
bool rlc_am_is_pdu_segment(uint8_t* payload);
bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status, uint32_t rx_win_min = 0);
bool rlc_am_start_aligned(const uint8_t fi);

@ -25,6 +25,7 @@
#include "srsran/common/buffer_pool.h"
#include "srsran/common/common.h"
#include "srsran/common/timers.h"
#include "srsran/interfaces/pdcp_interface_types.h"
#include "srsran/rlc/rlc_am_base.h"
#include "srsran/rlc/rlc_am_data_structs.h"
#include "srsran/rlc/rlc_am_nr_packing.h"
@ -81,13 +82,12 @@ struct rlc_am_nr_tx_state_t {
struct rlc_amd_tx_pdu_nr {
const uint32_t rlc_sn = INVALID_RLC_SN;
const uint32_t pdcp_sn = INVALID_RLC_SN;
uint32_t pdcp_sn = INVALID_RLC_SN;
rlc_am_nr_pdu_header_t header = {};
unique_byte_buffer_t sdu_buf = nullptr;
uint32_t retx_count = 0;
uint32_t retx_count = RETX_COUNT_NOT_STARTED;
struct pdu_segment {
uint32_t so = 0;
uint32_t retx_count = 0;
uint32_t payload_len = 0;
};
std::list<pdu_segment> segment_list;
@ -105,22 +105,23 @@ public:
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) final;
void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) final;
void discard_sdu(uint32_t discard_sn) final;
bool sdu_queue_is_full() final;
void reestablish() final;
void stop() final;
int write_sdu(unique_byte_buffer_t sdu);
void empty_queue() final;
void empty_queue_no_lock();
// Data PDU helpers
uint32_t build_new_pdu(uint8_t* payload, uint32_t nof_bytes);
uint32_t build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* payload, uint32_t nof_bytes);
uint32_t build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* payload, uint32_t nof_bytes);
uint32_t build_retx_pdu(uint8_t* payload, uint32_t nof_bytes);
uint32_t build_retx_pdu_without_segmentation(rlc_amd_retx_t& retx, uint8_t* payload, uint32_t nof_bytes);
uint32_t build_retx_pdu_with_segmentation(rlc_amd_retx_t& retx, uint8_t* payload, uint32_t nof_bytes);
bool is_retx_segmentation_required(const rlc_amd_retx_t& retx, uint32_t nof_bytes);
uint32_t get_retx_expected_hdr_len(const rlc_amd_retx_t& retx);
uint32_t build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t retx, uint8_t* payload, uint32_t nof_bytes);
uint32_t build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes);
bool is_retx_segmentation_required(const rlc_amd_retx_nr_t& retx, uint32_t nof_bytes);
uint32_t get_retx_expected_hdr_len(const rlc_amd_retx_nr_t& retx);
// Buffer State
bool has_data() final;
@ -132,18 +133,21 @@ public:
uint32_t build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes);
// Polling
uint8_t get_pdu_poll();
uint8_t get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes);
void stop() final;
// Timers
void timer_expired(uint32_t timeout_id);
// Window helpers
bool inside_tx_window(uint32_t sn) const;
private:
rlc_am* parent = nullptr;
rlc_am_nr_rx* rx = nullptr;
uint32_t mod_nr = 4096;
uint32_t mod_nr = cardinality(rlc_am_nr_sn_size_t());
inline uint32_t tx_mod_base_nr(uint32_t sn) const;
void check_sn_reached_max_retx(uint32_t sn);
/****************************************************************************
* Configurable parameters
@ -155,26 +159,41 @@ private:
* Tx state variables
* Ref: 3GPP TS 38.322 version 16.2.0 Section 7.1
***************************************************************************/
struct rlc_am_nr_tx_state_t st = {};
rlc_ringbuffer_t<rlc_amd_tx_pdu_nr, RLC_AM_WINDOW_SIZE> tx_window;
struct rlc_am_nr_tx_state_t st = {};
std::unique_ptr<rlc_ringbuffer_base<rlc_amd_tx_pdu_nr> > tx_window;
// Queues and buffers
pdu_retx_queue<RLC_AM_WINDOW_SIZE> retx_queue;
uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented.
// Queues, buffers and container
std::unique_ptr<pdu_retx_queue_base<rlc_amd_retx_nr_t> > retx_queue;
uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented.
pdcp_sn_vector_t notify_info_vec;
// Helper constants
uint32_t min_hdr_size = 2;
uint32_t min_hdr_size = 2; // Pre-initialized for 12 bit SN, updated by configure()
uint32_t so_size = 2;
uint32_t max_hdr_size = 4;
uint32_t max_hdr_size = 4; // Pre-initialized for 12 bit SN, updated by configure()
/****************************************************************************
* Tx constants
* Ref: 3GPP TS 38.322 version 16.2.0 Section 7.2
***************************************************************************/
inline uint32_t tx_window_size() const;
/****************************************************************************
* TX timers
* Ref: 3GPP TS 38.322 version 16.2.0 Section 7.3
***************************************************************************/
srsran::timer_handler::unique_timer poll_retransmit_timer;
public:
// Getters/Setters
void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing.
rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing.
uint32_t get_tx_window_size() { return tx_window.size(); } // This should only be used for testing.
void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing.
rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing.
uint32_t get_tx_window_utilization() { return tx_window->size(); } // This should only be used for testing.
// Debug Helper
// Debug Helpers
void debug_state() const;
void info_state() const;
void debug_window() const;
};
/****************************************************************************
@ -231,7 +250,12 @@ public:
bool inside_rx_window(uint32_t sn);
void write_to_upper_layers(uint32_t lcid, unique_byte_buffer_t sdu);
void insert_received_segment(rlc_amd_rx_pdu_nr segment, rlc_amd_rx_sdu_nr_t::segment_list_t& segment_list) const;
bool have_all_segments_been_received(const rlc_amd_rx_sdu_nr_t::segment_list_t& segment_list) const;
/**
* @brief update_segment_inventory This function updates the flags has_gap and fully_received of an SDU
* according to the current inventory of received SDU segments
* @param rx_sdu The SDU to operate on
*/
void update_segment_inventory(rlc_amd_rx_sdu_nr_t& rx_sdu) const;
// Metrics
uint32_t get_sdu_rx_latency_ms() final;
@ -242,17 +266,18 @@ public:
// Helpers
void debug_state() const;
void debug_window() const;
private:
rlc_am* parent = nullptr;
rlc_am_nr_tx* tx = nullptr;
byte_buffer_pool* pool = nullptr;
uint32_t mod_nr = 4096;
uint32_t mod_nr = cardinality(rlc_am_nr_sn_size_t());
uint32_t rx_mod_base_nr(uint32_t sn) const;
// RX Window
rlc_ringbuffer_t<rlc_amd_rx_sdu_nr_t, RLC_AM_WINDOW_SIZE> rx_window;
std::unique_ptr<rlc_ringbuffer_base<rlc_amd_rx_sdu_nr_t> > rx_window;
// Mutexes
std::mutex mutex;
@ -271,16 +296,22 @@ private:
rlc_am_nr_config_t cfg = {};
/****************************************************************************
* Tx state variables
* Rx state variables
* Ref: 3GPP TS 38.322 version 16.2.0 Section 7.1
***************************************************************************/
struct rlc_am_nr_rx_state_t st = {};
/****************************************************************************
* Rx constants
* Ref: 3GPP TS 38.322 version 16.2.0 Section 7.2
***************************************************************************/
inline uint32_t rx_window_size() const;
public:
// Getters/Setters
void set_rx_state(const rlc_am_nr_rx_state_t& st_) { st = st_; } // This should only be used for testing.
rlc_am_nr_rx_state_t get_rx_state() { return st; } // This should only be used for testing.
uint32_t get_rx_window_size() { return rx_window.size(); } // This should only be used for testing.
void set_rx_state(const rlc_am_nr_rx_state_t& st_) { st = st_; } // This should only be used for testing.
rlc_am_nr_rx_state_t get_rx_state() { return st; } // This should only be used for testing.
uint32_t get_rx_window_size() { return rx_window->size(); } // This should only be used for testing.
};
} // namespace srsran

@ -28,7 +28,8 @@
namespace srsran {
const uint32_t INVALID_RLC_SN = 0xFFFFFFFF;
const uint32_t INVALID_RLC_SN = 0xFFFFFFFF;
const uint32_t RETX_COUNT_NOT_STARTED = 0xFFFFFFFF;
///< AM NR PDU header
struct rlc_am_nr_pdu_header_t {
@ -69,6 +70,7 @@ struct rlc_amd_rx_pdu_nr_cmp {
struct rlc_amd_rx_sdu_nr_t {
uint32_t rlc_sn = 0;
bool fully_received = false;
bool has_gap = false;
unique_byte_buffer_t buf;
using segment_list_t = std::set<rlc_amd_rx_pdu_nr, rlc_amd_rx_pdu_nr_cmp>;
segment_list_t segments;
@ -85,14 +87,43 @@ struct rlc_amd_tx_sdu_nr_t {
explicit rlc_amd_tx_sdu_nr_t(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {}
};
///< AM NR Status PDU header (perhaps merge with LTE version)
typedef struct {
rlc_am_nr_control_pdu_type_t cpt;
uint32_t ack_sn; ///< SN of the next not received RLC Data PDU
uint16_t N_nack; ///< number of NACKs
uint8_t nack_range; ///< number of consecutively lost RLC SDUs starting from and including NACK_SN
rlc_status_nack_t nacks[RLC_AM_WINDOW_SIZE];
} rlc_am_nr_status_pdu_t;
constexpr uint32_t rlc_am_nr_status_pdu_sizeof_header_ack_sn = 3; ///< header fixed part and ACK SN
constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn = 2; ///< NACK SN and extension fields (12 bit SN)
constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn = 3; ///< NACK SN and extension fields (18 bit SN)
constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_so = 4; ///< NACK segment offsets (start and end)
constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_range = 1; ///< NACK range (nof consecutively lost SDUs)
/// AM NR Status PDU header
class rlc_am_nr_status_pdu_t
{
private:
/// Stored SN size required to compute the packed size
rlc_am_nr_sn_size_t sn_size = rlc_am_nr_sn_size_t::nulltype;
/// Internal NACK container; keep in sync with packed_size_
std::vector<rlc_status_nack_t> nacks_ = {};
/// Stores the current packed size; sync on each change of nacks_
uint32_t packed_size_ = rlc_am_nr_status_pdu_sizeof_header_ack_sn;
void refresh_packed_size();
uint32_t nack_size(const rlc_status_nack_t& nack) const;
public:
/// CPT header
rlc_am_nr_control_pdu_type_t cpt = rlc_am_nr_control_pdu_type_t::status_pdu;
/// SN of the next not received RLC Data PDU
uint32_t ack_sn = INVALID_RLC_SN;
/// Read-only reference to NACKs
const std::vector<rlc_status_nack_t>& nacks = nacks_;
/// Read-only reference to packed size
const uint32_t& packed_size = packed_size_;
rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size);
void reset();
void push_nack(const rlc_status_nack_t& nack);
const std::vector<rlc_status_nack_t>& get_nacks() const { return nacks_; }
uint32_t get_packed_size() const { return packed_size; }
bool trim(uint32_t max_packed_size);
};
/****************************************************************************
* Header pack/unpack helper functions for NR
@ -112,6 +143,10 @@ uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, b
uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header);
/****************************************************************************
* Status PDU pack/unpack helper functions for NR
* Ref: 3GPP TS 38.322 v16.2.0 Section 6.2.2.5
***************************************************************************/
uint32_t
rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status);
@ -119,10 +154,16 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload,
const uint32_t nof_bytes,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_status_pdu_t* status);
uint32_t
rlc_am_nr_read_status_pdu_12bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status);
uint32_t
rlc_am_nr_read_status_pdu_18bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status);
int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu,
const rlc_am_nr_sn_size_t sn_size,
byte_buffer_t* pdu);
int32_t rlc_am_nr_write_status_pdu_12bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu);
int32_t rlc_am_nr_write_status_pdu_18bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu);
/**
* Logs Status PDU into provided log channel, using fmt_str as format string
@ -131,21 +172,35 @@ template <typename... Args>
void log_rlc_am_nr_status_pdu_to_string(srslog::log_channel& log_ch,
const char* fmt_str,
rlc_am_nr_status_pdu_t* status,
const std::string& rb_name,
Args&&... args)
{
if (not log_ch.enabled()) {
return;
}
fmt::memory_buffer buffer;
fmt::format_to(buffer, "ACK_SN = {}, N_nack = {}", status->ack_sn, status->N_nack);
if (status->N_nack > 0) {
fmt::format_to(buffer, "ACK_SN = {}, N_nack = {}", status->ack_sn, status->nacks.size());
if (status->nacks.size() > 0) {
fmt::format_to(buffer, ", NACK_SN = ");
for (uint32_t i = 0; i < status->N_nack; ++i) {
if (status->nacks[i].has_so) {
fmt::format_to(
buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end);
for (uint32_t i = 0; i < status->nacks.size(); ++i) {
if (status->nacks[i].has_nack_range) {
if (status->nacks[i].has_so) {
fmt::format_to(buffer,
"[{} {}:{} r{}]",
status->nacks[i].nack_sn,
status->nacks[i].so_start,
status->nacks[i].so_end,
status->nacks[i].nack_range);
} else {
fmt::format_to(buffer, "[{} r{}]", status->nacks[i].nack_sn, status->nacks[i].nack_range);
}
} else {
fmt::format_to(buffer, "[{}]", status->nacks[i].nack_sn);
if (status->nacks[i].has_so) {
fmt::format_to(
buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end);
} else {
fmt::format_to(buffer, "[{}]", status->nacks[i].nack_sn);
}
}
}
}
@ -155,18 +210,21 @@ void log_rlc_am_nr_status_pdu_to_string(srslog::log_channel& log_ch,
/*
* Log NR AMD PDUs
*/
inline void log_rlc_am_nr_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_am_nr_pdu_header_t& header)
inline void log_rlc_am_nr_pdu_header_to_string(srslog::log_channel& log_ch,
const rlc_am_nr_pdu_header_t& header,
const std::string& rb_name)
{
if (not log_ch.enabled()) {
return;
}
fmt::memory_buffer buffer;
fmt::format_to(buffer,
"[{}, P={}, SI={}, SN_SIZE={}, SN={}, SO={}",
"{}: [{}, P={}, SI={}, SN_SIZE={}, SN={}, SO={}",
rb_name,
rlc_dc_field_text[header.dc],
(header.p ? "1" : "0"),
to_string_short(header.si),
header.sn,
to_string(header.sn_size),
header.sn,
header.so);
fmt::format_to(buffer, "]");

@ -43,6 +43,9 @@ namespace srsran {
#define RLC_MAX_SDU_SIZE ((1 << 11) - 1) // Length of LI field is 11bits
#define RLC_AM_MIN_DATA_PDU_SIZE (3) // AMD PDU with 10 bit SN (length of LI field is 11 bits) (No LI)
#define RLC_AM_NR_TYP_NACKS 512 // Expected number of NACKs in status PDU before expanding space by alloc
#define RLC_AM_NR_MAX_NACKS 2048 // Maximum number of NACKs in status PDU
#define RlcDebug(fmt, ...) logger.debug("%s: " fmt, rb_name, ##__VA_ARGS__)
#define RlcInfo(fmt, ...) logger.info("%s: " fmt, rb_name, ##__VA_ARGS__)
#define RlcWarning(fmt, ...) logger.warning("%s: " fmt, rb_name, ##__VA_ARGS__)
@ -171,17 +174,21 @@ public:
// NACK helper (for LTE and NR)
struct rlc_status_nack_t {
uint32_t nack_sn;
bool has_so;
uint16_t so_start;
uint16_t so_end;
uint32_t nack_sn; // Sequence Number (SN) of first missing SDU
bool has_so; // NACKs continuous sequence of bytes [so_start..so_end]
uint16_t so_start; // First missing byte in SDU with SN=nack_sn
uint16_t so_end; // Last missing byte in SDU with SN=nack_sn or SN=nack_sn+nack_range-1 if has_nack_range.
bool has_nack_range; // NACKs continuous sequence of SDUs
uint8_t nack_range; // Number of SDUs being NACKed (including SN=nack_sn)
rlc_status_nack_t()
{
has_so = false;
nack_sn = 0;
so_start = 0;
so_end = 0;
has_so = false;
nack_sn = 0;
so_start = 0;
so_end = 0;
has_nack_range = false;
nack_range = 0;
}
};

@ -58,6 +58,7 @@ private:
bool configure(const rlc_config_t& cfg, std::string rb_name);
uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes);
void discard_sdu(uint32_t discard_sn);
uint32_t get_buffer_state();
bool sdu_queue_is_full();

@ -62,6 +62,7 @@ private:
bool configure(const rlc_config_t& cfg, std::string rb_name);
uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes);
void discard_sdu(uint32_t discard_sn);
uint32_t get_buffer_state();
private:

@ -42,7 +42,7 @@ namespace srsran {
class undelivered_sdus_queue
{
public:
explicit undelivered_sdus_queue(srsran::task_sched_handle task_sched);
explicit undelivered_sdus_queue(srsran::task_sched_handle task_sched, uint32_t sn_mod);
bool empty() const { return count == 0; }
bool is_full() const { return count >= capacity; }
@ -82,8 +82,9 @@ public:
private:
const static uint32_t capacity = 4096;
const static uint32_t invalid_sn = -1;
uint32_t sn_mod = 0;
static uint32_t increment_sn(uint32_t sn) { return (sn + 1) % capacity; }
uint32_t increment_sn(uint32_t sn) { return (sn + 1) % sn_mod; }
struct sdu_data {
srsran::unique_byte_buffer_t sdu;

@ -28,7 +28,7 @@ add_library(srsran_asn1 STATIC
# ASN1 utils
add_library(asn1_utils STATIC asn1_utils.cc)
target_link_libraries(asn1_utils srsran_common)
INSTALL(TARGETS asn1_utils DESTINATION ${LIBRARY_DIR})
install(TARGETS asn1_utils DESTINATION ${LIBRARY_DIR} OPTIONAL)
# RRC ASN1 lib
add_library(rrc_asn1 STATIC
@ -54,29 +54,29 @@ add_library(rrc_asn1 STATIC
# Compile RRC ASN1 optimized for size
target_compile_options(rrc_asn1 PRIVATE "-Os")
target_link_libraries(rrc_asn1 asn1_utils srsran_common)
INSTALL(TARGETS rrc_asn1 DESTINATION ${LIBRARY_DIR})
install(TARGETS rrc_asn1 DESTINATION ${LIBRARY_DIR} OPTIONAL)
# S1AP ASN1 lib
add_library(s1ap_asn1 STATIC
s1ap.cc s1ap_utils.cc)
target_compile_options(s1ap_asn1 PRIVATE "-Os")
target_link_libraries(s1ap_asn1 asn1_utils srsran_common)
INSTALL(TARGETS s1ap_asn1 DESTINATION ${LIBRARY_DIR})
install(TARGETS s1ap_asn1 DESTINATION ${LIBRARY_DIR} OPTIONAL)
# RRC NR ASN1
add_library(rrc_nr_asn1 STATIC rrc_nr.cc rrc_nr_utils.cc)
target_compile_options(rrc_nr_asn1 PRIVATE "-Os")
target_link_libraries(rrc_nr_asn1 asn1_utils srsran_common)
INSTALL(TARGETS rrc_nr_asn1 DESTINATION ${LIBRARY_DIR})
install(TARGETS rrc_nr_asn1 DESTINATION ${LIBRARY_DIR} OPTIONAL)
# NGAP ASN1
add_library(ngap_nr_asn1 STATIC ngap.cc)
target_compile_options(ngap_nr_asn1 PRIVATE "-Os")
target_link_libraries(ngap_nr_asn1 asn1_utils srsran_common)
INSTALL(TARGETS ngap_nr_asn1 DESTINATION ${LIBRARY_DIR})
install(TARGETS ngap_nr_asn1 DESTINATION ${LIBRARY_DIR} OPTIONAL)
# NAS 5G
add_library(nas_5g_msg STATIC nas_5g_msg.cc nas_5g_ies.cc nas_5g_utils.cc)
target_compile_options(nas_5g_msg PRIVATE "-Os")
target_link_libraries(nas_5g_msg asn1_utils srsran_common)
INSTALL(TARGETS nas_5g_msg DESTINATION ${LIBRARY_DIR})
install(TARGETS nas_5g_msg DESTINATION ${LIBRARY_DIR} OPTIONAL)

@ -195,7 +195,7 @@ SRSASN_CODE unpack_bits(T& val, Ptr& ptr, uint8_t& offset, const uint8_t* max_pt
n_bits = 0;
} else {
auto mask = static_cast<uint8_t>((1u << (8u - offset)) - 1u);
val += ((uint32_t)((*ptr) & mask)) << (n_bits - 8 + offset);
val += static_cast<T>((*ptr) & mask) << (n_bits - 8 + offset);
n_bits -= 8 - offset;
offset = 0;
ptr++;

File diff suppressed because it is too large Load Diff

@ -512,6 +512,21 @@ bool make_phy_harq_ack_cfg(const phys_cell_group_cfg_s& phys_cell_group_cfg,
return true;
}
void make_phy_search_space0_cfg(srsran_search_space_t* in_srsran_search_space)
{
in_srsran_search_space->id = 0;
in_srsran_search_space->coreset_id = 0;
in_srsran_search_space->type = srsran_search_space_type_common_0;
in_srsran_search_space->nof_candidates[0] = 0;
in_srsran_search_space->nof_candidates[1] = 0;
in_srsran_search_space->nof_candidates[2] = 4;
in_srsran_search_space->nof_candidates[3] = 2;
in_srsran_search_space->nof_candidates[4] = 0;
in_srsran_search_space->nof_formats = 1;
in_srsran_search_space->formats[0] = srsran_dci_format_nr_1_0;
in_srsran_search_space->duration = 1;
}
bool make_phy_search_space_cfg(const search_space_s& search_space, srsran_search_space_t* in_srsran_search_space)
{
srsran_search_space_t srsran_search_space = {};

@ -63,6 +63,6 @@ target_include_directories(srsran_common PUBLIC ${SEC_INCLUDE_DIRS} ${CMAKE_SOUR
target_link_libraries(srsran_common srsran_phy support srslog ${SEC_LIBRARIES} ${BACKWARD_LIBRARIES} ${SCTP_LIBRARIES})
target_compile_definitions(srsran_common PRIVATE ${BACKWARD_DEFINITIONS})
INSTALL(TARGETS srsran_common DESTINATION ${LIBRARY_DIR})
install(TARGETS srsran_common DESTINATION ${LIBRARY_DIR} OPTIONAL)
add_subdirectory(test)

@ -61,18 +61,28 @@ bool ue_bearer_manager_impl::has_active_radio_bearer(uint32_t eps_bearer_id)
return bearers.count(eps_bearer_id) > 0;
}
ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_radio_bearer(uint32_t eps_bearer_id)
ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_radio_bearer(uint32_t eps_bearer_id) const
{
auto it = bearers.find(eps_bearer_id);
return it != bearers.end() ? it->second : invalid_rb;
}
ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_eps_bearer_id_for_lcid(uint32_t lcid)
ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_eps_bearer_id_for_lcid(uint32_t lcid) const
{
auto lcid_it = lcid_to_eps_bearer_id.find(lcid);
return lcid_it != lcid_to_eps_bearer_id.end() ? bearers.at(lcid_it->second) : invalid_rb;
}
bool ue_bearer_manager_impl::set_five_qi(uint32_t eps_bearer_id, uint16_t five_qi)
{
auto it = bearers.find(eps_bearer_id);
if (it == bearers.end()) {
return false;
}
it->second.five_qi = five_qi;
return true;
}
} // namespace detail
} // namespace srsran
@ -185,7 +195,7 @@ bool enb_bearer_manager::has_active_radio_bearer(uint16_t rnti, uint32_t eps_bea
return user_it->second.has_active_radio_bearer(eps_bearer_id);
}
enb_bearer_manager::radio_bearer_t enb_bearer_manager::get_lcid_bearer(uint16_t rnti, uint32_t lcid)
enb_bearer_manager::radio_bearer_t enb_bearer_manager::get_lcid_bearer(uint16_t rnti, uint32_t lcid) const
{
auto user_it = users_map.find(rnti);
if (user_it == users_map.end()) {
@ -203,4 +213,13 @@ enb_bearer_manager::radio_bearer_t enb_bearer_manager::get_radio_bearer(uint16_t
return user_it->second.get_radio_bearer(eps_bearer_id);
}
bool enb_bearer_manager::set_five_qi(uint16_t rnti, uint32_t eps_bearer_id, uint16_t five_qi)
{
auto user_it = users_map.find(rnti);
if (user_it == users_map.end()) {
return false;
}
return user_it->second.set_five_qi(eps_bearer_id, five_qi);
}
} // namespace srsenb

@ -330,7 +330,7 @@ bool phy_cfg_nr_t::get_pucch_uci_cfg(const srsran_slot_cfg_t& slot_cfg,
srsran_pucch_nr_resource_t& resource) const
{
// Select PUCCH resource
if (srsran_ra_ul_nr_pucch_resource(&pucch, &uci_cfg, &resource) < SRSRAN_SUCCESS) {
if (srsran_ra_ul_nr_pucch_resource(&pucch, &uci_cfg, carrier.nof_prb, &resource) < SRSRAN_SUCCESS) {
ERROR("Selecting PUCCH resource");
return false;
}

@ -22,4 +22,4 @@ set(SOURCES gtpu.cc)
add_library(srsran_gtpu STATIC ${SOURCES})
target_link_libraries(srsran_gtpu srsran_common srsran_asn1 ${ATOMIC_LIBS})
INSTALL(TARGETS srsran_gtpu DESTINATION ${LIBRARY_DIR})
install(TARGETS srsran_gtpu DESTINATION ${LIBRARY_DIR} OPTIONAL)

@ -22,6 +22,6 @@ SET(SOURCES pdu.cc pdu_queue.cc mac_sch_pdu_nr.cc mac_rar_pdu_nr.cc)
add_library(srsran_mac STATIC ${SOURCES})
target_link_libraries(srsran_mac srsran_common)
INSTALL(TARGETS srsran_mac DESTINATION ${LIBRARY_DIR})
install(TARGETS srsran_mac DESTINATION ${LIBRARY_DIR} OPTIONAL)
add_subdirectory(test)

@ -25,4 +25,4 @@ set(SOURCES pdcp.cc
add_library(srsran_pdcp STATIC ${SOURCES})
target_link_libraries(srsran_pdcp srsran_common srsran_asn1 ${ATOMIC_LIBS})
INSTALL(TARGETS srsran_pdcp DESTINATION ${LIBRARY_DIR})
install(TARGETS srsran_pdcp DESTINATION ${LIBRARY_DIR} OPTIONAL)

@ -100,7 +100,7 @@ bool pdcp_entity_lte::configure(const pdcp_config_t& cnfg_)
logger.info("Status Report Required: %s", cfg.status_report_required ? "True" : "False");
if (is_drb() and not rlc->rb_is_um(lcid)) {
undelivered_sdus = std::unique_ptr<undelivered_sdus_queue>(new undelivered_sdus_queue(task_sched));
undelivered_sdus = std::unique_ptr<undelivered_sdus_queue>(new undelivered_sdus_queue(task_sched, maximum_pdcp_sn));
rx_counts_info.reserve(reordering_window);
}
@ -230,6 +230,10 @@ void pdcp_entity_lte::write_sdu(unique_byte_buffer_t sdu, int upper_sn)
// Pass PDU to lower layers
metrics.num_tx_pdus++;
metrics.num_tx_pdu_bytes += sdu->N_bytes;
// Count TX'd bytes as if they were ACK'd if RLC is UM
if (rlc->rb_is_um(lcid)) {
metrics.num_tx_acked_bytes = metrics.num_tx_pdu_bytes;
}
rlc->write_sdu(lcid, std::move(sdu));
}
@ -867,7 +871,7 @@ void pdcp_entity_lte::reset_metrics()
/****************************************************************************
* Undelivered SDUs queue helpers
***************************************************************************/
undelivered_sdus_queue::undelivered_sdus_queue(srsran::task_sched_handle task_sched)
undelivered_sdus_queue::undelivered_sdus_queue(srsran::task_sched_handle task_sched, uint32_t sn_mod) : sn_mod(sn_mod)
{
for (auto& e : sdus) {
e.discard_timer = task_sched.get_unique_timer();

@ -59,4 +59,4 @@ set(srsran_srcs $<TARGET_OBJECTS:srsran_agc>
add_library(srsran_phy STATIC ${srsran_srcs} )
target_link_libraries(srsran_phy pthread m ${FFT_LIBRARIES})
INSTALL(TARGETS srsran_phy DESTINATION ${LIBRARY_DIR})
install(TARGETS srsran_phy DESTINATION ${LIBRARY_DIR} OPTIONAL)

@ -162,6 +162,10 @@ int srsran_cfr_init(srsran_cfr_t* q, srsran_cfr_cfg_t* cfg)
ERROR("Error, invalid configuration");
goto clean_exit;
}
if (cfg->cfr_mode == SRSRAN_CFR_THR_INVALID) {
ERROR("Error, invalid CFR mode");
goto clean_exit;
}
if (cfg->cfr_mode == SRSRAN_CFR_THR_MANUAL && cfg->manual_thr <= 0) {
ERROR("Error, invalid configuration for manual threshold");
goto clean_exit;
@ -330,6 +334,9 @@ bool srsran_cfr_params_valid(srsran_cfr_cfg_t* cfr_conf)
if (cfr_conf == NULL) {
return false;
}
if (cfr_conf->cfr_mode == SRSRAN_CFR_THR_INVALID) {
return false;
}
if (cfr_conf->alpha < 0 || cfr_conf->alpha > 1) {
return false;
}
@ -374,3 +381,22 @@ int srsran_cfr_set_papr(srsran_cfr_t* q, float papr)
q->max_papr_lin = srsran_convert_dB_to_power(q->cfg.max_papr_db);
return SRSRAN_SUCCESS;
}
srsran_cfr_mode_t srsran_cfr_str2mode(const char* mode_str)
{
srsran_cfr_mode_t ret;
if (strcmp(mode_str, "")) {
if (!strcmp(mode_str, "manual")) {
ret = SRSRAN_CFR_THR_MANUAL;
} else if (!strcmp(mode_str, "auto_cma")) {
ret = SRSRAN_CFR_THR_AUTO_CMA;
} else if (!strcmp(mode_str, "auto_ema")) {
ret = SRSRAN_CFR_THR_AUTO_EMA;
} else {
ret = SRSRAN_CFR_THR_INVALID; // mode_str is not recognised
}
} else {
ret = SRSRAN_CFR_THR_INVALID; // mode_str is empty
}
return ret;
}

@ -30,13 +30,9 @@
#define MAX_ACPR_DB -100
// CFR type test args
static char cfr_manual_str[] = "manual";
static char cfr_auto_cma_str[] = "auto_cma";
static char cfr_auto_ema_str[] = "auto_ema";
// Default CFR type
static char* cfr_type_arg = cfr_manual_str;
static char* cfr_mode_str = "manual";
static int nof_prb = -1;
static srsran_cp_t cp = SRSRAN_CP_NORM;
@ -69,7 +65,7 @@ static void usage(char* prog)
printf("\t-e extended cyclic prefix [Default Normal]\n");
printf("\t-f Number of frames [Default %d]\n", nof_frames);
printf("\t-r Number of repetitions [Default %d]\n", nof_repetitions);
printf("\t-m CFR mode: %s, %s, %s [Default %s]\n", cfr_manual_str, cfr_auto_cma_str, cfr_auto_ema_str, cfr_type_arg);
printf("\t-m CFR mode: manual, auto_cma, auto_ema [Default %s]\n", cfr_mode_str);
printf("\t-d Use DC subcarrier: [Default DC empty]\n");
printf("\t-a CFR alpha: [Default %.2f]\n", alpha);
printf("\t-t CFR manual threshold: [Default %.2f]\n", thr_manual);
@ -98,7 +94,7 @@ static int parse_args(int argc, char** argv)
nof_frames = (int)strtol(argv[optind], NULL, 10);
break;
case 'm':
cfr_type_arg = argv[optind];
cfr_mode_str = argv[optind];
break;
case 'a':
alpha = strtof(argv[optind], NULL);
@ -149,13 +145,8 @@ int main(int argc, char** argv)
goto clean_exit;
}
if (!strcmp(cfr_type_arg, cfr_manual_str)) {
cfr_mode = SRSRAN_CFR_THR_MANUAL;
} else if (!strcmp(cfr_type_arg, cfr_auto_cma_str)) {
cfr_mode = SRSRAN_CFR_THR_AUTO_CMA;
} else if (!strcmp(cfr_type_arg, cfr_auto_ema_str)) {
cfr_mode = SRSRAN_CFR_THR_AUTO_EMA;
} else {
cfr_mode = srsran_cfr_str2mode(cfr_mode_str);
if (cfr_mode == SRSRAN_CFR_THR_INVALID) {
ERROR("CFR mode is not recognised");
goto clean_exit;
}
@ -202,6 +193,11 @@ int main(int argc, char** argv)
cfr_tx_cfg.ema_alpha = ema_alpha;
cfr_tx_cfg.dc_sc = dc_empty;
if (!srsran_cfr_params_valid(&cfr_tx_cfg)) {
ERROR("Invalid CFR configuration");
goto clean_exit;
}
if (srsran_cfr_init(&cfr, &cfr_tx_cfg)) {
ERROR("Error initializing CFR");
goto clean_exit;

@ -355,16 +355,30 @@ static void chest_ul_estimate(srsran_chest_ul_t* q,
}
}
// Estimate received pilot power
// Measure reference signal RE average power
cf_t corr = srsran_vec_acc_cc(q->pilot_recv_signal, nslots * nrefs_sym) / (nslots * nrefs_sym);
float rsrp_avg = __real__ corr * __real__ corr + __imag__ corr * __imag__ corr;
// Measure EPRE
float epre = srsran_vec_avg_power_cf(q->pilot_recv_signal, nslots * nrefs_sym);
// RSRP shall not be greater than EPRE
rsrp_avg = SRSRAN_MIN(rsrp_avg, epre);
// Calculate SNR
if (isnormal(res->noise_estimate)) {
res->snr = srsran_vec_avg_power_cf(q->pilot_recv_signal, nslots * nrefs_sym) / res->noise_estimate;
res->snr = epre / res->noise_estimate;
} else {
res->snr = NAN;
}
// Convert measurements in logarithm scale
res->snr_db = srsran_convert_power_to_dB(res->snr);
res->noise_estimate_dbm = srsran_convert_power_to_dBm(res->noise_estimate);
// Set EPRE and RSRP
res->epre = epre;
res->epre_dBfs = srsran_convert_power_to_dB(res->epre);
res->rsrp = rsrp_avg;
res->rsrp_dBfs = srsran_convert_power_to_dB(res->rsrp);
res->snr_db = srsran_convert_power_to_dB(res->snr);
res->noise_estimate_dbFs = srsran_convert_power_to_dBm(res->noise_estimate);
}
int srsran_chest_ul_estimate_pusch(srsran_chest_ul_t* q,
@ -479,15 +493,12 @@ int srsran_chest_ul_estimate_pucch(srsran_chest_ul_t* q,
srsran_vec_prod_conj_ccc(q->pilot_recv_signal, q->pilot_known_signal, q->pilot_estimates, nrefs_sf);
}
// Measure power
float rsrp_avg = 0.0f;
for (int ns = 0; ns < SRSRAN_NOF_SLOTS_PER_SF; ns++) {
for (int i = 0; i < n_rs; i++) {
cf_t corr = srsran_vec_acc_cc(q->pilot_estimates, SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NRE * n_rs) / (SRSRAN_NRE);
rsrp_avg += __real__ corr * __real__ corr + __imag__ corr * __imag__ corr;
}
}
rsrp_avg /= SRSRAN_NOF_SLOTS_PER_SF * n_rs;
// Measure reference signal RE average power
cf_t corr = srsran_vec_acc_cc(q->pilot_estimates, SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NRE * n_rs) /
(SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NRE * n_rs);
float rsrp_avg = __real__ corr * __real__ corr + __imag__ corr * __imag__ corr;
// Measure EPRE
float epre = srsran_vec_avg_power_cf(q->pilot_estimates, SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NRE * n_rs);
// RSRP shall not be greater than EPRE
@ -562,11 +573,11 @@ int srsran_chest_ul_estimate_pucch(srsran_chest_ul_t* q,
if (fpclassify(res->noise_estimate) == FP_ZERO) {
res->noise_estimate = FLT_MIN;
}
res->noise_estimate_dbm = srsran_convert_power_to_dBm(res->noise_estimate);
res->noise_estimate_dbFs = srsran_convert_power_to_dBm(res->noise_estimate);
// Estimate SINR
if (isnormal(res->noise_estimate)) {
res->snr = res->rsrp / res->noise_estimate;
res->snr = res->epre / res->noise_estimate;
res->snr_db = srsran_convert_power_to_dB(res->snr);
} else {
res->snr = NAN;

@ -120,43 +120,58 @@ int srsran_dmrs_pucch_format1_put(const srsran_pucch_nr_t* q,
return SRSRAN_ERROR;
}
uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, 0);
if (n_pucch == 0) {
ERROR("Error getting number of symbols");
return SRSRAN_ERROR;
}
// First symbol index
uint32_t l_prime = resource->start_symbol_idx;
for (uint32_t m = 0; m < n_pucch; m++) {
// Clause 6.4.1.3.1.2 specifies l=0,2,4...
uint32_t l = m * 2;
// Get start of the sequence in resource grid
cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE];
// Get Alpha index
uint32_t alpha_idx = 0;
if (srsran_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) <
SRSRAN_SUCCESS) {
ERROR("Calculating alpha");
}
// get r_uv sequence from LUT object
const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
if (r_uv == NULL) {
ERROR("Getting r_uv sequence");
// Clause 6.4.1.3.1.2 specifies l=0,2,4...
for (uint32_t m_prime = 0, l = 0; m_prime < (resource->intra_slot_hopping ? 2 : 1); m_prime++) {
// Get number of symbols carrying DMRS
uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, m_prime);
if (n_pucch == 0) {
ERROR("Error getting number of symbols");
return SRSRAN_ERROR;
}
// Get w_i_m
cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m);
// Get the starting PRB
uint32_t starting_prb = (m_prime == 0) ? resource->starting_prb : resource->second_hop_prb;
for (uint32_t m = 0; m < n_pucch; m++, l += 2) {
// Get start of the sequence in resource grid
cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + starting_prb) * SRSRAN_NRE];
// Get Alpha index
uint32_t alpha_idx = 0;
if (srsran_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) <
SRSRAN_SUCCESS) {
ERROR("Calculating alpha");
}
// get r_uv sequence from LUT object
const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
if (r_uv == NULL) {
ERROR("Getting r_uv sequence");
return SRSRAN_ERROR;
}
// Get w_i_m
cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m);
// Compute z(n) = w(i) * r_uv(n)
cf_t z[SRSRAN_NRE];
srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE);
// Compute z(n) = w(i) * r_uv(n)
cf_t z[SRSRAN_NRE];
srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE);
if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) {
printf("[PUCCH Format 1 DMRS TX] m_prime=%d; m=%d; w_i_m=%+.3f%+.3f; z=",
m_prime,
m,
__real__ w_i_m,
__imag__ w_i_m);
srsran_vec_fprint_c(stdout, z, SRSRAN_NRE);
}
// Put z in the grid
srsran_vec_cf_copy(slot_symbols_ptr, z, SRSRAN_NRE);
// Put z in the grid
srsran_vec_cf_copy(slot_symbols_ptr, z, SRSRAN_NRE);
}
}
return SRSRAN_SUCCESS;
@ -186,50 +201,71 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q,
return SRSRAN_ERROR;
}
uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, 0);
if (n_pucch == 0) {
ERROR("Error getting number of symbols");
return SRSRAN_ERROR;
}
cf_t ce[SRSRAN_PUCCH_NR_FORMAT1_N_MAX][SRSRAN_NRE];
// Prevent ce[m] overflow
assert(n_pucch <= SRSRAN_PUCCH_NR_FORMAT1_N_MAX);
uint32_t start_rb_idx[SRSRAN_PUCCH_NR_FORMAT1_N_MAX];
uint32_t symbol_idx[SRSRAN_PUCCH_NR_FORMAT1_N_MAX];
cf_t ce[SRSRAN_PUCCH_NR_FORMAT1_N_MAX][SRSRAN_NRE];
// First symbol index
uint32_t l_prime = resource->start_symbol_idx;
for (uint32_t m = 0; m < n_pucch; m++) {
// Clause 6.4.1.3.1.2 specifies l=0,2,4...
uint32_t l = m * 2;
// Get start of the sequence in resource grid
const cf_t* slot_symbols_ptr =
&slot_symbols[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE];
// Get Alpha index
uint32_t alpha_idx = 0;
if (srsran_pucch_nr_alpha_idx(&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) <
SRSRAN_SUCCESS) {
ERROR("Calculating alpha");
}
// get r_uv sequence from LUT object
const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
if (r_uv == NULL) {
ERROR("Getting r_uv sequence");
uint32_t n_pucch_sum = 0;
for (uint32_t m_prime = 0, l = 0; m_prime < (resource->intra_slot_hopping ? 2 : 1); m_prime++) {
// Get number of symbols carrying DMRS
uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, m_prime);
if (n_pucch == 0) {
ERROR("Error getting number of symbols");
return SRSRAN_ERROR;
}
// Get w_i_m
cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m);
// Prevent ce[m] overflow
assert(n_pucch <= SRSRAN_PUCCH_NR_FORMAT1_N_MAX);
// Get the starting PRB
uint32_t starting_prb = (m_prime == 0) ? resource->starting_prb : resource->second_hop_prb;
start_rb_idx[n_pucch_sum] = starting_prb;
for (uint32_t m = 0; m < n_pucch; m++, l += 2) { // Clause 6.4.1.3.1.2 specifies l=0,2,4...
symbol_idx[n_pucch_sum] = l + l_prime;
// Compute z(n) = w(i) * r_uv(n)
cf_t z[SRSRAN_NRE];
srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE);
// Get start of the sequence in resource grid
const cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + starting_prb) * SRSRAN_NRE];
// Get Alpha index
uint32_t alpha_idx = 0;
if (srsran_pucch_nr_alpha_idx(&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) <
SRSRAN_SUCCESS) {
ERROR("Calculating alpha");
}
// get r_uv sequence from LUT object
const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
if (r_uv == NULL) {
ERROR("Getting r_uv sequence");
return SRSRAN_ERROR;
}
// TODO: can ce[m] overflow?
// Calculate least square estimates for this symbol
srsran_vec_prod_conj_ccc(slot_symbols_ptr, z, ce[m], SRSRAN_NRE);
// Get w_i_m
cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m);
// Compute z(n) = w(i) * r_uv(n)
cf_t z[SRSRAN_NRE];
srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE);
if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) {
INFO("[PUCCH Format 1 DMRS RX] m_prime=%d; m=%d; w_i_m=%+.3f%+.3f", m_prime, m, __real__ w_i_m, __imag__ w_i_m);
srsran_vec_fprint_c(stdout, z, SRSRAN_NRE);
}
// TODO: can ce[m] overflow?
// Calculate least square estimates for this symbol
srsran_vec_prod_conj_ccc(slot_symbols_ptr, z, ce[n_pucch_sum], SRSRAN_NRE);
if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) {
printf("[PUCCH Format 1 DMRS RX] ce[%d]=", n_pucch_sum);
srsran_vec_fprint_c(stdout, ce[n_pucch_sum], SRSRAN_NRE);
}
n_pucch_sum++;
}
}
// Perform measurements
@ -237,7 +273,7 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q,
float epre = 0.0f;
float ta_err = 0.0f;
cf_t corr[SRSRAN_PUCCH_NR_FORMAT1_N_MAX] = {};
for (uint32_t m = 0; m < n_pucch; m++) {
for (uint32_t m = 0; m < n_pucch_sum; m++) {
corr[m] = srsran_vec_acc_cc(ce[m], SRSRAN_NRE) / SRSRAN_NRE;
rsrp += SRSRAN_CSQABS(corr[m]);
epre += srsran_vec_avg_power_cf(ce[m], SRSRAN_NRE);
@ -245,9 +281,9 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q,
}
// Average measurements
rsrp /= n_pucch;
epre /= n_pucch;
ta_err /= n_pucch;
rsrp /= n_pucch_sum;
epre /= n_pucch_sum;
ta_err /= n_pucch_sum;
// Set power measures
rsrp = SRSRAN_MIN(rsrp, epre);
@ -256,7 +292,7 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q,
res->epre = epre;
res->epre_dBfs = srsran_convert_power_to_dB(epre);
res->noise_estimate = SRSRAN_MAX(epre - rsrp, 1e-6f);
res->noise_estimate_dbm = srsran_convert_power_to_dB(res->noise_estimate);
res->noise_estimate_dbFs = srsran_convert_power_to_dB(res->noise_estimate);
res->snr = rsrp / res->noise_estimate;
res->snr_db = srsran_convert_power_to_dB(res->snr);
@ -271,16 +307,16 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q,
}
// Measure CFO
if (n_pucch > 1) {
if (n_pucch_sum > 1) {
float cfo_avg_hz = 0.0f;
for (uint32_t m = 0; m < n_pucch - 1; m++) {
for (uint32_t m = 0; m < n_pucch_sum - 1; m++) {
uint32_t l0 = resource->start_symbol_idx + m * 2;
uint32_t l1 = resource->start_symbol_idx + (m + 1) * 2;
float time_diff = srsran_symbol_distance_s(l0, l1, q->carrier.scs);
float phase_diff = cargf(corr[m + 1] * conjf(corr[m]));
if (isnormal(time_diff)) {
cfo_avg_hz += phase_diff / (2.0f * M_PI * time_diff * (n_pucch - 1));
cfo_avg_hz += phase_diff / (2.0f * M_PI * time_diff * (n_pucch_sum - 1));
}
}
res->cfo_hz = cfo_avg_hz;
@ -292,11 +328,10 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q,
// ... Not implemented
// Interpolates between DMRS symbols
for (uint32_t m = 0; m < n_pucch; m++) {
uint32_t l = m * 2 + 1;
cf_t* ce_ptr = &res->ce[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE];
for (uint32_t m = 0; m < n_pucch_sum; m++) {
cf_t* ce_ptr = &res->ce[m * SRSRAN_NRE];
if (m != n_pucch - 1) {
if (m != n_pucch_sum - 1) {
// If it is not the last symbol with DMRS, average between
srsran_vec_sum_ccc(ce[m], ce[m + 1], ce_ptr, SRSRAN_NRE);
srsran_vec_sc_prod_cfc(ce_ptr, 0.5f, ce_ptr, SRSRAN_NRE);
@ -306,7 +341,7 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q,
srsran_vec_sub_ccc(ce_ptr, ce[m - 1], ce_ptr, SRSRAN_NRE);
srsran_vec_sc_prod_cfc(ce_ptr, 0.5f, ce_ptr, SRSRAN_NRE);
} else {
// Simply copy the
// Simply copy the estimated channel
srsran_vec_cf_copy(ce_ptr, ce[m], SRSRAN_NRE);
}
}
@ -432,7 +467,7 @@ int srsran_dmrs_pucch_format2_estimate(const srsran_pucch_nr_t* q,
res->epre = epre;
res->epre_dBfs = srsran_convert_power_to_dB(epre);
res->noise_estimate = SRSRAN_MAX(epre - rsrp, 1e-6f);
res->noise_estimate_dbm = srsran_convert_power_to_dB(res->noise_estimate);
res->noise_estimate_dbFs = srsran_convert_power_to_dB(res->noise_estimate);
res->snr = rsrp / res->noise_estimate;
res->snr_db = srsran_convert_power_to_dB(res->snr);

@ -159,12 +159,12 @@ int srs_test_context_run(srs_test_context_t* q)
INFO("RESULTS: tti=%d; snr_db=%+.1f; noise_estimate_dbm=%+.1f; ta_us=%+.1f;",
ul_sf_cfg.tti,
q->chest_ul_res.snr_db,
q->chest_ul_res.noise_estimate_dbm,
q->chest_ul_res.noise_estimate_dbFs,
q->chest_ul_res.ta_us);
// Assert SRS measurements
TESTASSERT(fabsf(q->chest_ul_res.snr_db - snr_db) < CHEST_TEST_SRS_SNR_DB_TOLERANCE);
TESTASSERT(fabsf(q->chest_ul_res.noise_estimate_dbm - n0_dbm) < CHEST_TEST_SRS_SNR_DB_TOLERANCE);
TESTASSERT(fabsf(q->chest_ul_res.noise_estimate_dbFs - n0_dbm) < CHEST_TEST_SRS_SNR_DB_TOLERANCE);
TESTASSERT(fabsf(q->chest_ul_res.ta_us) < CHEST_TEST_SRS_TA_US_TOLERANCE);
return SRSRAN_SUCCESS;

@ -37,3 +37,12 @@ target_link_libraries(sliv_test srsran_phy)
add_test(sliv_test_14 sliv_test 14)
add_test(sliv_test_52 sliv_test 48)
add_test(sliv_test_52 sliv_test 52)
########################################################################
# PHY COMMON TEST
########################################################################
add_executable(phy_common_test phy_common_test.c)
target_link_libraries(phy_common_test srsran_phy)
add_test(phy_common_test phy_common_test)

@ -0,0 +1,50 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsran/common/test_common.h"
#include "srsran/phy/common/phy_common.h"
int srsran_default_rates_test()
{
// Verify calculated sample rates for all valid PRB sizes.
// By default we use the reduced 3/4 sampling to save bandwidth on the fronthaul.
#ifdef FORCE_STANDARD_RATE
srsran_use_standard_symbol_size(false);
#endif
TESTASSERT(srsran_sampling_freq_hz(6) == 1920000);
TESTASSERT(srsran_sampling_freq_hz(15) == 3840000);
TESTASSERT(srsran_sampling_freq_hz(25) == 5760000);
TESTASSERT(srsran_sampling_freq_hz(50) == 11520000);
TESTASSERT(srsran_sampling_freq_hz(75) == 15360000); // need to use default rate for 15 MHz BW
TESTASSERT(srsran_sampling_freq_hz(100) == 23040000);
return SRSRAN_SUCCESS;
}
int lte_standard_rates_test()
{
// Verify calculated sample rates for all valid PRB sizes.
// Enable standard LTE rates (required by some RF HW).
srsran_use_standard_symbol_size(true);
TESTASSERT(srsran_sampling_freq_hz(6) == 1920000);
TESTASSERT(srsran_sampling_freq_hz(15) == 3840000);
TESTASSERT(srsran_sampling_freq_hz(25) == 7680000);
TESTASSERT(srsran_sampling_freq_hz(50) == 15360000);
TESTASSERT(srsran_sampling_freq_hz(75) == 23040000);
TESTASSERT(srsran_sampling_freq_hz(100) == 30720000);
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
TESTASSERT(srsran_default_rates_test() == SRSRAN_SUCCESS);
TESTASSERT(lte_standard_rates_test() == SRSRAN_SUCCESS);
return SRSRAN_SUCCESS;
}

@ -711,7 +711,8 @@ int srsran_ofdm_set_cfr(srsran_ofdm_t* q, srsran_cfr_cfg_t* cfr)
q->cfg.cfr_tx_cfg.symbol_sz = q->cfg.symbol_sz;
q->cfg.cfr_tx_cfg.symbol_bw = q->nof_re;
// in the DL, the DC carrier is empty but still counts when designing the filter BW
// in the LTE DL, the DC carrier is empty but still counts when designing the filter BW
// in the LTE UL, the DC carrier is used
q->cfg.cfr_tx_cfg.dc_sc = (!q->cfg.keep_dc) && (!isnormal(q->cfg.freq_shift_f));
if (q->cfg.cfr_tx_cfg.cfr_enable) {
if (srsran_cfr_init(&q->tx_cfr, &q->cfg.cfr_tx_cfg) < SRSRAN_SUCCESS) {

@ -193,7 +193,9 @@ static int get_pucch(srsran_enb_ul_t* q, srsran_ul_sf_cfg_t* ul_sf, srsran_pucch
ERROR("Error estimating PUCCH DMRS");
return SRSRAN_ERROR;
}
pucch_res.snr_db = q->chest_res.snr_db;
pucch_res.snr_db = q->chest_res.snr_db;
pucch_res.rssi_dbFs = q->chest_res.epre_dBfs;
pucch_res.ni_dbFs = q->chest_res.noise_estimate_dbFs;
ret = srsran_pucch_decode(&q->pucch, ul_sf, cfg, &q->chest_res, q->sf_symbols, &pucch_res);
if (ret < SRSRAN_SUCCESS) {

@ -317,7 +317,7 @@ int srsran_gnb_ul_get_pucch(srsran_gnb_ul_t* q,
meas->epre = q->chest_pucch.epre;
meas->epre_dB = q->chest_pucch.epre_dBfs;
meas->n0 = q->chest_pucch.noise_estimate;
meas->n0_dB = q->chest_pucch.noise_estimate_dbm;
meas->n0_dB = q->chest_pucch.noise_estimate_dbFs;
meas->snr_dB = q->chest_pucch.snr_db;
meas->cfo_hz = q->chest_pucch.cfo_hz;
meas->cfo_hz_max = NAN; // Unavailable

@ -88,6 +88,7 @@ static int harq_ack_gen_ack_type2(const srsran_harq_ack_cfg_hl_t* cfg,
if (ack->present) {
// Load ACK resource data into UCI info
uci_cfg->pucch.resource_id = ack_info->cc[c].m[m].resource.pucch_resource_id;
uci_cfg->pucch.n_cce_0 = ack_info->cc[c].m[m].resource.n_cce;
uci_cfg->pucch.rnti = ack_info->cc[c].m[m].resource.rnti;
if (V_DL_CDAI <= V_temp) {
@ -183,6 +184,7 @@ int srsran_harq_ack_resource(const srsran_harq_ack_cfg_hl_t* cfg,
pdsch_ack_resource->v_dai_dl = dci_dl->dai;
pdsch_ack_resource->rnti = dci_dl->ctx.rnti;
pdsch_ack_resource->pucch_resource_id = dci_dl->pucch_resource;
pdsch_ack_resource->n_cce = dci_dl->ctx.location.ncce;
pdsch_ack_resource->pid = dci_dl->pid;
return SRSRAN_SUCCESS;

@ -169,7 +169,8 @@ int srsran_pucch_nr_cfg_resource_valid(const srsran_pucch_nr_resource_t* resourc
return SRSRAN_ERROR;
}
if (resource->intra_slot_hopping) {
// Frequency hopping is only possible with Format 1
if (resource->intra_slot_hopping && resource->format != SRSRAN_PUCCH_NR_FORMAT_1) {
ERROR("Intra-slot hopping is not implemented");
return SRSRAN_ERROR;
}

@ -406,6 +406,8 @@ int srsran_pucch_nr_format1_encode(const srsran_pucch_nr_t* q,
srsran_mod_modulate(&q->qpsk, b, d, 2);
}
INFO("[PUCCH Format 1 Data TX] d=%+.3f%+.3f", __real__ d[0], __imag__ d[0]);
// Get group sequence
uint32_t u = 0;
uint32_t v = 0;
@ -414,41 +416,60 @@ int srsran_pucch_nr_format1_encode(const srsran_pucch_nr_t* q,
return SRSRAN_ERROR;
}
// Calculate number of symbols carrying PUCCH (No DMRS)
uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, 0);
// First symbol of this PUCCH transmission
uint32_t l_prime = resource->start_symbol_idx;
for (uint32_t l = 1, m = 0; l < resource->nof_symbols; l += 2, m++) {
// Get start of the sequence in resource grid
cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE];
// Get Alpha index
uint32_t alpha_idx = 0;
if (srsran_pucch_nr_alpha_idx(&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) <
SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// For each hop
for (uint32_t m_prime = 0, l = 1; m_prime < (resource->intra_slot_hopping ? 2 : 1); m_prime++) {
// Calculate number of symbols carrying PUCCH (No DMRS)
uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, m_prime);
// get r_uv sequence from LUT object
const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
if (r_uv == NULL) {
ERROR("Getting r_uv sequence");
return SRSRAN_ERROR;
}
// Get the starting PRB
uint32_t starting_prb = (m_prime == 0) ? resource->starting_prb : resource->second_hop_prb;
// Compute y = d(0) * r_uv
cf_t y[SRSRAN_NRE];
srsran_vec_sc_prod_ccc(r_uv, d[0], y, SRSRAN_NRE);
// For each symbol carrying PUCCH data
for (uint32_t m = 0; m < n_pucch; m++, l += 2) {
// Get start of the sequence in resource grid
cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + starting_prb) * SRSRAN_NRE];
// Get w_i_m
cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m);
// Get Alpha index
uint32_t alpha_idx = 0;
if (srsran_pucch_nr_alpha_idx(&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) <
SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// get r_uv sequence from LUT object
const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
if (r_uv == NULL) {
ERROR("Getting r_uv sequence");
return SRSRAN_ERROR;
}
// Get w_i_m
cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m);
// Compute z(n) = w(i) * y(n)
cf_t z[SRSRAN_NRE];
srsran_vec_sc_prod_ccc(y, w_i_m, z, SRSRAN_NRE);
// Compute z(n) = w(i) * r_uv(n)
cf_t z[SRSRAN_NRE];
srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE);
// Put z in the grid
srsran_vec_cf_copy(slot_symbols_ptr, z, SRSRAN_NRE);
if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) {
printf("[PUCCH Format 1 Data TX] m_prime=%d; m=%d; w_i_m=%+.3f%+.3f z=",
m_prime,
m,
__real__ w_i_m,
__imag__ w_i_m);
srsran_vec_fprint_c(stdout, z, SRSRAN_NRE);
}
// Put z in the grid
srsran_vec_sc_prod_ccc(z, d[0], slot_symbols_ptr, SRSRAN_NRE);
if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) {
printf("[PUCCH Format 1 TX] l=%d; x=", l + l_prime);
srsran_vec_fprint_c(stdout, slot_symbols_ptr, SRSRAN_NRE);
}
}
}
return SRSRAN_SUCCESS;
@ -493,46 +514,79 @@ int srsran_pucch_nr_format1_decode(srsran_pucch_nr_t* q,
return SRSRAN_ERROR;
}
// Calculate number of symbols carrying PUCCH (No DMRS)
uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, 0);
// First symbol of this PUCCH transmission
uint32_t l_prime = resource->start_symbol_idx;
for (uint32_t l = 1, m = 0; l < resource->nof_symbols; l += 2, m++) {
// Get start of the sequence in resource grid
cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE];
cf_t* ce_ptr = &chest_res->ce[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE];
// Equalise x = w(i) * d' * r_uv(n)
cf_t x[SRSRAN_NRE];
srsran_predecoding_single(slot_symbols_ptr, ce_ptr, x, NULL, SRSRAN_NRE, 1.0f, chest_res->noise_estimate);
// For each hop
uint32_t n_pucch_sum = 0;
for (uint32_t m_prime = 0, l = 1; m_prime < (resource->intra_slot_hopping ? 2 : 1); m_prime++) {
// Calculate number of symbols carrying PUCCH (No DMRS)
uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, m_prime);
// Get the starting PRB
uint32_t starting_prb = (m_prime == 0) ? resource->starting_prb : resource->second_hop_prb;
// For each symbol carrying PUCCH data
for (uint32_t m = 0; m < n_pucch; m++, l += 2) {
// Get start of the sequence in resource grid
cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + starting_prb) * SRSRAN_NRE];
cf_t* ce_ptr = &chest_res->ce[SRSRAN_NRE * n_pucch_sum];
n_pucch_sum++;
if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) {
printf("[PUCCH Format 1 CE RX] ce=");
srsran_vec_fprint_c(stdout, ce_ptr, SRSRAN_NRE);
}
// Get Alpha index
uint32_t alpha_idx = 0;
if (srsran_pucch_nr_alpha_idx(
&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// Equalise x = w(i) * d' * r_uv(n)
cf_t x[SRSRAN_NRE];
srsran_predecoding_single(slot_symbols_ptr, ce_ptr, x, NULL, SRSRAN_NRE, 1.0f, chest_res->noise_estimate);
// get r_uv sequence from LUT object
const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
if (r_uv == NULL) {
ERROR("Getting r_uv sequence");
return SRSRAN_ERROR;
}
// Get w_i_m
cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m);
if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) {
printf("[PUCCH Format 1 RX] l=%d; x=", l + l_prime);
srsran_vec_fprint_c(stdout, x, SRSRAN_NRE);
}
// Compute z(n) = w(i) * r_uv(n)
cf_t z[SRSRAN_NRE];
srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE);
// Get Alpha index
uint32_t alpha_idx = 0;
if (srsran_pucch_nr_alpha_idx(
&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// get r_uv sequence from LUT object
const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
if (r_uv == NULL) {
ERROR("Getting r_uv sequence");
return SRSRAN_ERROR;
}
// Compute d = sum(x * conj(w(i) * r_uv(n))) = sum(w(i) * d' * r_uv(n) * conj(w(i) * r_uv(n))) = d'
d += srsran_vec_dot_prod_conj_ccc(x, z, SRSRAN_NRE) / SRSRAN_NRE;
// Get w_i_m
cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m);
// Compute and accumulate average symbol power
pwr_acc += srsran_vec_avg_power_cf(x, SRSRAN_NRE);
// Compute z(n) = w(i) * r_uv(n)
cf_t z[SRSRAN_NRE];
srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE);
if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) {
printf("[PUCCH Format 1 Data RX] m_prime=%d; m=%d; w_i_m=%+.3f%+.3f z=",
m_prime,
m,
__real__ w_i_m,
__imag__ w_i_m);
srsran_vec_fprint_c(stdout, z, SRSRAN_NRE);
}
// Compute d = sum(x * conj(w(i) * r_uv(n))) = sum(w(i) * d' * r_uv(n) * conj(w(i) * r_uv(n))) = d'
d += srsran_vec_dot_prod_conj_ccc(x, z, SRSRAN_NRE) / SRSRAN_NRE;
// Compute and accumulate average symbol power
pwr_acc += srsran_vec_avg_power_cf(x, SRSRAN_NRE);
}
}
INFO("[PUCCH Format 1 Data RX] d=%+.3f%+.3f", __real__ d, __imag__ d);
// Demodulate d
float llr[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS];
srsran_demod_soft_demodulate((nof_bits == 1) ? SRSRAN_MOD_BPSK : SRSRAN_MOD_QPSK, &d, llr, 1);

@ -479,11 +479,89 @@ int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier,
return SRSRAN_ERROR;
}
typedef struct {
srsran_pucch_nr_format_t format;
uint32_t start_symbol;
uint32_t rb_offset;
uint32_t N_cs;
} pucch_res_common_cfg_t;
// Table 9.2.1-1
#define PUCCH_RES_COMMON_CFG_IDX_MAX 15
static pucch_res_common_cfg_t pucch_res_common_cfg[PUCCH_RES_COMMON_CFG_IDX_MAX + 1] = {
{SRSRAN_PUCCH_NR_FORMAT_0, 12, 0, 2},
{SRSRAN_PUCCH_NR_FORMAT_0, 12, 0, 3},
{SRSRAN_PUCCH_NR_FORMAT_0, 12, 3, 3},
{SRSRAN_PUCCH_NR_FORMAT_1, 10, 0, 2},
{SRSRAN_PUCCH_NR_FORMAT_1, 10, 0, 3},
{SRSRAN_PUCCH_NR_FORMAT_1, 10, 2, 3},
{SRSRAN_PUCCH_NR_FORMAT_1, 10, 4, 3},
{SRSRAN_PUCCH_NR_FORMAT_1, 4, 0, 2},
{SRSRAN_PUCCH_NR_FORMAT_1, 4, 0, 3},
{SRSRAN_PUCCH_NR_FORMAT_1, 4, 2, 3},
{SRSRAN_PUCCH_NR_FORMAT_1, 4, 4, 3},
{SRSRAN_PUCCH_NR_FORMAT_1, 0, 0, 2},
{SRSRAN_PUCCH_NR_FORMAT_1, 0, 0, 4},
{SRSRAN_PUCCH_NR_FORMAT_1, 0, 2, 4},
{SRSRAN_PUCCH_NR_FORMAT_1, 0, 4, 4},
{SRSRAN_PUCCH_NR_FORMAT_1, 0, 0, 4}};
// Implements TS 38.213 Table 9.2.1-1: PUCCH resource sets before dedicated PUCCH resource configuration
static int ra_ul_nr_pucch_resource_default(uint32_t r_pucch, srsran_pucch_nr_resource_t* resource)
static int ra_ul_nr_pucch_resource_default(uint32_t pucch_res_common_idx,
uint32_t N_size_bwp,
uint32_t r_pucch,
srsran_pucch_nr_resource_t* resource)
{
ERROR("Not implemented");
return SRSRAN_ERROR;
if (pucch_res_common_idx > PUCCH_RES_COMMON_CFG_IDX_MAX) {
ERROR("Invalid pucch_res_common_idx value (%d)\n", pucch_res_common_idx);
return SRSRAN_ERROR;
}
uint32_t cs_v_2[2] = {0, 6};
uint32_t cs_v_3[3] = {0, 4, 8};
uint32_t cs_v_4[4] = {0, 3, 6, 9};
// Table 9.2.1-1
resource->format = pucch_res_common_cfg[pucch_res_common_idx].format;
resource->start_symbol_idx = pucch_res_common_cfg[pucch_res_common_idx].start_symbol;
resource->nof_symbols = 14 - resource->start_symbol_idx;
uint32_t rb_offset_bwp = pucch_res_common_cfg[pucch_res_common_idx].rb_offset;
uint32_t N_cs = pucch_res_common_cfg[pucch_res_common_idx].N_cs;
// Special case for cs_v_2 value
if (pucch_res_common_idx == 0) {
cs_v_2[1] = 3;
}
// Special case for rb_offset
if (pucch_res_common_idx == 15) {
rb_offset_bwp = N_size_bwp / 4;
}
uint32_t csv_idx = 0;
if (r_pucch / 8 == 0) {
resource->starting_prb = rb_offset_bwp + SRSRAN_FLOOR(r_pucch, N_cs);
resource->second_hop_prb = N_size_bwp - 1 - resource->starting_prb;
csv_idx = r_pucch % N_cs;
} else {
resource->second_hop_prb = rb_offset_bwp + SRSRAN_FLOOR(r_pucch - 8, N_cs);
resource->starting_prb = N_size_bwp - 1 - resource->second_hop_prb;
csv_idx = (r_pucch - 8) % N_cs;
}
switch (N_cs) {
case 2:
resource->initial_cyclic_shift = cs_v_2[csv_idx];
break;
case 3:
resource->initial_cyclic_shift = cs_v_3[csv_idx];
break;
case 4:
resource->initial_cyclic_shift = cs_v_4[csv_idx];
break;
}
return SRSRAN_SUCCESS;
}
static int ra_ul_nr_pucch_resource_hl(const srsran_pucch_nr_hl_cfg_t* cfg,
@ -531,6 +609,7 @@ static int ra_ul_nr_pucch_resource_hl(const srsran_pucch_nr_hl_cfg_t* cfg,
int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg,
const srsran_uci_cfg_nr_t* uci_cfg,
uint32_t N_bwp_sz,
srsran_pucch_nr_resource_t* resource)
{
if (pucch_cfg == NULL || uci_cfg == NULL || resource == NULL) {
@ -632,8 +711,9 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg,
// a PUCCH resource set is provided by pucch-ResourceCommon through an index to a row of Table 9.2.1-1 for size
// transmission of HARQ-ACK information on PUCCH in an initial UL BWP of N BWP PRBs.
if (!pucch_cfg->enabled) {
uint32_t r_pucch = (2 * uci_cfg->pucch.n_cce_0) + 2 * uci_cfg->pucch.resource_id;
return ra_ul_nr_pucch_resource_default(r_pucch, resource);
uint32_t N_cce = SRSRAN_FLOOR(N_bwp_sz, 6);
uint32_t r_pucch = ((2 * uci_cfg->pucch.n_cce_0) / N_cce) + 2 * uci_cfg->pucch.resource_id;
return ra_ul_nr_pucch_resource_default(pucch_cfg->common.resource_common, N_bwp_sz, r_pucch, resource);
}
return ra_ul_nr_pucch_resource_hl(pucch_cfg, uci_cfg, uci_cfg->pucch.resource_id, resource);
}

@ -57,15 +57,15 @@ add_executable(psbch_file_test psbch_file_test.c)
target_link_libraries(psbch_file_test srsran_phy)
# TM2 file tests
add_lte_test(psbch_file_test_ideal_tm2_p6_c0 psbch_file_test -p 6 -c 0 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p6_c0_s1.92e6.dat)
add_lte_test(psbch_file_test_ideal_tm2_p15_c84 psbch_file_test -p 15 -c 84 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p15_c84_s3.84e6.dat)
add_lte_test(psbch_file_test_ideal_tm2_p25_c168 psbch_file_test -p 25 -c 168 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat)
add_lte_test(psbch_file_test_ideal_tm2_p50_c252 psbch_file_test -p 50 -c 252 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p50_c252_s15.36e6.dat)
add_lte_test(psbch_file_test_ideal_tm2_p100_c335 psbch_file_test -p 100 -c 335 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat)
add_lte_test(psbch_file_test_ideal_tm2_p50_c252_ext psbch_file_test -p 50 -c 252 -e -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p50_c252_s15.36e6_ext.dat)
add_lte_test(psbch_file_test_ideal_tm2_p6_c0 psbch_file_test -p 6 -c 0 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p6_c0_s1.92e6.dat)
add_lte_test(psbch_file_test_ideal_tm2_p15_c84 psbch_file_test -p 15 -c 84 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p15_c84_s3.84e6.dat)
add_lte_test(psbch_file_test_ideal_tm2_p25_c168 psbch_file_test -p 25 -c 168 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat)
add_lte_test(psbch_file_test_ideal_tm2_p50_c252 psbch_file_test -p 50 -c 252 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p50_c252_s15.36e6.dat)
add_lte_test(psbch_file_test_ideal_tm2_p100_c335 psbch_file_test -p 100 -c 335 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat)
add_lte_test(psbch_file_test_ideal_tm2_p50_c252_ext psbch_file_test -p 50 -c 252 -e -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p50_c252_s15.36e6_ext.dat)
# TM4 file tests
add_lte_test(psbch_file_test_cmw_tm4_p50_c169 psbch_file_test -p 50 -c 169 -t 4 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_cmw500_f5.92e9_s11.52e6_50prb_slss_id169.dat)
add_lte_test(psbch_file_test_cmw_tm4_p50_c169 psbch_file_test -p 50 -c 169 -t 4 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_cmw500_f5.92e9_s11.52e6_50prb_slss_id169.dat)
########################################################################
# PSCCH TEST
@ -123,38 +123,38 @@ add_executable(pssch_pscch_file_test pssch_pscch_file_test.c)
target_link_libraries(pssch_pscch_file_test srsran_phy)
# TM2 file tests
add_lte_test(pssch_pscch_file_test_ideal_tm2_p100 pssch_pscch_file_test -p 100 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat)
add_lte_test(pssch_pscch_file_test_ideal_tm2_p100 pssch_pscch_file_test -p 100 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat)
set_property(TEST pssch_pscch_file_test_ideal_tm2_p100 PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=[2,3] num_decoded_tb=1")
# TM4 file tests (first SF is sf_idx = 6 such that the PSSCH sf_idx=0)
add_lte_test(pssch_pscch_file_test_ideal_tm4_p100 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -d -m 6 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat)
add_lte_test(pssch_pscch_file_test_ideal_tm4_p100 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -d -m 6 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat)
set_property(TEST pssch_pscch_file_test_ideal_tm4_p100 PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=1")
add_lte_test(pssch_pscch_test_tm4_p50_qc pssch_pscch_file_test -p 50 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_qc9150_f5.92e9_s15.36e6_50prb_20offset.dat)
add_lte_test(pssch_pscch_test_tm4_p50_qc pssch_pscch_file_test -p 50 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_qc9150_f5.92e9_s15.36e6_50prb_20offset.dat)
set_property(TEST pssch_pscch_test_tm4_p50_qc PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=1 num_decoded_tb=1")
# Capture has a SFO offset of ~64 samples, but offsetting by 20 is enough to decode it
add_lte_test(pssch_pscch_test_tm4_p50_cmw pssch_pscch_file_test -p 50 -t 4 -o 20 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_cmw500_f5.92e9_s11.52e6_50prb_0offset_1ms.dat)
add_lte_test(pssch_pscch_test_tm4_p50_cmw pssch_pscch_file_test -p 50 -t 4 -o 20 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_cmw500_f5.92e9_s11.52e6_50prb_0offset_1ms.dat)
set_property(TEST pssch_pscch_test_tm4_p50_cmw PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=1 num_decoded_tb=1")
# With PHY retransmission (3 TTI offset) first SF at sf_idx=5
add_lte_test(pssch_pscch_test_tm4_p50_huawei pssch_pscch_file_test -p 50 -t 4 -m 5 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_huawei_s11.52e6_50prb_10prb_offset_with_retx.dat)
add_lte_test(pssch_pscch_test_tm4_p50_huawei pssch_pscch_file_test -p 50 -t 4 -m 5 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_huawei_s11.52e6_50prb_10prb_offset_with_retx.dat)
set_property(TEST pssch_pscch_test_tm4_p50_huawei PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=2 num_decoded_tb=2")
# With PHY ReTx (0 TTI offset?)
add_lte_test(pssch_pscch_test_tm4_p50_uxm1 pssch_pscch_file_test -p 50 -d -t 4 -s 5 -n 10 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_uxm_s15.36e6_50prb_0prb_offset_mcs12.dat)
add_lte_test(pssch_pscch_test_tm4_p50_uxm1 pssch_pscch_file_test -p 50 -d -t 4 -s 5 -n 10 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_uxm_s15.36e6_50prb_0prb_offset_mcs12.dat)
set_property(TEST pssch_pscch_test_tm4_p50_uxm1 PROPERTY PASS_REGULAR_EXPRESSION "mcs=12.*num_decoded_sci=2 num_decoded_tb=2")
# 100 PRB startOffset 1 MCS12 MAC padding, first SF is index 0
add_lte_test(pssch_pscch_test_tm4_p100_uxm2 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_uxm_s23.04e6_100prb_1prb_offset_mcs12_padding.dat)
add_lte_test(pssch_pscch_test_tm4_p100_uxm2 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_uxm_s23.04e6_100prb_1prb_offset_mcs12_padding.dat)
set_property(TEST pssch_pscch_test_tm4_p100_uxm2 PROPERTY PASS_REGULAR_EXPRESSION "mcs=12.*num_decoded_sci=4")
# 100 PRB LTE sampling rate, startOffset1 MCS12 ITS data, first SF is index 6
add_lte_test(pssch_pscch_test_tm4_p100_uxm3 pssch_pscch_file_test -p 100 -d -t 4 -s 10 -n 10 -m 6 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_uxm_s30.72e6_100prb_1prb_offset_mcs12_its.dat)
add_lte_test(pssch_pscch_test_tm4_p100_uxm3 pssch_pscch_file_test -p 100 -d -t 4 -s 10 -n 10 -m 6 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_uxm_s30.72e6_100prb_1prb_offset_mcs12_its.dat)
set_property(TEST pssch_pscch_test_tm4_p100_uxm3 PROPERTY PASS_REGULAR_EXPRESSION "mcs=12.*num_decoded_sci=1")
# 50 PRB LTE sampling rate, startOffset0 MCS28 MAC padding, first SF is index 1
add_lte_test(pssch_pscch_test_tm4_p50_uxm4 pssch_pscch_file_test -p 50 -d -t 4 -s 5 -n 10 -m 1 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_uxm_s15.36e6_50prb_0prb_offset_mcs28_padding_5ms.dat)
add_lte_test(pssch_pscch_test_tm4_p50_uxm4 pssch_pscch_file_test -p 50 -d -t 4 -s 5 -n 10 -m 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_uxm_s15.36e6_50prb_0prb_offset_mcs28_padding_5ms.dat)
set_property(TEST pssch_pscch_test_tm4_p50_uxm4 PROPERTY PASS_REGULAR_EXPRESSION "mcs=28.*num_decoded_sci=5")
########################################################################

@ -88,63 +88,71 @@ static int test_pucch_format0(srsran_pucch_nr_t* pucch, const srsran_pucch_nr_co
static int test_pucch_format1(srsran_pucch_nr_t* pucch,
const srsran_pucch_nr_common_cfg_t* cfg,
srsran_chest_ul_res_t* chest_res,
cf_t* slot_symbols)
cf_t* slot_symbols,
bool enable_intra_slot_hopping)
{
srsran_slot_cfg_t slot = {};
srsran_pucch_nr_resource_t resource = {};
resource.format = SRSRAN_PUCCH_NR_FORMAT_1;
resource.intra_slot_hopping = enable_intra_slot_hopping;
for (slot.idx = 0; slot.idx < SRSRAN_NSLOTS_PER_FRAME_NR(carrier.scs); slot.idx++) {
for (resource.starting_prb = 0; resource.starting_prb < carrier.nof_prb;
resource.starting_prb += starting_prb_stride) {
for (resource.nof_symbols = SRSRAN_PUCCH_NR_FORMAT1_MIN_NSYMB;
resource.nof_symbols <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NSYMB;
resource.nof_symbols++) {
for (resource.start_symbol_idx = 0;
resource.start_symbol_idx <=
SRSRAN_MIN(SRSRAN_PUCCH_NR_FORMAT1_MAX_STARTSYMB, SRSRAN_NSYMB_PER_SLOT_NR - resource.nof_symbols);
resource.start_symbol_idx += starting_symbol_stride) {
for (resource.time_domain_occ = 0; resource.time_domain_occ <= SRSRAN_PUCCH_NR_FORMAT1_MAX_TOCC;
resource.time_domain_occ++) {
for (resource.initial_cyclic_shift = 0; resource.initial_cyclic_shift <= SRSRAN_PUCCH_NR_FORMAT1_MAX_CS;
resource.initial_cyclic_shift++) {
for (uint32_t nof_bits = 1; nof_bits <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS; nof_bits++) {
for (uint32_t word = 0; word < (1U << nof_bits); word++) {
// Generate bits
uint8_t b[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS] = {};
for (uint32_t i = 0; i < nof_bits; i++) {
b[i] = (word >> i) & 1U;
}
// Encode PUCCH
TESTASSERT(srsran_pucch_nr_format1_encode(pucch, cfg, &slot, &resource, b, nof_bits, slot_symbols) ==
SRSRAN_SUCCESS);
// Put DMRS
TESTASSERT(srsran_dmrs_pucch_format1_put(pucch, &carrier, cfg, &slot, &resource, slot_symbols) ==
SRSRAN_SUCCESS);
// Apply AWGN
srsran_channel_awgn_run_c(
&awgn, slot_symbols, slot_symbols, carrier.nof_prb * SRSRAN_NRE * SRSRAN_NSYMB_PER_SLOT_NR);
// Estimate channel
TESTASSERT(srsran_dmrs_pucch_format1_estimate(
pucch, cfg, &slot, &resource, slot_symbols, chest_res) == SRSRAN_SUCCESS);
TESTASSERT(fabsf(chest_res->rsrp_dBfs - 0.0f) < 3.0f);
TESTASSERT(fabsf(chest_res->epre_dBfs - 0.0f) < 3.0f);
TESTASSERT(fabsf(chest_res->snr_db - snr_db) < 10.0f);
// Decode PUCCH
uint8_t b_rx[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS];
TESTASSERT(srsran_pucch_nr_format1_decode(
pucch, cfg, &slot, &resource, chest_res, slot_symbols, b_rx, nof_bits, NULL) ==
SRSRAN_SUCCESS);
// Check received bits
for (uint32_t i = 0; i < nof_bits; i++) {
TESTASSERT(b[i] == b_rx[i]);
for (resource.second_hop_prb = 0; resource.second_hop_prb < (enable_intra_slot_hopping) ? carrier.nof_prb : 0;
resource.second_hop_prb += starting_prb_stride) {
for (resource.nof_symbols = SRSRAN_PUCCH_NR_FORMAT1_MIN_NSYMB;
resource.nof_symbols <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NSYMB;
resource.nof_symbols++) {
for (resource.start_symbol_idx = 0;
resource.start_symbol_idx <=
SRSRAN_MIN(SRSRAN_PUCCH_NR_FORMAT1_MAX_STARTSYMB, SRSRAN_NSYMB_PER_SLOT_NR - resource.nof_symbols);
resource.start_symbol_idx += starting_symbol_stride) {
for (resource.time_domain_occ = 0; resource.time_domain_occ <= SRSRAN_PUCCH_NR_FORMAT1_MAX_TOCC;
resource.time_domain_occ++) {
for (resource.initial_cyclic_shift = 0; resource.initial_cyclic_shift <= SRSRAN_PUCCH_NR_FORMAT1_MAX_CS;
resource.initial_cyclic_shift++) {
for (uint32_t nof_bits = 1; nof_bits <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS; nof_bits++) {
for (uint32_t word = 0; word < (1U << nof_bits); word++) {
// Generate bits
uint8_t b[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS] = {};
for (uint32_t i = 0; i < nof_bits; i++) {
b[i] = (word >> i) & 1U;
}
// Encode PUCCH
TESTASSERT(srsran_pucch_nr_format1_encode(
pucch, cfg, &slot, &resource, b, nof_bits, slot_symbols) == SRSRAN_SUCCESS);
// Put DMRS
TESTASSERT(srsran_dmrs_pucch_format1_put(pucch, &carrier, cfg, &slot, &resource, slot_symbols) ==
SRSRAN_SUCCESS);
// Apply AWGN
srsran_channel_awgn_run_c(
&awgn, slot_symbols, slot_symbols, carrier.nof_prb * SRSRAN_NRE * SRSRAN_NSYMB_PER_SLOT_NR);
// Estimate channel
TESTASSERT(srsran_dmrs_pucch_format1_estimate(
pucch, cfg, &slot, &resource, slot_symbols, chest_res) == SRSRAN_SUCCESS);
TESTASSERT(fabsf(chest_res->rsrp_dBfs - 0.0f) < 3.0f);
TESTASSERT(fabsf(chest_res->epre_dBfs - 0.0f) < 3.0f);
TESTASSERT(fabsf(chest_res->snr_db - snr_db) < 10.0f);
// Decode PUCCH
uint8_t b_rx[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS];
TESTASSERT(srsran_pucch_nr_format1_decode(
pucch, cfg, &slot, &resource, chest_res, slot_symbols, b_rx, nof_bits, NULL) ==
SRSRAN_SUCCESS);
// Check received bits
for (uint32_t i = 0; i < nof_bits; i++) {
if (b[i] != b_rx[i]) {
printf("aaa");
}
TESTASSERT(b[i] == b_rx[i]);
}
}
}
}
@ -345,9 +353,13 @@ int main(int argc, char** argv)
}
}
// Test Format 1
// Test Format 1 with and without intra slot frequency hopping
if (format < 0 || format == 1) {
if (test_pucch_format1(&pucch, &common_cfg, &chest_res, slot_symb) < SRSRAN_SUCCESS) {
if (test_pucch_format1(&pucch, &common_cfg, &chest_res, slot_symb, false) < SRSRAN_SUCCESS) {
ERROR("Failed PUCCH format 1");
goto clean_exit;
}
if (test_pucch_format1(&pucch, &common_cfg, &chest_res, slot_symb, true) < SRSRAN_SUCCESS) {
ERROR("Failed PUCCH format 1");
goto clean_exit;
}

@ -23,13 +23,32 @@ if(RF_FOUND)
add_library(srsran_rf_utils STATIC rf_utils.c)
target_link_libraries(srsran_rf_utils srsran_phy)
# Include common RF files
# Top-level RF library sources
set(SOURCES_RF "")
list(APPEND SOURCES_RF rf_imp.c)
if (UHD_FOUND)
# Lists of static (builtin) and dynamic RF plugins
set(STATIC_PLUGINS "")
set(DYNAMIC_PLUGINS "")
if (ENABLE_RF_PLUGINS)
add_definitions(-DENABLE_RF_PLUGINS)
endif (ENABLE_RF_PLUGINS)
# RF plugins
if (UHD_FOUND AND ENABLE_UHD)
add_definitions(-DENABLE_UHD)
list(APPEND SOURCES_RF rf_uhd_imp.cc)
set(SOURCES_UHD rf_uhd_imp.cc)
if (ENABLE_RF_PLUGINS)
add_library(srsran_rf_uhd SHARED ${SOURCES_UHD})
set_target_properties(srsran_rf_uhd PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION})
list(APPEND DYNAMIC_PLUGINS srsran_rf_uhd)
else (ENABLE_RF_PLUGINS)
add_library(srsran_rf_uhd STATIC ${SOURCES_UHD})
list(APPEND STATIC_PLUGINS srsran_rf_uhd)
endif (ENABLE_RF_PLUGINS)
target_link_libraries(srsran_rf_uhd srsran_rf_utils srsran_phy ${UHD_LIBRARIES} ${Boost_LIBRARIES})
install(TARGETS srsran_rf_uhd DESTINATION ${LIBRARY_DIR} OPTIONAL)
# If found, add a macro to inform the UHD driver about the available feature
if (UHD_ENABLE_X300_FW_RESET)
@ -41,72 +60,117 @@ if(RF_FOUND)
if (UHD_ENABLE_CUSTOM_RFNOC)
add_definitions(-DUHD_ENABLE_CUSTOM_RFNOC)
endif(UHD_ENABLE_CUSTOM_RFNOC)
endif (UHD_FOUND)
endif (UHD_FOUND AND ENABLE_UHD)
if (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC)
add_executable(rfnoc_test rfnoc_test.cc)
target_link_libraries(rfnoc_test srsran_rf ${UHD_LIBRARIES} ${Boost_LIBRARIES} /usr/lib/x86_64-linux-gnu/libboost_system.so)
message(info ${Boost_LIBRARIES})
endif (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC)
if (BLADERF_FOUND)
if (BLADERF_FOUND AND ENABLE_BLADERF)
add_definitions(-DENABLE_BLADERF)
list(APPEND SOURCES_RF rf_blade_imp.c)
endif (BLADERF_FOUND)
set(SOURCES_BLADE rf_blade_imp.c)
if (ENABLE_RF_PLUGINS)
add_library(srsran_rf_blade SHARED ${SOURCES_BLADE})
set_target_properties(srsran_rf_blade PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION})
list(APPEND DYNAMIC_PLUGINS srsran_rf_blade)
else (ENABLE_RF_PLUGINS)
add_library(srsran_rf_blade STATIC ${SOURCES_BLADE})
list(APPEND STATIC_PLUGINS srsran_rf_blade)
endif (ENABLE_RF_PLUGINS)
target_link_libraries(srsran_rf_blade srsran_rf_utils srsran_phy ${BLADERF_LIBRARIES})
install(TARGETS srsran_rf_blade DESTINATION ${LIBRARY_DIR} OPTIONAL)
endif (BLADERF_FOUND AND ENABLE_BLADERF)
if (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR)
add_definitions(-DENABLE_SOAPYSDR)
list(APPEND SOURCES_RF rf_soapy_imp.c)
set(SOURCES_SOAPY rf_soapy_imp.c)
if (ENABLE_RF_PLUGINS)
add_library(srsran_rf_soapy SHARED ${SOURCES_SOAPY})
set_target_properties(srsran_rf_soapy PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION})
list(APPEND DYNAMIC_PLUGINS srsran_rf_soapy)
else (ENABLE_RF_PLUGINS)
add_library(srsran_rf_soapy STATIC ${SOURCES_SOAPY})
list(APPEND STATIC_PLUGINS srsran_rf_soapy)
endif (ENABLE_RF_PLUGINS)
target_link_libraries(srsran_rf_soapy srsran_rf_utils srsran_phy ${SOAPYSDR_LIBRARIES})
install(TARGETS srsran_rf_soapy DESTINATION ${LIBRARY_DIR} OPTIONAL)
endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR)
if(SKIQ_FOUND)
if(SKIQ_FOUND AND ENABLE_SKIQ)
add_executable(skiq_pps_test skiq_pps_test.c)
target_link_libraries(skiq_pps_test ${SKIQ_LIBRARIES} rt pthread m)
add_definitions(-DENABLE_SIDEKIQ)
list(APPEND SOURCES_RF rf_skiq_imp.c rf_skiq_imp_card.c rf_skiq_imp_port.c)
endif(SKIQ_FOUND)
if (ZEROMQ_FOUND)
set(SOURCES_SKIQ rf_skiq_imp.c rf_skiq_imp_card.c rf_skiq_imp_port.c)
if (ENABLE_RF_PLUGINS)
add_library(srsran_rf_skiq SHARED ${SOURCES_SKIQ})
set_target_properties(srsran_rf_skiq PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION})
list(APPEND DYNAMIC_PLUGINS srsran_rf_skiq)
else (ENABLE_RF_PLUGINS)
add_library(srsran_rf_skiq STATIC ${SOURCES_SKIQ})
list(APPEND STATIC_PLUGINS srsran_rf_skiq)
endif (ENABLE_RF_PLUGINS)
target_link_libraries(srsran_rf_skiq srsran_rf_utils srsran_phy ${SKIQ_LIBRARIES} rt)
install(TARGETS srsran_rf_skiq DESTINATION ${LIBRARY_DIR} OPTIONAL)
endif(SKIQ_FOUND AND ENABLE_SKIQ)
if (ZEROMQ_FOUND AND ENABLE_ZEROMQ)
add_definitions(-DENABLE_ZEROMQ)
list(APPEND SOURCES_RF rf_zmq_imp.c rf_zmq_imp_tx.c rf_zmq_imp_rx.c)
endif (ZEROMQ_FOUND)
set(SOURCES_ZMQ rf_zmq_imp.c rf_zmq_imp_tx.c rf_zmq_imp_rx.c)
if (ENABLE_RF_PLUGINS)
add_library(srsran_rf_zmq SHARED ${SOURCES_ZMQ})
set_target_properties(srsran_rf_zmq PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION})
list(APPEND DYNAMIC_PLUGINS srsran_rf_zmq)
else (ENABLE_RF_PLUGINS)
add_library(srsran_rf_zmq STATIC ${SOURCES_ZMQ})
list(APPEND STATIC_PLUGINS srsran_rf_zmq)
endif (ENABLE_RF_PLUGINS)
target_link_libraries(srsran_rf_zmq srsran_rf_utils srsran_phy ${ZEROMQ_LIBRARIES})
install(TARGETS srsran_rf_zmq DESTINATION ${LIBRARY_DIR} OPTIONAL)
endif (ZEROMQ_FOUND AND ENABLE_ZEROMQ)
# Add sources of file-based RF directly to the RF library (not as a plugin)
list(APPEND SOURCES_RF rf_file_imp.c rf_file_imp_tx.c rf_file_imp_rx.c)
# Top-level RF library
add_library(srsran_rf_object OBJECT ${SOURCES_RF})
set_property(TARGET srsran_rf_object PROPERTY POSITION_INDEPENDENT_CODE 1)
add_library(srsran_rf STATIC $<TARGET_OBJECTS:srsran_rf_object>)
add_library(srsran_rf_shared SHARED $<TARGET_OBJECTS:srsran_rf_object>)
target_link_libraries(srsran_rf srsran_rf_utils srsran_phy)
set_target_properties(srsran_rf PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION})
target_link_libraries(srsran_rf_shared srsran_rf_utils srsran_phy)
set_target_properties(srsran_rf_shared PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION})
if (UHD_FOUND)
target_link_libraries(srsran_rf ${UHD_LIBRARIES} ${Boost_LIBRARIES}) # Ubuntu 18.04 requires 'system' from Boost_LIBRARIES
target_link_libraries(srsran_rf_shared ${UHD_LIBRARIES} ${Boost_LIBRARIES})
endif (UHD_FOUND)
if (BLADERF_FOUND)
target_link_libraries(srsran_rf ${BLADERF_LIBRARIES})
target_link_libraries(srsran_rf_shared ${BLADERF_LIBRARIES})
endif (BLADERF_FOUND)
if (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR)
target_link_libraries(srsran_rf ${SOAPYSDR_LIBRARIES})
target_link_libraries(srsran_rf_shared ${SOAPYSDR_LIBRARIES})
endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR)
if(SKIQ_FOUND)
target_link_libraries(srsran_rf ${SKIQ_LIBRARIES} rt)
target_link_libraries(srsran_rf_shared ${SKIQ_LIBRARIES} rt)
endif(SKIQ_FOUND)
set(TOP_RF_LIBS)
if (ENABLE_RF_PLUGINS)
# Build as shared library with optional RF plugins (optional dependencies)
if (DYNAMIC_PLUGINS)
add_dependencies(srsran_rf_object ${DYNAMIC_PLUGINS})
endif (DYNAMIC_PLUGINS)
add_library(srsran_rf SHARED $<TARGET_OBJECTS:srsran_rf_object>)
target_link_libraries(srsran_rf dl)
list(APPEND TOP_RF_LIBS srsran_rf)
# Add $ORIGIN (i.e. current location of this library) to rpath of srsran_rf.
# This ensures that it will find the plugins that reside in the same directory as the library
set_target_properties(srsran_rf PROPERTIES BUILD_RPATH "\$ORIGIN/")
set_target_properties(srsran_rf PROPERTIES INSTALL_RPATH "\$ORIGIN/")
else (ENABLE_RF_PLUGINS)
# Build as static library with built-in RF plugins (mandatory dependencies)
add_library(srsran_rf STATIC $<TARGET_OBJECTS:srsran_rf_object>)
target_link_libraries(srsran_rf ${STATIC_PLUGINS})
list(APPEND TOP_RF_LIBS srsran_rf)
# Also build as shared library with built-in RF plugins (mandatory dependencies)
add_library(srsran_rf_shared SHARED $<TARGET_OBJECTS:srsran_rf_object>)
target_link_libraries(srsran_rf_shared ${STATIC_PLUGINS})
list(APPEND TOP_RF_LIBS srsran_rf_shared)
set_target_properties(srsran_rf_shared PROPERTIES OUTPUT_NAME srsran_rf)
endif (ENABLE_RF_PLUGINS)
foreach (TOP_RF_LIB ${TOP_RF_LIBS})
target_link_libraries(${TOP_RF_LIB} srsran_rf_utils srsran_phy)
set_target_properties(${TOP_RF_LIB} PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION})
install(TARGETS ${TOP_RF_LIB} DESTINATION ${LIBRARY_DIR} OPTIONAL)
endforeach ()
# Tests
if (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC)
add_executable(rfnoc_test rfnoc_test.cc)
target_link_libraries(rfnoc_test srsran_rf ${UHD_LIBRARIES} ${Boost_LIBRARIES} /usr/lib/x86_64-linux-gnu/libboost_system.so)
message(info ${Boost_LIBRARIES})
endif (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC)
if (ZEROMQ_FOUND)
target_link_libraries(srsran_rf ${ZEROMQ_LIBRARIES})
target_link_libraries(srsran_rf_shared ${ZEROMQ_LIBRARIES})
add_executable(rf_zmq_test rf_zmq_test.c)
target_link_libraries(rf_zmq_test srsran_rf)
#add_test(rf_zmq_test rf_zmq_test)
@ -115,6 +179,4 @@ if(RF_FOUND)
add_executable(rf_file_test rf_file_test.c)
target_link_libraries(rf_file_test srsran_rf)
add_test(rf_file_test rf_file_test)
INSTALL(TARGETS srsran_rf DESTINATION ${LIBRARY_DIR})
endif(RF_FOUND)

@ -24,6 +24,7 @@
#include <unistd.h>
#include "rf_blade_imp.h"
#include "rf_plugin.h"
#include "srsran/phy/common/timestamp.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
@ -544,3 +545,44 @@ int rf_blade_send_timed(void* h,
return nsamples;
}
rf_dev_t srsran_rf_dev_blade = {"bladeRF",
rf_blade_devname,
rf_blade_start_rx_stream,
rf_blade_stop_rx_stream,
rf_blade_flush_buffer,
rf_blade_has_rssi,
rf_blade_get_rssi,
rf_blade_suppress_stdout,
rf_blade_register_error_handler,
rf_blade_open,
.srsran_rf_open_multi = rf_blade_open_multi,
rf_blade_close,
rf_blade_set_rx_srate,
rf_blade_set_rx_gain,
rf_blade_set_rx_gain_ch,
rf_blade_set_tx_gain,
rf_blade_set_tx_gain_ch,
rf_blade_get_rx_gain,
rf_blade_get_tx_gain,
rf_blade_get_info,
rf_blade_set_rx_freq,
rf_blade_set_tx_srate,
rf_blade_set_tx_freq,
rf_blade_get_time,
NULL,
rf_blade_recv_with_time,
rf_blade_recv_with_time_multi,
rf_blade_send_timed,
.srsran_rf_send_timed_multi = rf_blade_send_timed_multi};
#ifdef ENABLE_RF_PLUGINS
int register_plugin(rf_dev_t** rf_api)
{
if (rf_api == NULL) {
return SRSRAN_ERROR;
}
*rf_api = &srsran_rf_dev_blade;
return SRSRAN_SUCCESS;
}
#endif /* ENABLE_RF_PLUGINS */

@ -19,11 +19,16 @@
*
*/
#ifndef SRSRAN_RF_BLADE_IMP_H_
#define SRSRAN_RF_BLADE_IMP_H_
#include "srsran/config.h"
#include "srsran/phy/rf/rf.h"
#define DEVNAME "bladerf"
extern rf_dev_t srsran_rf_dev_blade;
SRSRAN_API int rf_blade_open(char* args, void** handler);
SRSRAN_API int rf_blade_open_multi(char* args, void** handler, uint32_t nof_channels);
@ -99,3 +104,5 @@ SRSRAN_API int rf_blade_send_timed(void* h,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst);
#endif /* SRSRAN_RF_BLADE_IMP_H_ */

@ -22,276 +22,58 @@
#include "srsran/phy/rf/rf.h"
#include <stdbool.h>
/* RF frontend API */
typedef struct {
const char* name;
const char* (*srsran_rf_devname)(void* h);
int (*srsran_rf_start_rx_stream)(void* h, bool now);
int (*srsran_rf_stop_rx_stream)(void* h);
void (*srsran_rf_flush_buffer)(void* h);
bool (*srsran_rf_has_rssi)(void* h);
float (*srsran_rf_get_rssi)(void* h);
void (*srsran_rf_suppress_stdout)(void* h);
void (*srsran_rf_register_error_handler)(void* h, srsran_rf_error_handler_t error_handler, void* arg);
int (*srsran_rf_open)(char* args, void** h);
int (*srsran_rf_open_multi)(char* args, void** h, uint32_t nof_channels);
int (*srsran_rf_close)(void* h);
double (*srsran_rf_set_rx_srate)(void* h, double freq);
int (*srsran_rf_set_rx_gain)(void* h, double gain);
int (*srsran_rf_set_rx_gain_ch)(void* h, uint32_t ch, double gain);
int (*srsran_rf_set_tx_gain)(void* h, double gain);
int (*srsran_rf_set_tx_gain_ch)(void* h, uint32_t ch, double gain);
double (*srsran_rf_get_rx_gain)(void* h);
double (*srsran_rf_get_tx_gain)(void* h);
srsran_rf_info_t* (*srsran_rf_get_info)(void* h);
double (*srsran_rf_set_rx_freq)(void* h, uint32_t ch, double freq);
double (*srsran_rf_set_tx_srate)(void* h, double freq);
double (*srsran_rf_set_tx_freq)(void* h, uint32_t ch, double freq);
void (*srsran_rf_get_time)(void* h, time_t* secs, double* frac_secs);
void (*srsran_rf_sync_pps)(void* h);
int (*srsran_rf_recv_with_time)(void* h,
void* data,
uint32_t nsamples,
bool blocking,
time_t* secs,
double* frac_secs);
int (*srsran_rf_recv_with_time_multi)(void* h,
void** data,
uint32_t nsamples,
bool blocking,
time_t* secs,
double* frac_secs);
int (*srsran_rf_send_timed)(void* h,
void* data,
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst);
int (*srsran_rf_send_timed_multi)(void* h,
void** data,
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst);
} rf_dev_t;
/* Define implementation for UHD */
#ifdef ENABLE_UHD
#ifdef ENABLE_RF_PLUGINS
static srsran_rf_plugin_t plugin_uhd = {"libsrsran_rf_uhd.so", NULL, NULL};
#else
#include "rf_uhd_imp.h"
static rf_dev_t dev_uhd = {"UHD",
rf_uhd_devname,
rf_uhd_start_rx_stream,
rf_uhd_stop_rx_stream,
rf_uhd_flush_buffer,
rf_uhd_has_rssi,
rf_uhd_get_rssi,
rf_uhd_suppress_stdout,
rf_uhd_register_error_handler,
rf_uhd_open,
.srsran_rf_open_multi = rf_uhd_open_multi,
rf_uhd_close,
rf_uhd_set_rx_srate,
rf_uhd_set_rx_gain,
rf_uhd_set_rx_gain_ch,
rf_uhd_set_tx_gain,
rf_uhd_set_tx_gain_ch,
rf_uhd_get_rx_gain,
rf_uhd_get_tx_gain,
rf_uhd_get_info,
rf_uhd_set_rx_freq,
rf_uhd_set_tx_srate,
rf_uhd_set_tx_freq,
rf_uhd_get_time,
rf_uhd_sync_pps,
rf_uhd_recv_with_time,
rf_uhd_recv_with_time_multi,
rf_uhd_send_timed,
.srsran_rf_send_timed_multi = rf_uhd_send_timed_multi};
static srsran_rf_plugin_t plugin_uhd = {"", NULL, &srsran_rf_dev_uhd};
#endif
#endif
/* Define implementation for bladeRF */
#ifdef ENABLE_BLADERF
#ifdef ENABLE_RF_PLUGINS
static srsran_rf_plugin_t plugin_blade = {"libsrsran_rf_blade.so", NULL, NULL};
#else
#include "rf_blade_imp.h"
static rf_dev_t dev_blade = {"bladeRF",
rf_blade_devname,
rf_blade_start_rx_stream,
rf_blade_stop_rx_stream,
rf_blade_flush_buffer,
rf_blade_has_rssi,
rf_blade_get_rssi,
rf_blade_suppress_stdout,
rf_blade_register_error_handler,
rf_blade_open,
.srsran_rf_open_multi = rf_blade_open_multi,
rf_blade_close,
rf_blade_set_rx_srate,
rf_blade_set_rx_gain,
rf_blade_set_rx_gain_ch,
rf_blade_set_tx_gain,
rf_blade_set_tx_gain_ch,
rf_blade_get_rx_gain,
rf_blade_get_tx_gain,
rf_blade_get_info,
rf_blade_set_rx_freq,
rf_blade_set_tx_srate,
rf_blade_set_tx_freq,
rf_blade_get_time,
NULL,
rf_blade_recv_with_time,
rf_blade_recv_with_time_multi,
rf_blade_send_timed,
.srsran_rf_send_timed_multi = rf_blade_send_timed_multi};
static srsran_rf_plugin_t plugin_blade = {"", NULL, &srsran_rf_dev_blade};
#endif
#endif
/* Define implementation for SoapySDR */
#ifdef ENABLE_SOAPYSDR
#ifdef ENABLE_RF_PLUGINS
static srsran_rf_plugin_t plugin_soapy = {"libsrsran_rf_soapy.so", NULL, NULL};
#else
#include "rf_soapy_imp.h"
static rf_dev_t dev_soapy = {"soapy",
rf_soapy_devname,
rf_soapy_start_rx_stream,
rf_soapy_stop_rx_stream,
rf_soapy_flush_buffer,
rf_soapy_has_rssi,
rf_soapy_get_rssi,
rf_soapy_suppress_stdout,
rf_soapy_register_error_handler,
rf_soapy_open,
rf_soapy_open_multi,
rf_soapy_close,
rf_soapy_set_rx_srate,
rf_soapy_set_rx_gain,
rf_soapy_set_rx_gain_ch,
rf_soapy_set_tx_gain,
rf_soapy_set_tx_gain_ch,
rf_soapy_get_rx_gain,
rf_soapy_get_tx_gain,
rf_soapy_get_info,
rf_soapy_set_rx_freq,
rf_soapy_set_tx_srate,
rf_soapy_set_tx_freq,
rf_soapy_get_time,
NULL,
rf_soapy_recv_with_time,
rf_soapy_recv_with_time_multi,
rf_soapy_send_timed,
.srsran_rf_send_timed_multi = rf_soapy_send_timed_multi};
static srsran_rf_plugin_t plugin_soapy = {"", NULL, &srsran_rf_dev_soapy};
#endif
#endif
/* Define implementation for UHD */
/* Define implementation for ZeroMQ */
#ifdef ENABLE_ZEROMQ
#ifdef ENABLE_RF_PLUGINS
static srsran_rf_plugin_t plugin_zmq = {"libsrsran_rf_zmq.so", NULL, NULL};
#else
#include "rf_zmq_imp.h"
static rf_dev_t dev_zmq = {"zmq",
rf_zmq_devname,
rf_zmq_start_rx_stream,
rf_zmq_stop_rx_stream,
rf_zmq_flush_buffer,
rf_zmq_has_rssi,
rf_zmq_get_rssi,
rf_zmq_suppress_stdout,
rf_zmq_register_error_handler,
rf_zmq_open,
.srsran_rf_open_multi = rf_zmq_open_multi,
rf_zmq_close,
rf_zmq_set_rx_srate,
rf_zmq_set_rx_gain,
rf_zmq_set_rx_gain_ch,
rf_zmq_set_tx_gain,
rf_zmq_set_tx_gain_ch,
rf_zmq_get_rx_gain,
rf_zmq_get_tx_gain,
rf_zmq_get_info,
rf_zmq_set_rx_freq,
rf_zmq_set_tx_srate,
rf_zmq_set_tx_freq,
rf_zmq_get_time,
NULL,
rf_zmq_recv_with_time,
rf_zmq_recv_with_time_multi,
rf_zmq_send_timed,
.srsran_rf_send_timed_multi = rf_zmq_send_timed_multi};
static srsran_rf_plugin_t plugin_zmq = {"", NULL, &srsran_rf_dev_zmq};
#endif
#endif
/* Define implementation for file-based RF */
#include "rf_file_imp.h"
static rf_dev_t dev_file = {"file",
rf_file_devname,
rf_file_start_rx_stream,
rf_file_stop_rx_stream,
rf_file_flush_buffer,
rf_file_has_rssi,
rf_file_get_rssi,
rf_file_suppress_stdout,
rf_file_register_error_handler,
rf_file_open,
.srsran_rf_open_multi = rf_file_open_multi,
rf_file_close,
rf_file_set_rx_srate,
rf_file_set_rx_gain,
rf_file_set_rx_gain_ch,
rf_file_set_tx_gain,
rf_file_set_tx_gain_ch,
rf_file_get_rx_gain,
rf_file_get_tx_gain,
rf_file_get_info,
rf_file_set_rx_freq,
rf_file_set_tx_srate,
rf_file_set_tx_freq,
rf_file_get_time,
NULL,
rf_file_recv_with_time,
rf_file_recv_with_time_multi,
rf_file_send_timed,
.srsran_rf_send_timed_multi = rf_file_send_timed_multi};
static srsran_rf_plugin_t plugin_file = {"", NULL, &srsran_rf_dev_file};
/* Define implementation for Sidekiq */
#ifdef ENABLE_SIDEKIQ
#ifdef ENABLE_RF_PLUGINS
static srsran_rf_plugin_t plugin_skiq = {"libsrsran_rf_skiq.so", NULL, NULL};
#else
#include "rf_skiq_imp.h"
static rf_dev_t dev_skiq = {.name = "Sidekiq",
.srsran_rf_devname = rf_skiq_devname,
.srsran_rf_start_rx_stream = rf_skiq_start_rx_stream,
.srsran_rf_stop_rx_stream = rf_skiq_stop_rx_stream,
.srsran_rf_flush_buffer = rf_skiq_flush_buffer,
.srsran_rf_has_rssi = rf_skiq_has_rssi,
.srsran_rf_get_rssi = rf_skiq_get_rssi,
.srsran_rf_suppress_stdout = rf_skiq_suppress_stdout,
.srsran_rf_register_error_handler = rf_skiq_register_error_handler,
.srsran_rf_open = rf_skiq_open,
.srsran_rf_open_multi = rf_skiq_open_multi,
.srsran_rf_close = rf_skiq_close,
.srsran_rf_set_rx_srate = rf_skiq_set_rx_srate,
.srsran_rf_set_tx_srate = rf_skiq_set_tx_srate,
.srsran_rf_set_rx_gain = rf_skiq_set_rx_gain,
.srsran_rf_set_tx_gain = rf_skiq_set_tx_gain,
.srsran_rf_set_tx_gain_ch = rf_skiq_set_tx_gain_ch,
.srsran_rf_set_rx_gain_ch = rf_skiq_set_rx_gain_ch,
.srsran_rf_get_rx_gain = rf_skiq_get_rx_gain,
.srsran_rf_get_tx_gain = rf_skiq_get_tx_gain,
.srsran_rf_get_info = rf_skiq_get_info,
.srsran_rf_set_rx_freq = rf_skiq_set_rx_freq,
.srsran_rf_set_tx_freq = rf_skiq_set_tx_freq,
.srsran_rf_get_time = rf_skiq_get_time,
.srsran_rf_recv_with_time = rf_skiq_recv_with_time,
.srsran_rf_recv_with_time_multi = rf_skiq_recv_with_time_multi,
.srsran_rf_send_timed = rf_skiq_send_timed,
.srsran_rf_send_timed_multi = rf_skiq_send_timed_multi};
static srsran_rf_plugin_t plugin_skiq = {"", NULL, &srsran_rf_dev_skiq};
#endif
#endif
//#define ENABLE_DUMMY_DEV
@ -304,34 +86,36 @@ int dummy_rcv()
}
void dummy_fnc() {}
static rf_dev_t dev_dummy = {"dummy", dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc,
dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc,
dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_rcv,
dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc};
static rf_dev_t srsran_rf_dev_dummy = {
"dummy", dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc,
dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc,
dummy_fnc, dummy_fnc, dummy_fnc, dummy_rcv, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc};
static srsran_rf_plugin_t plugin_dummy = {"", NULL, &srsran_rf_dev_dummy};
#endif
/**
* Collection of all currently supported RF devices
* Collection of all currently available RF plugins
*/
static rf_dev_t* available_devices[] = {
static srsran_rf_plugin_t* rf_plugins[] = {
#ifdef ENABLE_UHD
&dev_uhd,
&plugin_uhd,
#endif
#ifdef ENABLE_SOAPYSDR
&dev_soapy,
&plugin_soapy,
#endif
#ifdef ENABLE_BLADERF
&dev_blade,
&plugin_blade,
#endif
#ifdef ENABLE_ZEROMQ
&dev_zmq,
&plugin_zmq,
#endif
#ifdef ENABLE_SIDEKIQ
&dev_skiq,
&plugin_skiq,
#endif
#ifdef ENABLE_DUMMY_DEV
&dev_dummy,
&plugin_dummy,
#endif
&dev_file,
&plugin_file,
NULL};

@ -19,9 +19,6 @@
*
*/
#ifndef SRSRAN_RF_IMP_TRX_H
#define SRSRAN_RF_IMP_TRX_H
#include "rf_file_imp.h"
#include "rf_file_imp_trx.h"
#include "rf_helper.h"
@ -865,4 +862,32 @@ clean_exit:
return ret;
}
#endif
rf_dev_t srsran_rf_dev_file = {"file",
rf_file_devname,
rf_file_start_rx_stream,
rf_file_stop_rx_stream,
rf_file_flush_buffer,
rf_file_has_rssi,
rf_file_get_rssi,
rf_file_suppress_stdout,
rf_file_register_error_handler,
rf_file_open,
.srsran_rf_open_multi = rf_file_open_multi,
rf_file_close,
rf_file_set_rx_srate,
rf_file_set_rx_gain,
rf_file_set_rx_gain_ch,
rf_file_set_tx_gain,
rf_file_set_tx_gain_ch,
rf_file_get_rx_gain,
rf_file_get_tx_gain,
rf_file_get_info,
rf_file_set_rx_freq,
rf_file_set_tx_srate,
rf_file_set_tx_freq,
rf_file_get_time,
NULL,
rf_file_recv_with_time,
rf_file_recv_with_time_multi,
rf_file_send_timed,
.srsran_rf_send_timed_multi = rf_file_send_timed_multi};

@ -32,6 +32,8 @@
#define PARAM_LEN (128)
#define PARAM_LEN_SHORT (PARAM_LEN / 2)
extern rf_dev_t srsran_rf_dev_file;
SRSRAN_API const char* rf_file_devname(void* h);
SRSRAN_API int rf_file_start_rx_stream(void* h, bool now);
@ -136,6 +138,7 @@ SRSRAN_API int rf_file_send_timed_multi(void* h,
* @param[in] base_srate Sample rate of RX and TX files
* @return SRSRAN_SUCCESS on success, otherwise error code
*/
SRSRAN_API int rf_file_open_file(void** h, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate);
SRSRAN_API int
rf_file_open_file(void** h, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate);
#endif // SRSRAN_RF_FILE_IMP_H

@ -19,17 +19,19 @@
*
*/
#include <string.h>
#include "rf_dev.h"
#include "srsran/phy/rf/rf.h"
#include "srsran/phy/utils/debug.h"
#include <dlfcn.h>
#include <string.h>
int rf_get_available_devices(char** devnames, int max_strlen)
{
int i = 0;
while (available_devices[i]->name) {
strncpy(devnames[i], available_devices[i]->name, max_strlen);
while (rf_plugins[i] != NULL) {
if (rf_plugins[i]->rf_api != NULL) {
strncpy(devnames[i], rf_plugins[i]->rf_api->name, max_strlen);
}
i++;
}
return i;
@ -104,20 +106,22 @@ int srsran_rf_open_devname(srsran_rf_t* rf, const char* devname, char* args, uin
rf->thread_gain_run = false;
bool no_rf_devs_detected = true;
printf("Available RF device list:");
for (unsigned int i = 0; available_devices[i]; i++) {
printf("Supported RF device list:");
for (unsigned int i = 0; rf_plugins[i] && rf_plugins[i]->rf_api; i++) {
no_rf_devs_detected = false;
printf(" %s ", available_devices[i]->name);
printf(" %s", rf_plugins[i]->rf_api->name);
}
printf("%s\n", no_rf_devs_detected ? " <none>" : "");
// Try to open the device if name is provided
if (devname && devname[0] != '\0') {
int i = 0;
while (available_devices[i] != NULL) {
if (!strcasecmp(available_devices[i]->name, devname)) {
rf->dev = available_devices[i];
return available_devices[i]->srsran_rf_open_multi(args, &rf->handler, nof_channels);
while (rf_plugins[i] != NULL) {
if (rf_plugins[i]->rf_api) {
if (!strcasecmp(rf_plugins[i]->rf_api->name, devname)) {
rf->dev = rf_plugins[i]->rf_api;
return rf_plugins[i]->rf_api->srsran_rf_open_multi(args, &rf->handler, nof_channels);
}
}
i++;
}
@ -129,16 +133,16 @@ int srsran_rf_open_devname(srsran_rf_t* rf, const char* devname, char* args, uin
return SRSRAN_ERROR;
}
// auto-mode, try to open in order of apperance in available_devices[] array
// auto-mode, try to open in order of apperance in rf_plugins[] array
int i = 0;
while (available_devices[i] != NULL) {
printf("Trying to open RF device '%s'\n", available_devices[i]->name);
if (!available_devices[i]->srsran_rf_open_multi(args, &rf->handler, nof_channels)) {
rf->dev = available_devices[i];
printf("RF device '%s' successfully opened\n", available_devices[i]->name);
while (rf_plugins[i] != NULL && rf_plugins[i]->rf_api != NULL) {
printf("Trying to open RF device '%s'\n", rf_plugins[i]->rf_api->name);
if (!rf_plugins[i]->rf_api->srsran_rf_open_multi(args, &rf->handler, nof_channels)) {
rf->dev = rf_plugins[i]->rf_api;
printf("RF device '%s' successfully opened\n", rf_plugins[i]->rf_api->name);
return SRSRAN_SUCCESS;
}
printf("Unable to open RF device '%s'\n", available_devices[i]->name);
printf("Unable to open RF device '%s'\n", rf_plugins[i]->rf_api->name);
i++;
}
@ -150,7 +154,7 @@ int srsran_rf_open_devname(srsran_rf_t* rf, const char* devname, char* args, uin
int srsran_rf_open_file(srsran_rf_t* rf, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate)
{
rf->dev = &dev_file;
rf->dev = &srsran_rf_dev_file;
// file abstraction has custom "open" function with file-related args
return rf_file_open_file(&rf->handler, rx_files, tx_files, nof_channels, base_srate);
@ -400,3 +404,93 @@ int srsran_rf_send_timed2(srsran_rf_t* rf,
{
return srsran_rf_send_timed3(rf, data, nsamples, secs, frac_secs, true, true, is_start_of_burst, is_end_of_burst);
}
#ifdef ENABLE_RF_PLUGINS
static void unload_plugin(srsran_rf_plugin_t* rf_plugin)
{
if (rf_plugin == NULL) {
return;
}
if (rf_plugin->dl_handle != NULL) {
rf_plugin->rf_api = NULL;
dlclose(rf_plugin->dl_handle);
rf_plugin->dl_handle = NULL;
}
}
static int load_plugin(srsran_rf_plugin_t* rf_plugin)
{
if (rf_plugin->rf_api != NULL) {
// already loaded
return SRSRAN_SUCCESS;
}
rf_plugin->dl_handle = dlopen(rf_plugin->plugin_name, RTLD_NOW);
if (rf_plugin->dl_handle == NULL) {
// Not an error, if loading failed due to missing dependencies.
// Mark this plugin as not available and return SUCCESS.
INFO("Failed to load RF plugin %s: %s", rf_plugin->plugin_name, dlerror());
rf_plugin->rf_api = NULL;
return SRSRAN_SUCCESS;
}
// clear errors
dlerror();
char* err = NULL;
// load symbols
int (*register_plugin)(rf_dev_t * *rf_api) = dlsym(rf_plugin->dl_handle, "register_plugin");
if ((err = dlerror()) != NULL) {
ERROR("Error loading symbol '%s': %s", "register_plugin", err);
goto clean_exit;
}
// register plugin
int ret = register_plugin(&rf_plugin->rf_api);
if (ret != SRSRAN_SUCCESS) {
ERROR("Failed to register RF API for plugin %s", rf_plugin->plugin_name);
goto clean_exit;
}
return SRSRAN_SUCCESS;
clean_exit:
unload_plugin(rf_plugin);
return SRSRAN_ERROR;
}
#endif /* ENABLE_RF_PLUGINS */
int srsran_rf_load_plugins()
{
#ifdef ENABLE_RF_PLUGINS
for (unsigned int i = 0; rf_plugins[i]; i++) {
if (load_plugin(rf_plugins[i]) != SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
}
printf("Active RF plugins:");
for (unsigned int i = 0; rf_plugins[i]; i++) {
if (rf_plugins[i]->dl_handle != NULL) {
printf(" %s", rf_plugins[i]->plugin_name);
}
}
printf("\n");
printf("Inactive RF plugins:");
for (unsigned int i = 0; rf_plugins[i]; i++) {
if (rf_plugins[i]->dl_handle == NULL) {
printf(" %s", rf_plugins[i]->plugin_name);
}
}
printf("\n");
#endif /* ENABLE_RF_PLUGINS */
return SRSRAN_SUCCESS;
}
// Search and load plugins when this library is loaded (shared) or right before main (static)
void __attribute__((constructor)) init()
{
if (srsran_rf_load_plugins() != SRSRAN_SUCCESS) {
ERROR("Failed to load RF plugins");
}
}

@ -0,0 +1,28 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2022 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSRAN_RF_PLUGIN_H
#define SRSRAN_RF_PLUGIN_H
#include "srsran/phy/rf/rf.h"
#ifdef __cplusplus
extern "C" {
#endif
SRSRAN_API int register_plugin(rf_dev_t** rf_api);
#ifdef __cplusplus
}
#endif
#endif /* SRSRAN_RF_PLUGIN_H */

@ -24,6 +24,7 @@
#include <sidekiq_api.h>
#include "rf_helper.h"
#include "rf_plugin.h"
#include "rf_skiq_imp.h"
#include "rf_skiq_imp_card.h"
@ -948,3 +949,43 @@ int rf_skiq_send_timed_multi(void* h_,
return (int)rpm;
}
rf_dev_t srsran_rf_dev_skiq = {.name = "Sidekiq",
.srsran_rf_devname = rf_skiq_devname,
.srsran_rf_start_rx_stream = rf_skiq_start_rx_stream,
.srsran_rf_stop_rx_stream = rf_skiq_stop_rx_stream,
.srsran_rf_flush_buffer = rf_skiq_flush_buffer,
.srsran_rf_has_rssi = rf_skiq_has_rssi,
.srsran_rf_get_rssi = rf_skiq_get_rssi,
.srsran_rf_suppress_stdout = rf_skiq_suppress_stdout,
.srsran_rf_register_error_handler = rf_skiq_register_error_handler,
.srsran_rf_open = rf_skiq_open,
.srsran_rf_open_multi = rf_skiq_open_multi,
.srsran_rf_close = rf_skiq_close,
.srsran_rf_set_rx_srate = rf_skiq_set_rx_srate,
.srsran_rf_set_tx_srate = rf_skiq_set_tx_srate,
.srsran_rf_set_rx_gain = rf_skiq_set_rx_gain,
.srsran_rf_set_tx_gain = rf_skiq_set_tx_gain,
.srsran_rf_set_tx_gain_ch = rf_skiq_set_tx_gain_ch,
.srsran_rf_set_rx_gain_ch = rf_skiq_set_rx_gain_ch,
.srsran_rf_get_rx_gain = rf_skiq_get_rx_gain,
.srsran_rf_get_tx_gain = rf_skiq_get_tx_gain,
.srsran_rf_get_info = rf_skiq_get_info,
.srsran_rf_set_rx_freq = rf_skiq_set_rx_freq,
.srsran_rf_set_tx_freq = rf_skiq_set_tx_freq,
.srsran_rf_get_time = rf_skiq_get_time,
.srsran_rf_recv_with_time = rf_skiq_recv_with_time,
.srsran_rf_recv_with_time_multi = rf_skiq_recv_with_time_multi,
.srsran_rf_send_timed = rf_skiq_send_timed,
.srsran_rf_send_timed_multi = rf_skiq_send_timed_multi};
#ifdef ENABLE_RF_PLUGINS
int register_plugin(rf_dev_t** rf_api)
{
if (rf_api == NULL) {
return SRSRAN_ERROR;
}
*rf_api = &srsran_rf_dev_skiq;
return SRSRAN_SUCCESS;
}
#endif /* ENABLE_RF_PLUGINS */

@ -25,6 +25,8 @@
#include "srsran/config.h"
#include "srsran/phy/rf/rf.h"
extern rf_dev_t srsran_rf_dev_skiq;
SRSRAN_API int rf_skiq_open(char* args, void** handler);
SRSRAN_API int rf_skiq_open_multi(char* args, void** handler, uint32_t nof_rx_antennas);

@ -25,6 +25,7 @@
#include <unistd.h>
#include "rf_helper.h"
#include "rf_plugin.h"
#include "rf_soapy_imp.h"
#include "srsran/phy/common/phy_common.h"
#include "srsran/phy/utils/debug.h"
@ -34,8 +35,8 @@
#include <SoapySDR/Formats.h>
#include <SoapySDR/Logger.h>
#include <SoapySDR/Time.h>
#include <SoapySDR/Version.h>
#include <SoapySDR/Types.h>
#include <SoapySDR/Version.h>
#define HAVE_ASYNC_THREAD 0
@ -862,7 +863,7 @@ int rf_soapy_recv_with_time_multi(void* h,
int rf_soapy_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs)
{
void* data_multi[SRSRAN_MAX_PORTS] = {NULL};
data_multi[0] = data;
data_multi[0] = data;
return rf_soapy_recv_with_time_multi(h, data_multi, nsamples, blocking, secs, frac_secs);
}
@ -1003,3 +1004,44 @@ int rf_soapy_send_timed_multi(void* h,
return n;
}
rf_dev_t srsran_rf_dev_soapy = {"soapy",
rf_soapy_devname,
rf_soapy_start_rx_stream,
rf_soapy_stop_rx_stream,
rf_soapy_flush_buffer,
rf_soapy_has_rssi,
rf_soapy_get_rssi,
rf_soapy_suppress_stdout,
rf_soapy_register_error_handler,
rf_soapy_open,
rf_soapy_open_multi,
rf_soapy_close,
rf_soapy_set_rx_srate,
rf_soapy_set_rx_gain,
rf_soapy_set_rx_gain_ch,
rf_soapy_set_tx_gain,
rf_soapy_set_tx_gain_ch,
rf_soapy_get_rx_gain,
rf_soapy_get_tx_gain,
rf_soapy_get_info,
rf_soapy_set_rx_freq,
rf_soapy_set_tx_srate,
rf_soapy_set_tx_freq,
rf_soapy_get_time,
NULL,
rf_soapy_recv_with_time,
rf_soapy_recv_with_time_multi,
rf_soapy_send_timed,
.srsran_rf_send_timed_multi = rf_soapy_send_timed_multi};
#ifdef ENABLE_RF_PLUGINS
int register_plugin(rf_dev_t** rf_api)
{
if (rf_api == NULL) {
return SRSRAN_ERROR;
}
*rf_api = &srsran_rf_dev_soapy;
return SRSRAN_SUCCESS;
}
#endif /* ENABLE_RF_PLUGINS */

@ -29,6 +29,8 @@
#include <stdint.h>
#define DEVNAME_SOAPY "soapy"
extern rf_dev_t srsran_rf_dev_soapy;
SRSRAN_API int rf_soapy_open(char* args, void** handler);
SRSRAN_API int rf_soapy_open_multi(char* args, void** handler, uint32_t num_requested_channels);

@ -29,6 +29,7 @@
#include <uhd/usrp/multi_usrp.hpp>
#include "rf_helper.h"
#include "rf_plugin.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
@ -1544,3 +1545,44 @@ int rf_uhd_send_timed_multi(void* h,
return nsamples;
}
rf_dev_t srsran_rf_dev_uhd = {"UHD",
rf_uhd_devname,
rf_uhd_start_rx_stream,
rf_uhd_stop_rx_stream,
rf_uhd_flush_buffer,
rf_uhd_has_rssi,
rf_uhd_get_rssi,
rf_uhd_suppress_stdout,
rf_uhd_register_error_handler,
rf_uhd_open,
rf_uhd_open_multi,
rf_uhd_close,
rf_uhd_set_rx_srate,
rf_uhd_set_rx_gain,
rf_uhd_set_rx_gain_ch,
rf_uhd_set_tx_gain,
rf_uhd_set_tx_gain_ch,
rf_uhd_get_rx_gain,
rf_uhd_get_tx_gain,
rf_uhd_get_info,
rf_uhd_set_rx_freq,
rf_uhd_set_tx_srate,
rf_uhd_set_tx_freq,
rf_uhd_get_time,
rf_uhd_sync_pps,
rf_uhd_recv_with_time,
rf_uhd_recv_with_time_multi,
rf_uhd_send_timed,
rf_uhd_send_timed_multi};
#ifdef ENABLE_RF_PLUGINS
int register_plugin(rf_dev_t** rf_api)
{
if (rf_api == NULL) {
return SRSRAN_ERROR;
}
*rf_api = &srsran_rf_dev_uhd;
return SRSRAN_SUCCESS;
}
#endif /* ENABLE_RF_PLUGINS */

@ -38,6 +38,8 @@ extern "C" {
#define DEVNAME_E3X0 "uhd_e3x0"
#define DEVNAME_UNKNOWN "uhd_unknown"
extern rf_dev_t srsran_rf_dev_uhd;
SRSRAN_API int rf_uhd_open(char* args, void** handler);
SRSRAN_API int rf_uhd_open_multi(char* args, void** handler, uint32_t nof_channels);

@ -21,6 +21,7 @@
#include "rf_zmq_imp.h"
#include "rf_helper.h"
#include "rf_plugin.h"
#include "rf_zmq_imp_trx.h"
#include <math.h>
#include <srsran/phy/common/phy_common.h>
@ -970,7 +971,7 @@ int rf_zmq_send_timed_multi(void* h,
}
// Scale according to current gain
srsran_vec_sc_prod_cfc(buf, tx_gain, buf, nsamples_baseband);
srsran_vec_sc_prod_cfc(buf, tx_gain, buf, nsamples_baseband);
// Finally, transmit baseband
int n = rf_zmq_tx_baseband(&handler->transmitter[i], buf, nsamples_baseband);
@ -992,3 +993,44 @@ clean_exit:
return ret;
}
rf_dev_t srsran_rf_dev_zmq = {"zmq",
rf_zmq_devname,
rf_zmq_start_rx_stream,
rf_zmq_stop_rx_stream,
rf_zmq_flush_buffer,
rf_zmq_has_rssi,
rf_zmq_get_rssi,
rf_zmq_suppress_stdout,
rf_zmq_register_error_handler,
rf_zmq_open,
.srsran_rf_open_multi = rf_zmq_open_multi,
rf_zmq_close,
rf_zmq_set_rx_srate,
rf_zmq_set_rx_gain,
rf_zmq_set_rx_gain_ch,
rf_zmq_set_tx_gain,
rf_zmq_set_tx_gain_ch,
rf_zmq_get_rx_gain,
rf_zmq_get_tx_gain,
rf_zmq_get_info,
rf_zmq_set_rx_freq,
rf_zmq_set_tx_srate,
rf_zmq_set_tx_freq,
rf_zmq_get_time,
NULL,
rf_zmq_recv_with_time,
rf_zmq_recv_with_time_multi,
rf_zmq_send_timed,
.srsran_rf_send_timed_multi = rf_zmq_send_timed_multi};
#ifdef ENABLE_RF_PLUGINS
int register_plugin(rf_dev_t** rf_api)
{
if (rf_api == NULL) {
return SRSRAN_ERROR;
}
*rf_api = &srsran_rf_dev_zmq;
return SRSRAN_SUCCESS;
}
#endif /* ENABLE_RF_PLUGINS */

@ -30,6 +30,8 @@
#define DEVNAME_ZMQ "ZeroMQ"
extern rf_dev_t srsran_rf_dev_zmq;
SRSRAN_API int rf_zmq_open(char* args, void** handler);
SRSRAN_API int rf_zmq_open_multi(char* args, void** handler, uint32_t nof_channels);

@ -102,12 +102,11 @@ void enb_tx_function(const char* tx_args, bool timed_tx)
// send data subframe per subframe
uint32_t num_txed_samples = 0;
// initial transmission without ts
void* data_ptr[SRSRAN_MAX_PORTS] = {NULL};
cf_t tx_buffer[NOF_RX_ANT][SF_LEN];
cf_t tx_buffer[NOF_RX_ANT][SF_LEN];
for (int c = 0; c < NOF_RX_ANT; c++) {
memcpy(&tx_buffer[c], &enb_tx_buffer[c][num_txed_samples], SF_LEN * sizeof (cf_t));
memcpy(&tx_buffer[c], &enb_tx_buffer[c][num_txed_samples], SF_LEN * sizeof(cf_t));
data_ptr[c] = &tx_buffer[c][0];
}
int ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false);
@ -125,7 +124,7 @@ void enb_tx_function(const char* tx_args, bool timed_tx)
// prepare data buffer
for (int c = 0; c < NOF_RX_ANT; c++) {
memcpy(&tx_buffer[c], &enb_tx_buffer[c][num_txed_samples], SF_LEN * sizeof (cf_t));
memcpy(&tx_buffer[c], &enb_tx_buffer[c][num_txed_samples], SF_LEN * sizeof(cf_t));
data_ptr[c] = &tx_buffer[c][0];
}
@ -243,34 +242,34 @@ int param_test(const char* args_param, const int num_channels)
int main()
{
// // two Rx ports
// if (param_test("rx_port=ipc://dl0,rx_port1=ipc://dl1", 2)) {
// fprintf(stderr, "Param test failed!\n");
// return SRSRAN_ERROR;
// }
// // multiple rx ports, no channel index provided
// if (param_test("rx_port=ipc://dl0,rx_port=ipc://dl1,rx_port=ipc://dl2,rx_port=ipc://dl3,base_srate=1.92e6", 4)) {
// fprintf(stderr, "Param test failed!\n");
// return SRSRAN_ERROR;
// }
// // One Rx, one Tx and all generic options
// if (param_test("rx_port0=tcp://"
// "localhost:2000,rx_format=sc16,tx_format=sc16,tx_type=pub,rx_type=sub,base_srate=1.92e6,id=test",
// 1)) {
// fprintf(stderr, "Param test failed!\n");
// return SRSRAN_ERROR;
// }
// // 1 port, 2 antennas, MIMO freq config
// if (param_test(
// "tx_port0=tcp://*:2001,tx_port1=tcp://*:2003,rx_port0=tcp://localhost:2000,rx_port1=tcp://"
// "localhost:2002,id=ue,base_srate=23.04e6,tx_freq0=2510e6,tx_freq1=2510e6,rx_freq0=2630e6,,rx_freq1=2630e6",
// 2)) {
// fprintf(stderr, "Param test failed!\n");
// return SRSRAN_ERROR;
// }
// // two Rx ports
// if (param_test("rx_port=ipc://dl0,rx_port1=ipc://dl1", 2)) {
// fprintf(stderr, "Param test failed!\n");
// return SRSRAN_ERROR;
// }
// // multiple rx ports, no channel index provided
// if (param_test("rx_port=ipc://dl0,rx_port=ipc://dl1,rx_port=ipc://dl2,rx_port=ipc://dl3,base_srate=1.92e6", 4)) {
// fprintf(stderr, "Param test failed!\n");
// return SRSRAN_ERROR;
// }
// // One Rx, one Tx and all generic options
// if (param_test("rx_port0=tcp://"
// "localhost:2000,rx_format=sc16,tx_format=sc16,tx_type=pub,rx_type=sub,base_srate=1.92e6,id=test",
// 1)) {
// fprintf(stderr, "Param test failed!\n");
// return SRSRAN_ERROR;
// }
// // 1 port, 2 antennas, MIMO freq config
// if (param_test(
// "tx_port0=tcp://*:2001,tx_port1=tcp://*:2003,rx_port0=tcp://localhost:2000,rx_port1=tcp://"
// "localhost:2002,id=ue,base_srate=23.04e6,tx_freq0=2510e6,tx_freq1=2510e6,rx_freq0=2630e6,,rx_freq1=2630e6",
// 2)) {
// fprintf(stderr, "Param test failed!\n");
// return SRSRAN_ERROR;
// }
#if NOF_RX_ANT == 1
// single tx, single rx with continuous transmissions (no timed tx) using IPC transport

@ -95,13 +95,13 @@ add_executable(psss_file_test psss_file_test.c)
target_link_libraries(psss_file_test srsran_phy)
# SL TM 2
add_test(sync_sl_test_tm2_p6_c_0 sync_sl_test -p 6 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p6_c0_s1.92e6.dat)
add_test(sync_sl_test_tm2_p15_c_84 sync_sl_test -p 15 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p15_c84_s3.84e6.dat)
add_test(sync_sl_test_tm2_p25_c_168 sync_sl_test -p 25 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat)
add_test(sync_sl_test_tm2_p50_c_252 sync_sl_test -p 50 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p50_c252_s15.36e6.dat)
add_test(sync_sl_test_tm2_p100_c_335 sync_sl_test -p 100 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat)
add_test(sync_sl_test_tm2_p6_c_0 sync_sl_test -p 6 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p6_c0_s1.92e6.dat)
add_test(sync_sl_test_tm2_p15_c_84 sync_sl_test -p 15 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p15_c84_s3.84e6.dat)
add_test(sync_sl_test_tm2_p25_c_168 sync_sl_test -p 25 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat)
add_test(sync_sl_test_tm2_p50_c_252 sync_sl_test -p 50 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p50_c252_s15.36e6.dat)
add_test(sync_sl_test_tm2_p100_c_335 sync_sl_test -p 100 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat)
# Sample offset
add_test(sync_sl_test_tm2_p25_c_168_so sync_sl_test -p 25 -d -o 300 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat)
add_test(sync_sl_test_tm2_p25_c_168_so sync_sl_test -p 25 -d -o 300 -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat)
# Self-test
add_test(sync_sl_test_tm2_self_test_p25_c_168 sync_sl_test -p 25 -c 168 -d)
# Self-test with frequency offset
@ -110,13 +110,13 @@ add_test(sync_sl_test_tm2_self_test_p25_c_168_fo sync_sl_test -p 25 -c 168 -d -f
add_test(sync_sl_test_tm2_self_test_p25_c_168_fo_so sync_sl_test -p 25 -c 168 -d -f 100 -o 3600)
# SL TM 4
add_test(sync_sl_test_tm4_p6_c_0 sync_sl_test -p 6 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p6_c0_size6_num1_cshift0_s1.92e6.dat)
add_test(sync_sl_test_tm4_p15_c_84 sync_sl_test -p 15 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p15_c84_size5_num3_cshift0_s3.84e6.dat)
add_test(sync_sl_test_tm4_p25_c_168 sync_sl_test -p 25 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p25_c168_size5_num5_cshift0_s7.68e6.dat)
add_test(sync_sl_test_tm4_p50_c_252 sync_sl_test -p 50 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p50_c252_size10_num5_cshift0_s15.36e6.dat)
#add_test(sync_sl_test_tm4_p100_c_335 sync_sl_test -p 100 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat)
add_test(sync_sl_test_tm4_p6_c_0 sync_sl_test -p 6 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p6_c0_size6_num1_cshift0_s1.92e6.dat)
add_test(sync_sl_test_tm4_p15_c_84 sync_sl_test -p 15 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p15_c84_size5_num3_cshift0_s3.84e6.dat)
add_test(sync_sl_test_tm4_p25_c_168 sync_sl_test -p 25 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p25_c168_size5_num5_cshift0_s7.68e6.dat)
add_test(sync_sl_test_tm4_p50_c_252 sync_sl_test -p 50 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p50_c252_size10_num5_cshift0_s15.36e6.dat)
#add_test(sync_sl_test_tm4_p100_c_335 sync_sl_test -p 100 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat)
# Sample offset
add_test(sync_sl_test_tm4_p25_c_168_so sync_sl_test -p 25 -t 4 -d -o 300 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p25_c168_size5_num5_cshift0_s7.68e6.dat )
add_test(sync_sl_test_tm4_p25_c_168_so sync_sl_test -p 25 -t 4 -d -o 300 -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p25_c168_size5_num5_cshift0_s7.68e6.dat )
# Self-test
add_test(sync_sl_test_self_test_tm4_p25_c_168 sync_sl_test -p 25 -t 4 -c 168 -d)
# Self-test with frequency offset

@ -339,8 +339,7 @@ int srsran_ue_sync_set_cell(srsran_ue_sync_t* q, srsran_cell_t cell)
q->cell = cell;
q->fft_size = srsran_symbol_sz(q->cell.nof_prb);
q->sf_len = SRSRAN_SF_LEN(q->fft_size);
srsran_sync_set_cp(&q->sfind, q->cell.cp);
srsran_sync_set_cp(&q->strack, q->cell.cp);
if (cell.id == 1000) {
/* If the cell is unkown, we search PSS/SSS in 5 ms */
q->nof_recv_sf = 5;
@ -376,6 +375,10 @@ int srsran_ue_sync_set_cell(srsran_ue_sync_t* q, srsran_cell_t cell)
}
}
// Set CP for find and track objects
srsran_sync_set_cp(&q->sfind, cell.cp);
srsran_sync_set_cp(&q->strack, cell.cp);
// When Cell ID is 1000, ue_sync receives nof_avg_find_frames frames in find state and does not go to tracking
// state and is used to search a cell
if (cell.id == 1000) {
@ -393,9 +396,6 @@ int srsran_ue_sync_set_cell(srsran_ue_sync_t* q, srsran_cell_t cell)
srsran_sync_set_cfo_ema_alpha(&q->strack, 0.1);
} else {
q->sfind.cp = cell.cp;
q->strack.cp = cell.cp;
srsran_sync_set_frame_type(&q->sfind, cell.frame_type);
srsran_sync_set_frame_type(&q->strack, cell.frame_type);

@ -174,6 +174,25 @@ int srsran_ue_ul_set_cell(srsran_ue_ul_t* q, srsran_cell_t cell)
return ret;
}
int srsran_ue_ul_set_cfr(srsran_ue_ul_t* q, const srsran_cfr_cfg_t* cfr)
{
if (q == NULL || cfr == NULL) {
ERROR("Error, invalid inputs");
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Copy the cfr config into the UE
q->cfr_config = *cfr;
// Set the cfr for the fft's
if (srsran_ofdm_set_cfr(&q->fft, &q->cfr_config) < SRSRAN_SUCCESS) {
ERROR("Error setting the CFR for the fft");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
int srsran_ue_ul_pregen_signals(srsran_ue_ul_t* q, srsran_ue_ul_cfg_t* cfg)
{
if (q->signals_pregenerated) {

@ -21,7 +21,7 @@
if(RF_FOUND)
add_library(srsran_radio STATIC radio.cc channel_mapping.cc)
target_link_libraries(srsran_radio srsran_rf srsran_common)
INSTALL(TARGETS srsran_radio DESTINATION ${LIBRARY_DIR})
install(TARGETS srsran_radio DESTINATION ${LIBRARY_DIR} OPTIONAL)
endif(RF_FOUND)
add_subdirectory(test)

@ -665,11 +665,11 @@ void radio::set_rx_freq(const uint32_t& carrier_idx, const double& freq)
// Map carrier index to physical channel
if (rx_channel_mapping.allocate_freq(carrier_idx, freq)) {
channel_mapping::device_mapping_t device_mapping = rx_channel_mapping.get_device_mapping(carrier_idx);
if (device_mapping.carrier_idx >= nof_channels_x_dev) {
logger.error("Invalid mapping RF channel %d to logical carrier %d on f_rx=%.1f MHz",
device_mapping.carrier_idx,
if (device_mapping.channel_idx >= nof_channels_x_dev) {
logger.error("Invalid mapping physical channel %d to logical carrier %d on f_rx=%.1f MHz (nof_channels_x_dev=%d, device_idx=%d)",
device_mapping.channel_idx,
carrier_idx,
freq / 1e6);
freq / 1e6, nof_channels_x_dev, device_mapping.device_idx);
return;
}
@ -684,7 +684,7 @@ void radio::set_rx_freq(const uint32_t& carrier_idx, const double& freq)
cur_rx_freqs[device_mapping.carrier_idx] = freq;
for (uint32_t i = 0; i < nof_antennas; i++) {
channel_mapping::device_mapping_t dm = rx_channel_mapping.get_device_mapping(carrier_idx, i);
if (dm.device_idx >= rf_devices.size() or dm.carrier_idx >= nof_channels_x_dev) {
if (dm.device_idx >= rf_devices.size() or dm.channel_idx >= nof_channels_x_dev) {
logger.error("Invalid port mapping %d:%d to logical carrier %d on f_rx=%.1f MHz",
dm.device_idx,
dm.channel_idx,
@ -804,9 +804,9 @@ void radio::set_tx_freq(const uint32_t& carrier_idx, const double& freq)
// Map carrier index to physical channel
if (tx_channel_mapping.allocate_freq(carrier_idx, freq)) {
channel_mapping::device_mapping_t device_mapping = tx_channel_mapping.get_device_mapping(carrier_idx);
if (device_mapping.carrier_idx >= nof_channels_x_dev) {
logger.error("Invalid mapping RF channel %d to logical carrier %d on f_tx=%.1f MHz",
device_mapping.carrier_idx,
if (device_mapping.channel_idx >= nof_channels_x_dev) {
logger.error("Invalid mapping physical channel %d to logical carrier %d on f_tx=%.1f MHz",
device_mapping.channel_idx,
carrier_idx,
freq / 1e6);
return;
@ -823,7 +823,7 @@ void radio::set_tx_freq(const uint32_t& carrier_idx, const double& freq)
cur_tx_freqs[device_mapping.carrier_idx] = freq;
for (uint32_t i = 0; i < nof_antennas; i++) {
device_mapping = tx_channel_mapping.get_device_mapping(carrier_idx, i);
if (device_mapping.device_idx >= rf_devices.size() or device_mapping.carrier_idx >= nof_channels_x_dev) {
if (device_mapping.device_idx >= rf_devices.size() or device_mapping.channel_idx >= nof_channels_x_dev) {
logger.error("Invalid port mapping %d:%d to logical carrier %d on f_rx=%.1f MHz",
device_mapping.device_idx,
device_mapping.channel_idx,

@ -32,4 +32,4 @@ set(SOURCES rlc.cc
add_library(srsran_rlc STATIC ${SOURCES})
target_link_libraries(srsran_rlc srsran_common ${ATOMIC_LIBS})
INSTALL(TARGETS srsran_rlc DESTINATION ${LIBRARY_DIR})
install(TARGETS srsran_rlc DESTINATION ${LIBRARY_DIR} OPTIONAL)

@ -86,14 +86,32 @@ bool rlc_am::configure(const rlc_config_t& cfg_)
return false;
}
RlcInfo("configured - t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, "
"t_reordering=%d, t_status_prohibit=%d",
cfg.am.t_poll_retx,
cfg.am.poll_pdu,
cfg.am.poll_byte,
cfg.am.max_retx_thresh,
cfg.am.t_reordering,
cfg.am.t_status_prohibit);
if (cfg.rat == srsran_rat_t::lte) {
RlcInfo("AM LTE configured - t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, "
"t_reordering=%d, t_status_prohibit=%d, tx_queue_length=%d",
cfg.am.t_poll_retx,
cfg.am.poll_pdu,
cfg.am.poll_byte,
cfg.am.max_retx_thresh,
cfg.am.t_reordering,
cfg.am.t_status_prohibit,
cfg.tx_queue_length);
} else if (cfg.rat == srsran_rat_t::nr) {
RlcInfo("AM NR configured - tx_sn_field_length=%d, rx_sn_field_length=%d, "
"t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, "
"max_retx_thresh=%d, t_reassembly=%d, t_status_prohibit=%di, tx_queue_length=%d",
to_number(cfg.am_nr.tx_sn_field_length),
to_number(cfg.am_nr.rx_sn_field_length),
cfg.am_nr.t_poll_retx,
cfg.am_nr.poll_pdu,
cfg.am_nr.poll_byte,
cfg.am_nr.max_retx_thresh,
cfg.am_nr.t_reassembly,
cfg.am_nr.t_status_prohibit,
cfg.tx_queue_length);
} else {
RlcError("Invalid RAT at entity configuration");
}
return true;
}
@ -244,6 +262,25 @@ int rlc_am::rlc_am_base_tx::write_sdu(unique_byte_buffer_t sdu)
return SRSRAN_SUCCESS;
}
void rlc_am::rlc_am_base_tx::discard_sdu(uint32_t discard_sn)
{
std::lock_guard<std::mutex> lock(mutex);
if (!tx_enabled) {
return;
}
bool discarded = tx_sdu_queue.apply_first([&discard_sn, this](unique_byte_buffer_t& sdu) {
if (sdu != nullptr && sdu->md.pdcp_sn == discard_sn) {
tx_sdu_queue.queue.pop_func(sdu);
sdu = nullptr;
}
return false;
});
// Discard fails when the PDCP PDU is already in Tx window.
RlcInfo("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn);
}
void rlc_am::rlc_am_base_tx::set_bsr_callback(bsr_callback_t callback)
{
bsr_callback = callback;

@ -232,7 +232,7 @@ void rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n
// Bytes needed for retx
if (not retx_queue.empty()) {
rlc_amd_retx_t& retx = retx_queue.front();
rlc_amd_retx_lte_t& retx = retx_queue.front();
RlcDebug("Buffer state - retx - SN=%d, Segment: %s, %d:%d",
retx.sn,
retx.is_segment ? "true" : "false",
@ -277,24 +277,6 @@ void rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n
}
}
void rlc_am_lte_tx::discard_sdu(uint32_t discard_sn)
{
if (!tx_enabled) {
return;
}
bool discarded = tx_sdu_queue.apply_first([&discard_sn, this](unique_byte_buffer_t& sdu) {
if (sdu != nullptr && sdu->md.pdcp_sn == discard_sn) {
tx_sdu_queue.queue.pop_func(sdu);
sdu = nullptr;
}
return false;
});
// Discard fails when the PDCP PDU is already in Tx window.
RlcInfo("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn);
}
bool rlc_am_lte_tx::sdu_queue_is_full()
{
return tx_sdu_queue.is_full();
@ -380,11 +362,11 @@ void rlc_am_lte_tx::retransmit_pdu(uint32_t sn)
RlcInfo("Schedule SN=%d for retx", pdu.rlc_sn);
rlc_amd_retx_t& retx = retx_queue.push();
retx.is_segment = false;
retx.so_start = 0;
retx.so_end = pdu.buf->N_bytes;
retx.sn = pdu.rlc_sn;
rlc_amd_retx_lte_t& retx = retx_queue.push();
retx.is_segment = false;
retx.so_start = 0;
retx.so_end = pdu.buf->N_bytes;
retx.sn = pdu.rlc_sn;
}
/****************************************************************************
@ -470,7 +452,7 @@ int rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes)
return -1;
}
rlc_amd_retx_t retx = retx_queue.front();
rlc_amd_retx_lte_t retx = retx_queue.front();
// Sanity check - drop any retx SNs not present in tx_window
while (not tx_window.has_sn(retx.sn)) {
@ -543,7 +525,7 @@ int rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes)
return (ptr - payload) + tx_window[retx.sn].buf->N_bytes;
}
int rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx)
int rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_lte_t retx)
{
if (tx_window[retx.sn].buf == NULL) {
RlcError("In build_segment: retx.sn=%d has null buffer", retx.sn);
@ -983,7 +965,7 @@ void rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
pdu.retx_count++;
check_sn_reached_max_retx(i);
rlc_amd_retx_t& retx = retx_queue.push();
rlc_amd_retx_lte_t& retx = retx_queue.push();
srsran_expect(tx_window[i].rlc_sn == i, "Incorrect RLC SN=%d!=%d being accessed", tx_window[i].rlc_sn, i);
retx.sn = i;
retx.is_segment = false;
@ -1107,7 +1089,7 @@ void rlc_am_lte_tx::debug_state()
RlcDebug("vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d", vt_a, vt_ms, vt_s, poll_sn);
}
int rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_t& retx)
int rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_lte_t& retx)
{
if (!retx.is_segment) {
if (tx_window.has_sn(retx.sn)) {

File diff suppressed because it is too large Load Diff

@ -24,6 +24,74 @@
namespace srsran {
/****************************************************************************
* Container implementation for pack/unpack functions
***************************************************************************/
rlc_am_nr_status_pdu_t::rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size) : sn_size(sn_size)
{
nacks_.reserve(RLC_AM_NR_TYP_NACKS);
}
void rlc_am_nr_status_pdu_t::reset()
{
cpt = rlc_am_nr_control_pdu_type_t::status_pdu;
ack_sn = INVALID_RLC_SN;
nacks_.clear();
packed_size_ = rlc_am_nr_status_pdu_sizeof_header_ack_sn;
}
void rlc_am_nr_status_pdu_t::push_nack(const rlc_status_nack_t& nack)
{
nacks_.push_back(nack);
packed_size_ += nack_size(nack);
}
bool rlc_am_nr_status_pdu_t::trim(uint32_t max_packed_size)
{
if (max_packed_size >= packed_size_) {
// no trimming required
return true;
}
if (max_packed_size < rlc_am_nr_status_pdu_sizeof_header_ack_sn) {
// too little space for smallest possible status PDU (only header + ACK).
return false;
}
// remove NACKs (starting from the back) until it fits into given space
// note: when removing a NACK for a segment, we have to remove all other NACKs with the same SN as well,
// see TS 38.322 Sec. 5.3.4:
// "set the ACK_SN to the SN of the next not received RLC SDU
// which is not indicated as missing in the resulting STATUS PDU."
while (nacks_.size() > 0 && (max_packed_size < packed_size_ || nacks_.back().nack_sn == ack_sn)) {
packed_size_ -= nack_size(nacks_.back());
ack_sn = nacks_.back().nack_sn;
nacks_.pop_back();
}
return true;
}
void rlc_am_nr_status_pdu_t::refresh_packed_size()
{
uint32_t packed_size = rlc_am_nr_status_pdu_sizeof_header_ack_sn;
for (auto nack : nacks_) {
packed_size += nack_size(nack);
}
}
uint32_t rlc_am_nr_status_pdu_t::nack_size(const rlc_status_nack_t& nack) const
{
uint32_t result = sn_size == rlc_am_nr_sn_size_t::size12bits ? rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn
: rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn;
if (nack.has_so) {
result += rlc_am_nr_status_pdu_sizeof_nack_so;
}
if (nack.has_nack_range) {
result += rlc_am_nr_status_pdu_sizeof_nack_range;
}
return result;
}
/****************************************************************************
* Header pack/unpack helper functions
* Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.4
@ -150,6 +218,11 @@ uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, b
return len;
}
/****************************************************************************
* Status PDU pack/unpack helper functions
* Ref: 3GPP TS 38.322 v16.2.0 Section 6.2.2.5
***************************************************************************/
uint32_t
rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status)
{
@ -160,8 +233,19 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload,
const uint32_t nof_bytes,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_status_pdu_t* status)
{
if (sn_size == rlc_am_nr_sn_size_t::size12bits) {
return rlc_am_nr_read_status_pdu_12bit_sn(payload, nof_bytes, status);
} else { // 18bit SN
return rlc_am_nr_read_status_pdu_18bit_sn(payload, nof_bytes, status);
}
}
uint32_t
rlc_am_nr_read_status_pdu_12bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status)
{
uint8_t* ptr = const_cast<uint8_t*>(payload);
status->reset();
// fixed part
status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT
@ -172,58 +256,144 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload,
return 0;
}
if (sn_size == rlc_am_nr_sn_size_t::size12bits) {
status->ack_sn = (*ptr & 0x0F) << 8; // first 4 bits SN
ptr++;
status->ack_sn = (*ptr & 0x0F) << 8; // first 4 bits SN
ptr++;
status->ack_sn |= (*ptr & 0xFF); // last 8 bits SN
ptr++;
// read E1 flag
uint8_t e1 = *ptr & 0x80;
status->ack_sn |= (*ptr & 0xFF); // last 8 bits SN
// sanity check for reserved bits
if ((*ptr & 0x7f) != 0) {
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
return 0;
}
// all good, continue with next byte depending on E1
ptr++;
while (e1 != 0) {
// E1 flag set, read a NACK_SN
rlc_status_nack_t nack = {};
nack.nack_sn = (*ptr & 0xff) << 4;
ptr++;
// read E1 flag
uint8_t e1 = *ptr & 0x80;
e1 = *ptr & 0x08; // 1 = further NACKs follow
uint8_t e2 = *ptr & 0x04; // 1 = set of {so_start, so_end} follows
uint8_t e3 = *ptr & 0x02; // 1 = NACK range follows (i.e. NACK across multiple SNs)
// sanity check for reserved bits
if ((*ptr & 0x7f) != 0) {
if ((*ptr & 0x01) != 0) {
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
return 0;
}
nack.nack_sn |= (*ptr & 0xF0) >> 4;
// all good, continue with next byte depending on E1
ptr++;
if (e2 != 0) {
nack.has_so = true;
nack.so_start = (*ptr) << 8;
ptr++;
nack.so_start |= (*ptr);
ptr++;
nack.so_end = (*ptr) << 8;
ptr++;
nack.so_end |= (*ptr);
ptr++;
}
if (e3 != 0) {
nack.has_nack_range = true;
nack.nack_range = (*ptr);
ptr++;
}
status->push_nack(nack);
if (uint32_t(ptr - payload) > nof_bytes) {
fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n");
return 0;
}
}
// reset number of acks
status->N_nack = 0;
return SRSRAN_SUCCESS;
}
while (e1 != 0) {
// E1 flag set, read a NACK_SN
rlc_status_nack_t nack = {};
nack.nack_sn = (*ptr & 0xff) << 4;
ptr++;
uint32_t
rlc_am_nr_read_status_pdu_18bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status)
{
uint8_t* ptr = const_cast<uint8_t*>(payload);
status->reset();
// fixed part
status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT
// sanity check
if (status->cpt != rlc_am_nr_control_pdu_type_t::status_pdu) {
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
return 0;
}
status->ack_sn = (*ptr & 0x0F) << 14; // upper 4 bits of SN
ptr++;
status->ack_sn |= (*ptr & 0xFF) << 6; // center 8 bits of SN
ptr++;
status->ack_sn |= (*ptr & 0xFC) >> 2; // lower 6 bits of SN
// read E1 flag
uint8_t e1 = *ptr & 0x02;
// sanity check for reserved bits
if ((*ptr & 0x01) != 0) {
fprintf(stderr, "Malformed PDU, reserved bit is set.\n");
return 0;
}
// all good, continue with next byte depending on E1
ptr++;
while (e1 != 0) {
// E1 flag set, read a NACK_SN
rlc_status_nack_t nack = {};
nack.nack_sn = (*ptr & 0xFF) << 10; // upper 8 bits of SN
ptr++;
nack.nack_sn |= (*ptr & 0xFF) << 2; // center 8 bits of SN
ptr++;
nack.nack_sn |= (*ptr & 0xC0) >> 6; // lower 2 bits of SN
e1 = *ptr & 0x08;
uint8_t e2 = *ptr & 0x04;
e1 = *ptr & 0x20; // 1 = further NACKs follow
uint8_t e2 = *ptr & 0x10; // 1 = set of {so_start, so_end} follows
uint8_t e3 = *ptr & 0x08; // 1 = NACK range follows (i.e. NACK across multiple SNs)
// uint8_t len2 = (*ptr & 0xF0) >> 4;
nack.nack_sn |= (*ptr & 0xF0) >> 4;
status->nacks[status->N_nack] = nack;
// sanity check for reserved bits
if ((*ptr & 0x07) != 0) {
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
return 0;
}
ptr++;
if (e2 != 0) {
nack.has_so = true;
nack.so_start = (*ptr) << 8;
ptr++;
nack.so_start |= (*ptr);
ptr++;
nack.so_end = (*ptr) << 8;
ptr++;
nack.so_end |= (*ptr);
ptr++;
if (e2 != 0) {
status->nacks[status->N_nack].has_so = true;
status->nacks[status->N_nack].so_start = (*ptr) << 8;
ptr++;
status->nacks[status->N_nack].so_start |= (*ptr);
ptr++;
status->nacks[status->N_nack].so_end = (*ptr) << 8;
ptr++;
status->nacks[status->N_nack].so_end |= (*ptr);
ptr++;
}
status->N_nack++;
if ((ptr - payload) > nof_bytes) {
fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n");
return 0;
}
}
if (e3 != 0) {
nack.has_nack_range = true;
nack.nack_range = (*ptr);
ptr++;
}
status->push_nack(nack);
if (uint32_t(ptr - payload) > nof_bytes) {
fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n");
return 0;
}
}
@ -239,64 +409,134 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload,
int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu,
const rlc_am_nr_sn_size_t sn_size,
byte_buffer_t* pdu)
{
if (sn_size == rlc_am_nr_sn_size_t::size12bits) {
return rlc_am_nr_write_status_pdu_12bit_sn(status_pdu, pdu);
} else { // 18bit SN
return rlc_am_nr_write_status_pdu_18bit_sn(status_pdu, pdu);
}
}
int32_t rlc_am_nr_write_status_pdu_12bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu)
{
uint8_t* ptr = pdu->msg;
// fixed header part
*ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero
if (sn_size == rlc_am_nr_sn_size_t::size12bits) {
// write first 4 bit of ACK_SN
*ptr |= (status_pdu.ack_sn >> 8) & 0x0f; // 4 bit ACK_SN
ptr++;
*ptr = status_pdu.ack_sn & 0xff; // remaining 8 bit of SN
ptr++;
// write first 4 bit of ACK_SN
*ptr |= (status_pdu.ack_sn >> 8) & 0x0f; // 4 bit ACK_SN
ptr++;
*ptr = status_pdu.ack_sn & 0xff; // remaining 8 bit of SN
ptr++;
// write E1 flag in octet 3
if (status_pdu.N_nack > 0) {
*ptr = 0x80;
} else {
*ptr = 0x00;
}
ptr++;
// write E1 flag in octet 3
if (status_pdu.nacks.size() > 0) {
*ptr = 0x80;
} else {
*ptr = 0x00;
}
ptr++;
if (status_pdu.nacks.size() > 0) {
for (uint32_t i = 0; i < status_pdu.nacks.size(); i++) {
// write first 8 bit of NACK_SN
*ptr = (status_pdu.nacks[i].nack_sn >> 4) & 0xff;
ptr++;
// write remaining 4 bits of NACK_SN
*ptr = (status_pdu.nacks[i].nack_sn & 0x0f) << 4;
// Set E1 if necessary
if (i < (uint32_t)(status_pdu.nacks.size() - 1)) {
*ptr |= 0x08;
}
if (status_pdu.N_nack > 0) {
for (uint32_t i = 0; i < status_pdu.N_nack; i++) {
// write first 8 bit of NACK_SN
*ptr = (status_pdu.nacks[i].nack_sn >> 4) & 0xff;
if (status_pdu.nacks[i].has_so) {
// Set E2
*ptr |= 0x04;
}
if (status_pdu.nacks[i].has_nack_range) {
// Set E3
*ptr |= 0x02;
}
ptr++;
if (status_pdu.nacks[i].has_so) {
(*ptr) = status_pdu.nacks[i].so_start >> 8;
ptr++;
(*ptr) = status_pdu.nacks[i].so_start;
ptr++;
(*ptr) = status_pdu.nacks[i].so_end >> 8;
ptr++;
(*ptr) = status_pdu.nacks[i].so_end;
ptr++;
}
if (status_pdu.nacks[i].has_nack_range) {
(*ptr) = status_pdu.nacks[i].nack_range;
ptr++;
}
}
}
pdu->N_bytes = ptr - pdu->msg;
return SRSRAN_SUCCESS;
}
int32_t rlc_am_nr_write_status_pdu_18bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu)
{
uint8_t* ptr = pdu->msg;
// fixed header part
*ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero
*ptr |= (status_pdu.ack_sn >> 14) & 0x0F; // upper 4 bits of SN
ptr++;
*ptr = (status_pdu.ack_sn >> 6) & 0xFF; // center 8 bits of SN
ptr++;
*ptr = (status_pdu.ack_sn << 2) & 0xFC; // lower 6 bits of SN
// write remaining 4 bits of NACK_SN
*ptr = (status_pdu.nacks[i].nack_sn & 0x0f) << 4;
// Set E1 if necessary
if (i < (uint32_t)(status_pdu.N_nack - 1)) {
*ptr |= 0x08;
}
if (status_pdu.nacks[i].has_so) {
// Set E2
*ptr |= 0x04;
ptr++;
(*ptr) = status_pdu.nacks[i].so_start >> 8;
ptr++;
(*ptr) = status_pdu.nacks[i].so_start;
ptr++;
(*ptr) = status_pdu.nacks[i].so_end >> 8;
ptr++;
(*ptr) = status_pdu.nacks[i].so_end;
}
// set E1 flag if necessary
if (status_pdu.nacks.size() > 0) {
*ptr |= 0x02;
}
ptr++;
if (status_pdu.nacks.size() > 0) {
for (uint32_t i = 0; i < status_pdu.nacks.size(); i++) {
*ptr = (status_pdu.nacks[i].nack_sn >> 10) & 0xFF; // upper 8 bits of SN
ptr++;
*ptr = (status_pdu.nacks[i].nack_sn >> 2) & 0xFF; // center 8 bits of SN
ptr++;
*ptr = (status_pdu.nacks[i].nack_sn << 6) & 0xC0; // lower 2 bits of SN
if (i < (uint32_t)(status_pdu.nacks.size() - 1)) {
*ptr |= 0x20; // Set E1
}
if (status_pdu.nacks[i].has_so) {
*ptr |= 0x10; // Set E2
}
if (status_pdu.nacks[i].has_nack_range) {
*ptr |= 0x08; // Set E3
}
ptr++;
if (status_pdu.nacks[i].has_so) {
(*ptr) = status_pdu.nacks[i].so_start >> 8;
ptr++;
(*ptr) = status_pdu.nacks[i].so_start;
ptr++;
(*ptr) = status_pdu.nacks[i].so_end >> 8;
ptr++;
(*ptr) = status_pdu.nacks[i].so_end;
ptr++;
}
if (status_pdu.nacks[i].has_nack_range) {
(*ptr) = status_pdu.nacks[i].nack_range;
ptr++;
}
}
} else {
// 18bit SN
*ptr |= (status_pdu.ack_sn >> 14) & 0x0f; // 4 bit ACK_SN
ptr++;
*ptr = status_pdu.ack_sn >> 8; // bit 3 - 10 of SN
ptr++;
*ptr = (status_pdu.ack_sn & 0xff); // remaining 6 bit of SN
ptr++;
}
pdu->N_bytes = ptr - pdu->msg;

@ -307,7 +307,18 @@ int rlc_um_base::rlc_um_base_tx::try_write_sdu(unique_byte_buffer_t sdu)
void rlc_um_base::rlc_um_base_tx::discard_sdu(uint32_t discard_sn)
{
RlcWarning("RLC UM: Discard SDU not implemented yet.");
std::lock_guard<std::mutex> lock(mutex);
bool discarded = tx_sdu_queue.apply_first([&discard_sn, this](unique_byte_buffer_t& sdu) {
if (sdu != nullptr && sdu->md.pdcp_sn == discard_sn) {
tx_sdu_queue.queue.pop_func(sdu);
sdu = nullptr;
}
return false;
});
// Discard fails when the PDCP PDU is already in Tx window.
RlcInfo("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn);
}
bool rlc_um_base::rlc_um_base_tx::sdu_queue_is_full()

@ -42,4 +42,4 @@ find_package(Threads REQUIRED)
add_library(srslog STATIC ${SOURCES})
target_link_libraries(srslog ${CMAKE_THREAD_LIBS_INIT})
INSTALL(TARGETS srslog DESTINATION ${LIBRARY_DIR})
install(TARGETS srslog DESTINATION ${LIBRARY_DIR} OPTIONAL)

@ -35,7 +35,7 @@ struct handler_instance {
// Handlers are added in a thread safe manner without using locks to avoid possible issues if a signal is emitted while
// modifying the callback array.
static constexpr unsigned max_handlers = 12;
static constexpr unsigned max_handlers = 256;
static handler_instance registered_handlers[max_handlers];
static std::atomic<unsigned> num_handlers;

@ -225,6 +225,39 @@ int test_dl_nas_transport()
return 0;
}
// DL NAS transport with AMF-UE-NGAP-ID larger than 32bit
int test_dl_nas_transport2()
{
uint8_t ngap_msg[] = {0x00, 0x04, 0x40, 0x42, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x06, 0x80, 0x03, 0x03,
0xcf, 0x37, 0xd0, 0x00, 0x55, 0x00, 0x02, 0x00, 0x01, 0x00, 0x26, 0x00, 0x2b, 0x2a,
0x7e, 0x00, 0x56, 0x00, 0x02, 0x00, 0x00, 0x21, 0xbc, 0x8d, 0xe5, 0x61, 0xf5, 0xb4,
0xa7, 0x05, 0x8f, 0xdb, 0xe2, 0x3b, 0x4e, 0x21, 0xda, 0x45, 0x20, 0x10, 0x5a, 0xb8,
0xd1, 0xdb, 0x13, 0x76, 0x80, 0x00, 0x1b, 0x1a, 0x8d, 0x3c, 0x98, 0x4c, 0x01, 0x06};
// 00044042000003000a0006800303cf37d00055000200010026002b2a7e00560002000021bc8de561f5b4a7058fdbe23b4e21da4520105ab8d1db137680001b1a8d3c984c0106
cbit_ref bref(ngap_msg, sizeof(ngap_msg));
ngap_pdu_c pdu;
TESTASSERT(pdu.unpack(bref) == SRSASN_SUCCESS);
// Check Fields
TESTASSERT(pdu.type().value == ngap_pdu_c::types_opts::init_msg);
TESTASSERT(pdu.init_msg().proc_code == ASN1_NGAP_ID_DL_NAS_TRANSPORT);
TESTASSERT(pdu.init_msg().crit.value == crit_opts::ignore);
TESTASSERT(pdu.init_msg().value.type().value == ngap_elem_procs_o::init_msg_c::types_opts::dl_nas_transport);
auto& dl_nas = pdu.init_msg().value.dl_nas_transport();
// Field 0
TESTASSERT_EQ(12948813776, dl_nas->amf_ue_ngap_id.value);
// ...
// Field 2
TESTASSERT(dl_nas->nas_pdu.value.size() == 42);
TESTASSERT(ceil(bref.distance(ngap_msg) / 8.0) == sizeof(ngap_msg));
TESTASSERT(test_pack_unpack_consistency(pdu) == SRSASN_SUCCESS);
return 0;
}
int test_ul_ran_status_transfer()
{
uint8_t ngap_msg[] = {0x00, 0x2e, 0x40, 0x3c, 0x00, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x55, 0x00,
@ -355,6 +388,7 @@ int main()
test_ngsetup_response();
test_init_ue_msg();
test_dl_nas_transport();
test_dl_nas_transport2();
test_ul_ran_status_transfer();
test_ue_context_release();
test_ue_context_release_complete();

@ -230,11 +230,11 @@ int rlc_am_nr_pdu_test6()
return SRSRAN_SUCCESS;
}
///< Control PDU tests
///< Control PDU tests (12bit SN)
// Status PDU for 12bit SN with ACK_SN=2065 and no further NACK_SN (E1 bit not set)
int rlc_am_nr_control_pdu_test1()
int rlc_am_nr_control_pdu_12bit_sn_test1()
{
test_delimit_logger delimiter("Control PDU test 1");
test_delimit_logger delimiter("Control PDU (12bit SN) test 1");
const int len = 3;
std::array<uint8_t, len> tv = {0x08, 0x11, 0x00};
srsran::byte_buffer_t pdu = make_pdu_and_log(tv);
@ -242,10 +242,10 @@ int rlc_am_nr_control_pdu_test1()
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu = {};
rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits);
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS);
TESTASSERT(status_pdu.ack_sn == 2065);
TESTASSERT(status_pdu.N_nack == 0);
TESTASSERT(status_pdu.nacks.size() == 0);
// reset status PDU
pdu.clear();
@ -262,9 +262,9 @@ int rlc_am_nr_control_pdu_test1()
}
// Status PDU for 12bit SN with ACK_SN=2065 and NACK_SN=273 (E1 bit set)
int rlc_am_nr_control_pdu_test2()
int rlc_am_nr_control_pdu_12bit_sn_test2()
{
test_delimit_logger delimiter("Control PDU test 2");
test_delimit_logger delimiter("Control PDU (12bit SN) test 2");
const int len = 5;
std::array<uint8_t, len> tv = {0x08, 0x11, 0x80, 0x11, 0x10};
srsran::byte_buffer_t pdu = make_pdu_and_log(tv);
@ -272,10 +272,10 @@ int rlc_am_nr_control_pdu_test2()
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu = {};
rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits);
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS);
TESTASSERT(status_pdu.ack_sn == 2065);
TESTASSERT(status_pdu.N_nack == 1);
TESTASSERT(status_pdu.nacks.size() == 1);
TESTASSERT(status_pdu.nacks[0].nack_sn == 273);
// reset status PDU
@ -283,7 +283,7 @@ int rlc_am_nr_control_pdu_test2()
// pack again
TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS);
// TESTASSERT(pdu.N_bytes == tv.size());
TESTASSERT(pdu.N_bytes == tv.size());
write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes);
@ -294,9 +294,9 @@ int rlc_am_nr_control_pdu_test2()
// Status PDU for 12bit SN with ACK_SN=2065, NACK_SN=273, SO_START=2, SO_END=5, NACK_SN=275, SO_START=5, SO_END=0xFFFF
// E1 and E2 bit set on first NACK, only E2 on second.
int rlc_am_nr_control_pdu_test3()
int rlc_am_nr_control_pdu_12bit_sn_test3()
{
test_delimit_logger delimiter("Control PDU test 3");
test_delimit_logger delimiter("Control PDU (12bit SN) test 3");
const int len = 15;
std::array<uint8_t, len> tv = {
0x08, 0x11, 0x80, 0x11, 0x1c, 0x00, 0x02, 0x00, 0x05, 0x11, 0x34, 0x00, 0x05, 0xFF, 0xFF};
@ -305,10 +305,10 @@ int rlc_am_nr_control_pdu_test3()
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu = {};
rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits);
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS);
TESTASSERT(status_pdu.ack_sn == 2065);
TESTASSERT(status_pdu.N_nack == 2);
TESTASSERT(status_pdu.nacks.size() == 2);
TESTASSERT(status_pdu.nacks[0].nack_sn == 273);
TESTASSERT(status_pdu.nacks[0].so_start == 2);
TESTASSERT(status_pdu.nacks[0].so_end == 5);
@ -331,9 +331,9 @@ int rlc_am_nr_control_pdu_test3()
// Status PDU for 12bit SN with ACK_SN=2065, NACK_SN=273, SO_START=2, SO_END=5, NACK_SN=275
// E1 and E2 bit set on first NACK, neither E1 or E2 on the second.
int rlc_am_nr_control_pdu_test4()
int rlc_am_nr_control_pdu_12bit_sn_test4()
{
test_delimit_logger delimiter("Control PDU test 4");
test_delimit_logger delimiter("Control PDU (12bit SN) test 4");
const int len = 11;
std::array<uint8_t, len> tv = {0x08, 0x11, 0x80, 0x11, 0x1c, 0x00, 0x02, 0x00, 0x05, 0x11, 0x30};
srsran::byte_buffer_t pdu = make_pdu_and_log(tv);
@ -341,10 +341,10 @@ int rlc_am_nr_control_pdu_test4()
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu = {};
rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits);
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS);
TESTASSERT(status_pdu.ack_sn == 2065);
TESTASSERT(status_pdu.N_nack == 2);
TESTASSERT(status_pdu.nacks.size() == 2);
TESTASSERT(status_pdu.nacks[0].nack_sn == 273);
TESTASSERT(status_pdu.nacks[0].has_so == true);
TESTASSERT(status_pdu.nacks[0].so_start == 2);
@ -368,9 +368,9 @@ int rlc_am_nr_control_pdu_test4()
// Malformed Status PDU, with E1 still set at the end of the PDU
// 12bit SN with ACK_SN=2065, NACK_SN=273, SO_START=2, SO_END=5, NACK_SN=275, SO_START=5, SO_END=0xFFFF
// E1 and E2 bit set on first NACK, only E2 on second.
int rlc_am_nr_control_pdu_test5()
int rlc_am_nr_control_pdu_12bit_sn_test5()
{
test_delimit_logger delimiter("Control PDU test 5");
test_delimit_logger delimiter("Control PDU (12bit SN) test 5");
const int len = 15;
std::array<uint8_t, len> tv = {
0x08, 0x11, 0x80, 0x11, 0x1c, 0x00, 0x02, 0x00, 0x05, 0x11, 0x3c, 0x00, 0x05, 0xFF, 0xFF};
@ -379,12 +379,562 @@ int rlc_am_nr_control_pdu_test5()
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu = {};
rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits);
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == 0);
return SRSRAN_SUCCESS;
}
// Status PDU for 12bit SN with ACK_SN=2065,
// NACK range0: 3 full SDUs, NACK_SN=273..275
// NACK range1: missing segment sequence across 4 SDUs
// starting at NACK_SN=276, SO_START=2,
// ending at NACK_SN=279, SO_END=5
// E1 and E3 bit set on first NACK, E2 and E3 bit set on the second.
int rlc_am_nr_control_pdu_12bit_sn_test_nack_range()
{
test_delimit_logger delimiter("Control PDU (12bit SN) test NACK range");
const int len = 13;
std::array<uint8_t, len> tv = {0x08, // D/C | 3CPT | 4ACK_SN_upper
0x11, // 8ACK_SN_lower
0x80, // E1 | 7R
0x11, // 8NACK_SN_upper
0x1a, // 4NACK_SN_lower | E1 | E2 | E3 | R
0x03, // 8NACK_range
0x11, // 8NACK_SN_upper
0x46, // 4NACK_SN_lower | E1 | E2 | E3 | R
0x00, // 8SO_START_upper
0x02, // 8SO_START_lower
0x00, // 8SO_END_upper
0x05, // 8SO_END_lower
0x04}; // 8NACK_range
srsran::byte_buffer_t pdu = make_pdu_and_log(tv);
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits);
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS);
TESTASSERT(status_pdu.ack_sn == 2065);
TESTASSERT(status_pdu.nacks.size() == 2);
TESTASSERT(status_pdu.nacks[0].nack_sn == 273);
TESTASSERT(status_pdu.nacks[0].has_so == false);
TESTASSERT(status_pdu.nacks[0].has_nack_range == true);
TESTASSERT(status_pdu.nacks[0].nack_range == 3);
TESTASSERT(status_pdu.nacks[1].nack_sn == 276);
TESTASSERT(status_pdu.nacks[1].has_so == true);
TESTASSERT(status_pdu.nacks[1].so_start == 2);
TESTASSERT(status_pdu.nacks[1].so_end == 5);
TESTASSERT(status_pdu.nacks[1].has_nack_range == true);
TESTASSERT(status_pdu.nacks[1].nack_range == 4);
// reset status PDU
pdu.clear();
// pack again
TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS);
TESTASSERT(pdu.N_bytes == tv.size());
write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes);
TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0);
return SRSRAN_SUCCESS;
}
// Test status PDU for correct trimming and estimation of packed size
// 1) Test init, copy and reset
// 2) Test step-wise growth and trimming of status PDU while covering several corner cases
int rlc_am_nr_control_pdu_test_trimming(rlc_am_nr_sn_size_t sn_size)
{
test_delimit_logger delimiter("Control PDU ({} bit SN) test trimming", to_number(sn_size));
// status PDU with no NACKs
{
constexpr uint32_t min_size = 3;
srsran::byte_buffer_t pdu;
rlc_am_nr_status_pdu_t status_pdu(sn_size);
status_pdu.ack_sn = 99;
TESTASSERT_EQ(status_pdu.packed_size, min_size); // minimum size
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, min_size);
rlc_am_nr_status_pdu_t status_pdu_copy = status_pdu;
TESTASSERT_EQ(status_pdu_copy.ack_sn, 99);
TESTASSERT_EQ(status_pdu_copy.packed_size, min_size); // minimum size
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu_copy, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, min_size);
status_pdu.reset();
status_pdu.ack_sn = 77;
TESTASSERT_EQ(status_pdu.packed_size, min_size); // should still have minimum size
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, min_size);
TESTASSERT_EQ(status_pdu_copy.ack_sn, 99); // shouldn't have changed
TESTASSERT_EQ(status_pdu_copy.packed_size, min_size); // minimum size
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu_copy, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, min_size);
}
// status PDU with multiple NACKs
// expect: ACK=77, NACKs=[12][14][17 50:99][17 150:199][17 250:299][19][21 333:111 r5][27 444:666 r3]
{
constexpr uint32_t min_size = 3;
const uint32_t nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3;
constexpr uint32_t so_size = 4;
constexpr uint32_t range_size = 1;
uint32_t expected_size = min_size;
srsran::byte_buffer_t pdu;
rlc_am_nr_status_pdu_t status_pdu(sn_size);
status_pdu.ack_sn = 77;
{
rlc_status_nack_t nack;
nack.nack_sn = 12;
status_pdu.push_nack(nack);
}
expected_size += nack_size;
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
{
rlc_status_nack_t nack;
nack.nack_sn = 14;
status_pdu.push_nack(nack);
}
expected_size += nack_size;
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
{
rlc_status_nack_t nack;
nack.nack_sn = 17;
nack.has_so = true;
nack.so_start = 50;
nack.so_end = 99;
status_pdu.push_nack(nack);
}
expected_size += nack_size + so_size;
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
{
rlc_status_nack_t nack;
nack.nack_sn = 17;
nack.has_so = true;
nack.so_start = 150;
nack.so_end = 199;
status_pdu.push_nack(nack);
}
expected_size += nack_size + so_size;
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
{
rlc_status_nack_t nack;
nack.nack_sn = 17;
nack.has_so = true;
nack.so_start = 250;
nack.so_end = 299;
status_pdu.push_nack(nack);
}
expected_size += nack_size + so_size;
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
{
rlc_status_nack_t nack;
nack.nack_sn = 19;
status_pdu.push_nack(nack);
}
expected_size += nack_size;
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
{
rlc_status_nack_t nack;
nack.nack_sn = 21;
nack.has_so = true;
nack.so_start = 333;
nack.so_end = 111;
nack.has_nack_range = true;
nack.nack_range = 5;
status_pdu.push_nack(nack);
}
expected_size += nack_size + so_size + range_size;
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
{
rlc_status_nack_t nack;
nack.nack_sn = 27;
nack.has_so = true;
nack.so_start = 444;
nack.so_end = 666;
nack.has_nack_range = true;
nack.nack_range = 3;
status_pdu.push_nack(nack);
}
expected_size += nack_size + so_size + range_size;
TESTASSERT_EQ(status_pdu.ack_sn, 77);
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
// current state: ACK=77, NACKs=[12][14][17 50:99][17 150:199][17 250:299][19][21 333:111 r5][27 444:666 r3]
// create a copy, check content
rlc_am_nr_status_pdu_t status_pdu_copy = status_pdu;
TESTASSERT_EQ(status_pdu_copy.ack_sn, 77);
TESTASSERT_EQ(status_pdu_copy.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu_copy, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
// current state: ACK=77, NACKs=[12][14][17 50:99][17 150:199][17 250:299][19][21 333:111 r5][27 444:666 r3]
// trim to much larger size: nothing should change
TESTASSERT_EQ(status_pdu.trim(expected_size * 2), true);
TESTASSERT_EQ(status_pdu.ack_sn, 77);
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
// trim to exact size: nothing should change
TESTASSERT_EQ(status_pdu.trim(expected_size), true);
TESTASSERT_EQ(status_pdu.ack_sn, 77);
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
// trim to (expected_size - 1): this should remove the last NACK and update ACK accordingly
TESTASSERT_EQ(status_pdu.trim(expected_size - 1), true);
expected_size -= nack_size + so_size + range_size;
TESTASSERT_EQ(status_pdu.ack_sn, 27);
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
// current state: ACK=27, NACKs=[12][14][17 50:99][17 150:199][17 250:299][19][21 333:111 r5]
// trim to (expected_size - last two NACKs): this should remove the last NACK and update ACK accordingly
TESTASSERT_EQ(status_pdu.trim(expected_size - (2 * nack_size + so_size + range_size)), true);
expected_size -= 2 * nack_size + so_size + range_size;
TESTASSERT_EQ(status_pdu.ack_sn, 19);
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
// current state: ACK=19, NACKs=[12][14][17 50:99][17 150:199][17 250:299]
// trim to (expected_size - 1): this should remove the last NACK and all other NACKs with the same SN
TESTASSERT_EQ(status_pdu.trim(expected_size - 1), true);
expected_size -= 3 * (nack_size + so_size);
TESTASSERT_EQ(status_pdu.ack_sn, 17);
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
// current state: ACK=17, NACKs=[12][14]
// trim to impossible size = 1: this should report a failure without changes of the PDU
TESTASSERT_EQ(status_pdu.trim(1), false);
TESTASSERT_EQ(status_pdu.ack_sn, 17);
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
// current state: ACK=17, NACKs=[12][14]
// trim to minimum size: this should remove all NACKs and update ACK to the SN of the first NACK
expected_size = min_size;
TESTASSERT_EQ(status_pdu.trim(expected_size), true);
TESTASSERT_EQ(status_pdu.ack_sn, 12);
TESTASSERT_EQ(status_pdu.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
// current state: ACK=12, NACKs empty
// check the copy again - should be unchanged if not a shallow copy
TESTASSERT_EQ(status_pdu_copy.ack_sn, 77);
TESTASSERT_EQ(status_pdu_copy.packed_size, expected_size);
TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu_copy, sn_size, &pdu), SRSRAN_SUCCESS);
TESTASSERT_EQ(pdu.N_bytes, expected_size);
}
return SRSRAN_SUCCESS;
}
///< Control PDU tests (18bit SN)
// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 and no further NACK_SN (E1 bit not set)
int rlc_am_nr_control_pdu_18bit_sn_test1()
{
test_delimit_logger delimiter("Control PDU (18bit SN) test 1");
const int len = 3;
std::array<uint8_t, len> tv = {0x0E, 0x66, 0x64};
srsran::byte_buffer_t pdu = make_pdu_and_log(tv);
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits);
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS);
TESTASSERT(status_pdu.ack_sn == 235929);
TESTASSERT(status_pdu.nacks.size() == 0);
// reset status PDU
pdu.clear();
// pack again
TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS);
TESTASSERT(pdu.N_bytes == tv.size());
write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes);
TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0);
return SRSRAN_SUCCESS;
}
// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set)
// and NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110
int rlc_am_nr_control_pdu_18bit_sn_test2()
{
test_delimit_logger delimiter("Control PDU (18bit SN) test 2");
const int len = 6;
std::array<uint8_t, len> tv = {0x0E, 0x66, 0x66, 0xD9, 0x99, 0x80};
srsran::byte_buffer_t pdu = make_pdu_and_log(tv);
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits);
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS);
TESTASSERT(status_pdu.ack_sn == 235929);
TESTASSERT(status_pdu.nacks.size() == 1);
TESTASSERT(status_pdu.nacks[0].nack_sn == 222822);
// reset status PDU
pdu.clear();
// pack again
TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS);
TESTASSERT(pdu.N_bytes == tv.size());
write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes);
TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0);
return SRSRAN_SUCCESS;
}
// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set),
// NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 (E1 and E2 bit set),
// SO_START=2, SO_END=5,
// NACK_SN=222975=0x366ff=0b11 0110 0110 1111 1111 (E2 bit set),
// SO_START=5, SO_END=0xFFFF
int rlc_am_nr_control_pdu_18bit_sn_test3()
{
test_delimit_logger delimiter("Control PDU (18bit SN) test 3");
const int len = 17;
std::array<uint8_t, len> tv = {0b00001110, // D/C | 3CPT | 4ACK_SN_upper
0b01100110, // 8ACK_SN_center
0b01100110, // 6ACK_SN_lower | E1 | R
0b11011001, // 8NACK_SN_upper
0b10011001, // 8NACK_SN_center
0b10110000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R
0x00, // 8SO_START_upper
0x02, // 8SO_START_lower
0x00, // 8SO_END_upper
0x05, // 8SO_END_lower
0b11011001, // 8NACK_SN_upper
0b10111111, // 8NACK_SN_center
0b11010000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R
0x00, // 8SO_START_upper
0x05, // 8SO_START_lower
0xFF, // 8SO_END_upper
0xFF}; // 8SO_END_lower
srsran::byte_buffer_t pdu = make_pdu_and_log(tv);
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits);
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS);
TESTASSERT(status_pdu.ack_sn == 235929);
TESTASSERT(status_pdu.nacks.size() == 2);
TESTASSERT(status_pdu.nacks[0].nack_sn == 222822);
TESTASSERT(status_pdu.nacks[0].has_so == true);
TESTASSERT(status_pdu.nacks[0].so_start == 2);
TESTASSERT(status_pdu.nacks[0].so_end == 5);
TESTASSERT(status_pdu.nacks[1].nack_sn == 222975);
TESTASSERT(status_pdu.nacks[1].has_so == true);
TESTASSERT(status_pdu.nacks[1].so_start == 5);
TESTASSERT(status_pdu.nacks[1].so_end == 0xFFFF);
// reset status PDU
pdu.clear();
// pack again
TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS);
TESTASSERT(pdu.N_bytes == tv.size());
write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes);
TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0);
return SRSRAN_SUCCESS;
}
// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set),
// NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 (E1 and E2 bit set),
// SO_START=2, SO_END=5,
// NACK_SN=222975=0x366ff=0b11 0110 0110 1111 1111 (E1 and E2 bit not set),
int rlc_am_nr_control_pdu_18bit_sn_test4()
{
test_delimit_logger delimiter("Control PDU (18bit SN) test 4");
const int len = 13;
std::array<uint8_t, len> tv = {0b00001110, // D/C | 3CPT | 4ACK_SN_upper
0b01100110, // 8ACK_SN_center
0b01100110, // 6ACK_SN_lower | E1 | R
0b11011001, // 8NACK_SN_upper
0b10011001, // 8NACK_SN_center
0b10110000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R
0x00, // 8SO_START_upper
0x02, // 8SO_START_lower
0x00, // 8SO_END_upper
0x05, // 8SO_END_lower
0b11011001, // 8NACK_SN_upper
0b10111111, // 8NACK_SN_center
0b11000000}; // 2NACK_SN_lower | E1 | E2 | E3 | 3R
srsran::byte_buffer_t pdu = make_pdu_and_log(tv);
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits);
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS);
TESTASSERT(status_pdu.ack_sn == 235929);
TESTASSERT(status_pdu.nacks.size() == 2);
TESTASSERT(status_pdu.nacks[0].nack_sn == 222822);
TESTASSERT(status_pdu.nacks[0].has_so == true);
TESTASSERT(status_pdu.nacks[0].so_start == 2);
TESTASSERT(status_pdu.nacks[0].so_end == 5);
TESTASSERT(status_pdu.nacks[1].nack_sn == 222975);
TESTASSERT(status_pdu.nacks[1].has_so == false);
// reset status PDU
pdu.clear();
// pack again
TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS);
TESTASSERT(pdu.N_bytes == tv.size());
write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes);
TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0);
return SRSRAN_SUCCESS;
}
// Malformed Status PDU, similar to test3 but with E1 still set at the end of the PDU
// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set),
// NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 (E1 and E2 bit set),
// SO_START=2, SO_END=5,
// NACK_SN=222975=0x366ff=0b11 0110 0110 1111 1111 ([!E1!] and E2 bit set),
// SO_START=5, SO_END=0xFFFF
int rlc_am_nr_control_pdu_18bit_sn_test5()
{
test_delimit_logger delimiter("Control PDU (18bit SN) test 5");
const int len = 17;
std::array<uint8_t, len> tv = {0b00001110, // D/C | 3CPT | 4ACK_SN_upper
0b01100110, // 8ACK_SN_center
0b01100110, // 6ACK_SN_lower | E1 | R
0b11011001, // 8NACK_SN_upper
0b10011001, // 8NACK_SN_center
0b10110000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R
0x00, // 8SO_START_upper
0x02, // 8SO_START_lower
0x00, // 8SO_END_upper
0x05, // 8SO_END_lower
0b11011001, // 8NACK_SN_upper
0b10111111, // 8NACK_SN_center
0b11110000, // 2NACK_SN_lower | [!E1!] | E2 | E3 | 3R
0x00, // 8SO_START_upper
0x05, // 8SO_START_lower
0xFF, // 8SO_END_upper
0xFF}; // 8SO_END_lower
srsran::byte_buffer_t pdu = make_pdu_and_log(tv);
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits);
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == 0);
return SRSRAN_SUCCESS;
}
// Status PDU for 18bit SN with ACK_SN=200977=0x31111=0b11 0001 0001 0001 0001,
// NACK range0: 3 full SDUs, NACK_SN=69905=0x11111=0b01 0001 0001 0001 0001
// NACK range1: missing segment sequence across 4 SDUs
// starting at NACK_SN=69913=0x11119=0b01 0001 0001 0001 1001, SO_START=2,
// ending at NACK_SN=69916, SO_END=5
// E1 and E3 bit set on first NACK, E2 and E3 bit set on the second.
int rlc_am_nr_control_pdu_18bit_sn_test_nack_range()
{
test_delimit_logger delimiter("Control PDU (18bit SN) test NACK range");
const int len = 15;
std::array<uint8_t, len> tv = {0b00001100, // D/C | 3CPT | 4ACK_SN_upper
0b01000100, // 8ACK_SN_center
0b01000110, // 6ACK_SN_lower | E1 | R
0b01000100, // 8NACK_SN_upper
0b01000100, // 8NACK_SN_center
0b01101000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R
0x03, // 8NACK_range
0b01000100, // 8NACK_SN_upper
0b01000110, // 8NACK_SN_center
0b01011000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R
0x00, // 8SO_START_upper
0x02, // 8SO_START_lower
0x00, // 8SO_END_upper
0x05, // 8SO_END_lower
0x04}; // 8NACK_range
srsran::byte_buffer_t pdu = make_pdu_and_log(tv);
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits);
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS);
TESTASSERT(status_pdu.ack_sn == 200977);
TESTASSERT(status_pdu.nacks.size() == 2);
TESTASSERT(status_pdu.nacks[0].nack_sn == 69905);
TESTASSERT(status_pdu.nacks[0].has_so == false);
TESTASSERT(status_pdu.nacks[0].has_nack_range == true);
TESTASSERT(status_pdu.nacks[0].nack_range == 3);
TESTASSERT(status_pdu.nacks[1].nack_sn == 69913);
TESTASSERT(status_pdu.nacks[1].has_so == true);
TESTASSERT(status_pdu.nacks[1].so_start == 2);
TESTASSERT(status_pdu.nacks[1].so_end == 5);
TESTASSERT(status_pdu.nacks[1].has_nack_range == true);
TESTASSERT(status_pdu.nacks[1].nack_range == 4);
// reset status PDU
pdu.clear();
// pack again
TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS);
TESTASSERT(pdu.N_bytes == tv.size());
write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes);
TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0);
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
static const struct option long_options[] = {{"pcap", no_argument, nullptr, 'p'}, {nullptr, 0, nullptr, 0}};
@ -441,28 +991,73 @@ int main(int argc, char** argv)
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_test1()) {
fprintf(stderr, "rlc_am_nr_control_pdu_test1() failed.\n");
if (rlc_am_nr_control_pdu_12bit_sn_test1()) {
fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test1() failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_12bit_sn_test2()) {
fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test2() failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_12bit_sn_test3()) {
fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test3() failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_12bit_sn_test4()) {
fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test4() failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_12bit_sn_test5()) {
fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test5() failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_12bit_sn_test_nack_range()) {
fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test_nack_range() failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_test_trimming(rlc_am_nr_sn_size_t::size12bits)) {
fprintf(stderr, "rlc_am_nr_control_pdu_test_trimming(size12bits) failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_18bit_sn_test1()) {
fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test1() failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_18bit_sn_test2()) {
fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test2() failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_18bit_sn_test3()) {
fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test3() failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_test2()) {
fprintf(stderr, "rlc_am_nr_control_pdu_test2() failed.\n");
if (rlc_am_nr_control_pdu_18bit_sn_test4()) {
fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test4() failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_test3()) {
fprintf(stderr, "rlc_am_nr_control_pdu_test3() failed.\n");
if (rlc_am_nr_control_pdu_18bit_sn_test5()) {
fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test5() failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_test4()) {
fprintf(stderr, "rlc_am_nr_control_pdu_test4() failed.\n");
if (rlc_am_nr_control_pdu_18bit_sn_test_nack_range()) {
fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test_nack_range() failed.\n");
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_test5()) {
fprintf(stderr, "rlc_am_nr_control_pdu_test5() failed.\n");
if (rlc_am_nr_control_pdu_test_trimming(rlc_am_nr_sn_size_t::size18bits)) {
fprintf(stderr, "rlc_am_nr_control_pdu_test_trimming(size18bits) failed.\n");
return SRSRAN_ERROR;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save