diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3b70b1f7a2..7cc12d18c5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -503,6 +503,14 @@ pages: cd .. - mkdir public/doxygen - rsync -a build/docs/html/ public/doxygen/ + - | + # Remove bigger file if size is bigger than 900MB + while [ $(du -s public | awk '{print $1}') -gt 921600 ]; do + file_to_remove=$(find public -type f ! -name "*.html" ! -name "*.js" -exec ls -l {} + | sort -k5 -n -r | awk 'NR==1' | awk '{print $NF}') + file_size=$(du -h "$file_to_remove" | awk '{print $1}') + echo "Removing file: $file_to_remove (Size: $file_size)" + rm -f "$file_to_remove" + done after_script: - | if [ $CI_JOB_STATUS = "failed" ]; then diff --git a/.gitlab/ci-shared/build.yml b/.gitlab/ci-shared/build.yml index e78efa8cf4..0cd2218e0c 100644 --- a/.gitlab/ci-shared/build.yml +++ b/.gitlab/ci-shared/build.yml @@ -61,9 +61,16 @@ variables: FORCE_DEBUG_INFO: "" # Empty for cmake default MARCH: "" # Empty for cmake default MTUNE: "" # Empty for cmake default + # CACHE + CCACHE_MAXSIZE: 3G # TEST TEST_EXECUTION_TIMEOUT: 0 # CI + TRANSFER_METER_FREQUENCY: 5s + ARTIFACT_COMPRESSION_LEVEL: slowest + CACHE_COMPRESSION_LEVEL: slowest + CACHE_REQUEST_TIMEOUT: 5 # minutes - 10 by default + # K8 KUBERNETES_CPU_REQUEST: 6 KUBERNETES_CPU_LIMIT: 6 KUBERNETES_MEMORY_REQUEST: 12Gi diff --git a/.gitlab/ci-shared/e2e.yml b/.gitlab/ci-shared/e2e.yml index 2336f3231e..4603254b20 100644 --- a/.gitlab/ci-shared/e2e.yml +++ b/.gitlab/ci-shared/e2e.yml @@ -66,7 +66,9 @@ variables: name: ${RETINA_REGISTRY_PREFIX}/launcher:${RETINA_VERSION} entrypoint: ["/bin/sh", "-c"] variables: - ARTIFACT_COMPRESSION_LEVEL: "slowest" + TRANSFER_METER_FREQUENCY: 5s + ARTIFACT_COMPRESSION_LEVEL: slowest + RUNNER_AFTER_SCRIPT_TIMEOUT: 1m KUBERNETES_CPU_REQUEST: 2 KUBERNETES_CPU_LIMIT: 2 KUBERNETES_MEMORY_REQUEST: 2Gi diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index 2ec01705ae..5d57c80406 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -732,6 +732,11 @@ smoke valgrind update cache: start_in: 30 minutes interruptible: false retry: 2 + after_script: + - *build_after_script + artifacts: + <<: *build_artifacts + when: on_failure cache: - *cache_build_set diff --git a/.gitlab/ci/docker.yml b/.gitlab/ci/docker.yml index f6e51de22c..89df865e75 100644 --- a/.gitlab/ci/docker.yml +++ b/.gitlab/ci/docker.yml @@ -291,6 +291,8 @@ grafana server image latest: KUBERNETES_CPU_LIMIT: 6 KUBERNETES_MEMORY_REQUEST: 12Gi KUBERNETES_MEMORY_LIMIT: 12Gi + KUBERNETES_EPHEMERAL_STORAGE_REQUEST: "50G" + KUBERNETES_EPHEMERAL_STORAGE_LIMIT: "50G" REGISTRY_URI: $GITLAB_REGISTRY_URI CONTEXT: ${CI_PROJECT_DIR} DOCKERFILE: docker diff --git a/.gitlab/ci/e2e.yml b/.gitlab/ci/e2e.yml index d531974cfb..16964687f8 100644 --- a/.gitlab/ci/e2e.yml +++ b/.gitlab/ci/e2e.yml @@ -210,22 +210,7 @@ amari 8UE: parallel: matrix: - KEYWORDS: - ["reestablishment and sequentially", "handover and sequentially"] - -amari 8UE [attach_detach 2024-03-15]: - extends: .zmq-uesim - variables: - MARKERS: "zmq and not smoke" - RETINA_PARAM_ARGS: "gnb.all.pcap=True gnb.all.rlc_enable=False gnb.all.enable_integrity_protection=True" - KEYWORDS: "attach_detach" - allow_failure: true - -amari 8UE [attach_detach 2023-09-08]: - extends: .zmq - variables: - MARKERS: "zmq and not smoke" - RETINA_PARAM_ARGS: "gnb.all.pcap=True gnb.all.rlc_enable=False gnb.all.enable_integrity_protection=True" - KEYWORDS: "attach_detach" + ["reestablishment and sequentially", "handover and sequentially", "attach_detach"] amari 8UE beta: extends: amari 8UE @@ -486,7 +471,8 @@ viavi: - KEYWORDS: [ "ideal and 1UE", - "ideal and 32UE and not experimental", + "ideal and 32UE and not experimental and not tdd", + "tdd", "fading and 1UE", "fading and 32UE", "birth-death and 1UE", diff --git a/.gitlab/ci/e2e/.env b/.gitlab/ci/e2e/.env index 5eedba5aff..03bbe9f76e 100644 --- a/.gitlab/ci/e2e/.env +++ b/.gitlab/ci/e2e/.env @@ -1,6 +1,6 @@ SRSGNB_REGISTRY_URI=registry.gitlab.com/softwareradiosystems/srsgnb RETINA_REGISTRY_PREFIX=registry.gitlab.com/softwareradiosystems/ci/retina -RETINA_VERSION=0.54.9 +RETINA_VERSION=0.54.12 UBUNTU_VERSION=24.04 AMARISOFT_VERSION=2023-09-08 SRSUE_VERSION=23.11 @@ -8,7 +8,7 @@ OPEN5GS_VERSION=2.7.0 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin METRICS_SERVER_VERSION=1.7.3 DPDK_VERSION=23.11.1 -ZMQ_HOSTLABEL_0=kubernetes.io/hostname=rfci-virt-master +ZMQ_HOSTLABEL_0=kubernetes.io/hostname=hp-generic-2 ZMQ_HOSTLABEL_1=kubernetes.io/hostname=srskit2 AMARISOFT_TXRX_BINARY_PATH=../../build_trx_srsran/libtrx_srsran.so GNB_BINARY_PATH=../../build/apps/gnb/gnb diff --git a/.gitlab/ci/e2e/retina_request_zmq.yml b/.gitlab/ci/e2e/retina_request_zmq.yml index bf2bc2678e..3695b2bb00 100644 --- a/.gitlab/ci/e2e/retina_request_zmq.yml +++ b/.gitlab/ci/e2e/retina_request_zmq.yml @@ -15,8 +15,8 @@ requirements: arch: amd64 cpu: - requests: 10 - limits: 10 + requests: 5 + limits: 5 memory: requests: "26G" limits: "26G" @@ -40,8 +40,8 @@ requirements: arch: amd64 cpu: - requests: 10 - limits: 10 + requests: 5 + limits: 5 memory: requests: "26G" limits: "26G" @@ -62,8 +62,8 @@ requirements: arch: amd64 cpu: - requests: 2 - limits: 2 + requests: 1 + limits: 1 memory: requests: "8G" limits: "8G" diff --git a/.gitlab/ci/e2e/retina_request_zmq_cudu.yml b/.gitlab/ci/e2e/retina_request_zmq_cudu.yml index 960b8bdd97..8aa9318a95 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_cudu.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_cudu.yml @@ -15,8 +15,8 @@ requirements: arch: amd64 cpu: - requests: 10 - limits: 10 + requests: 5 + limits: 5 memory: requests: "26G" limits: "26G" @@ -40,8 +40,8 @@ requirements: arch: amd64 cpu: - requests: 10 - limits: 10 + requests: 5 + limits: 5 memory: requests: "26G" limits: "26G" @@ -63,8 +63,8 @@ requirements: arch: amd64 cpu: - requests: 2 - limits: 2 + requests: 1 + limits: 1 memory: requests: "8G" limits: "8G" diff --git a/.gitlab/ci/e2e/retina_request_zmq_deb.yml b/.gitlab/ci/e2e/retina_request_zmq_deb.yml index 8a1197e9f9..253f1910c5 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_deb.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_deb.yml @@ -15,8 +15,8 @@ requirements: arch: amd64 cpu: - requests: 10 - limits: 10 + requests: 5 + limits: 5 memory: requests: "26G" limits: "26G" @@ -40,8 +40,8 @@ requirements: arch: amd64 cpu: - requests: 10 - limits: 10 + requests: 5 + limits: 5 memory: requests: "26G" limits: "26G" @@ -60,8 +60,8 @@ requirements: arch: amd64 cpu: - requests: 2 - limits: 2 + requests: 1 + limits: 1 memory: requests: "8G" limits: "8G" diff --git a/.gitlab/ci/e2e/retina_request_zmq_srsue.yml b/.gitlab/ci/e2e/retina_request_zmq_srsue.yml index 9f4f804687..94aef1e028 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_srsue.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_srsue.yml @@ -14,8 +14,8 @@ requirements: arch: amd64 cpu: - requests: 10 - limits: 10 + requests: 5 + limits: 5 memory: requests: "26G" limits: "26G" @@ -35,8 +35,8 @@ requirements: arch: amd64 cpu: - requests: 10 - limits: 10 + requests: 5 + limits: 5 memory: requests: "26G" limits: "26G" @@ -57,8 +57,8 @@ requirements: arch: amd64 cpu: - requests: 2 - limits: 2 + requests: 1 + limits: 1 memory: requests: "8G" limits: "8G" diff --git a/CMakeLists.txt b/CMakeLists.txt index cf2f10b05b..556b0fe092 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,7 @@ option(STOP_ON_WARNING "Interrupt application on warning" OFF) option(ENABLE_WERROR "Stop compilation on errors" ON) option(ENABLE_TSAN "Enable clang thread sanitizer" OFF) option(ENABLE_ASAN "Enable clang address sanitizer" OFF) +option(ENABLE_RTSAN "Enable clang real-time sanitizer" OFF) option(ENABLE_GCOV "Enable code coverage" OFF) option(ENABLE_UHD "Enable UHD" ON) option(ENABLE_ZEROMQ "Enable ZeroMQ" OFF) @@ -210,6 +211,12 @@ if (ENABLE_TSAN) add_definitions(-DENABLE_TSAN) endif (ENABLE_TSAN) +if (ENABLE_RTSAN) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=realtime") + ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-fsanitize=realtime" HAVE_RPATH_FORCE) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=realtime") +endif () + if (FORCE_DEBUG_INFO) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") endif (FORCE_DEBUG_INFO) diff --git a/apps/cu/CMakeLists.txt b/apps/cu/CMakeLists.txt index 6f2de3e645..c09b6aff9b 100644 --- a/apps/cu/CMakeLists.txt +++ b/apps/cu/CMakeLists.txt @@ -23,7 +23,6 @@ add_executable(srscu cu_appconfig_cli11_schema.cpp cu_appconfig_validator.cpp cu_appconfig_yaml_writer.cpp - ../gnb/gnb_appconfig_translators.cpp ) install(TARGETS srscu @@ -39,7 +38,7 @@ target_link_libraries(srscu srsran_f1c_gateway srsran_e1_gateway srsran_e2 - srsgnb_app_f1u_cu_up_split_connector + srsran_f1u_cu_up_split_connector srsran_pcap ngap_asn1 ) diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index bc8c2ef892..643ab81a4b 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -20,12 +20,35 @@ * */ +#include "apps/cu/adapters/e2_gateways.h" +#include "apps/cu/cu_appconfig_cli11_schema.h" +#include "apps/services/application_message_banners.h" +#include "apps/services/application_tracer.h" +#include "apps/services/buffer_pool/buffer_pool_manager.h" +#include "apps/services/metrics/metrics_manager.h" +#include "apps/services/metrics/metrics_notifier_proxy.h" +#include "apps/services/stdin_command_dispatcher.h" +#include "apps/services/worker_manager/worker_manager.h" +#include "apps/services/worker_manager/worker_manager_config.h" +#include "apps/units/cu_cp/cu_cp_application_unit.h" +#include "apps/units/cu_cp/cu_cp_config_translators.h" +#include "apps/units/cu_cp/cu_cp_unit_config.h" +#include "apps/units/cu_cp/pcap_factory.h" +#include "apps/units/cu_up/cu_up_application_unit.h" +#include "apps/units/cu_up/cu_up_unit_config.h" +#include "apps/units/cu_up/pcap_factory.h" +#include "cu_appconfig.h" +#include "cu_appconfig_validator.h" +#include "cu_appconfig_yaml_writer.h" +#include "srsran/cu_up/cu_up.h" +#include "srsran/e1ap/gateways/e1_local_connector_factory.h" #include "srsran/f1ap/gateways/f1c_network_server_factory.h" #include "srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h" #include "srsran/gateways/udp_network_gateway.h" #include "srsran/gtpu/gtpu_config.h" #include "srsran/gtpu/gtpu_demux_factory.h" #include "srsran/gtpu/ngu_gateway.h" +#include "srsran/ngap/gateways/n2_connection_client_factory.h" #include "srsran/pcap/dlt_pcap.h" #include "srsran/support/backtrace.h" #include "srsran/support/config_parsers.h" @@ -40,36 +63,6 @@ #include "srsran/support/tracing/event_tracing.h" #include "srsran/support/versioning/build_info.h" #include "srsran/support/versioning/version.h" - -#include "apps/cu/cu_appconfig_cli11_schema.h" -#include "apps/units/cu_cp/cu_cp_application_unit.h" -#include "apps/units/cu_cp/cu_cp_config_translators.h" -#include "apps/units/cu_cp/cu_cp_unit_config.h" -#include "apps/units/cu_cp/pcap_factory.h" -#include "apps/units/cu_up/cu_up_application_unit.h" -#include "apps/units/cu_up/cu_up_unit_config.h" -#include "apps/units/cu_up/pcap_factory.h" -#include "srsran/cu_up/cu_up.h" - -// TODO remove apps/gnb/*.h -#include "apps/cu/adapters/e2_gateways.h" -#include "apps/gnb/gnb_appconfig_translators.h" - -#include "apps/services/application_message_banners.h" -#include "apps/services/application_tracer.h" -#include "apps/services/buffer_pool/buffer_pool_manager.h" -#include "apps/services/metrics/metrics_manager.h" -#include "apps/services/metrics/metrics_notifier_proxy.h" -#include "apps/services/stdin_command_dispatcher.h" -#include "apps/services/worker_manager.h" -#include "apps/services/worker_manager_config.h" -#include "cu_appconfig.h" -#include "cu_appconfig_validator.h" -#include "cu_appconfig_yaml_writer.h" - -#include "srsran/e1ap/gateways/e1_local_connector_factory.h" -#include "srsran/ngap/gateways/n2_connection_client_factory.h" - #include #include diff --git a/apps/cu/cu_appconfig.h b/apps/cu/cu_appconfig.h index cd6e53a097..3ddb4bf3a6 100644 --- a/apps/cu/cu_appconfig.h +++ b/apps/cu/cu_appconfig.h @@ -22,9 +22,10 @@ #pragma once -#include "apps/gnb/gnb_appconfig.h" #include "apps/services/buffer_pool/buffer_pool_appconfig.h" +#include "apps/services/e2/e2_appconfig.h" #include "apps/services/logger/logger_appconfig.h" +#include "apps/services/worker_manager/worker_manager_appconfig.h" #include namespace srsran { @@ -46,7 +47,7 @@ struct cu_f1ap_appconfig { } // namespace srs_cu -/// Monolithic gnb application configuration. +/// CU application configuration. struct cu_appconfig { /// Default constructor to update the log filename. cu_appconfig() { log_cfg.filename = "/tmp/cu.log"; } diff --git a/apps/cu/cu_appconfig_cli11_schema.cpp b/apps/cu/cu_appconfig_cli11_schema.cpp index 2154cbf695..8c6c693567 100644 --- a/apps/cu/cu_appconfig_cli11_schema.cpp +++ b/apps/cu/cu_appconfig_cli11_schema.cpp @@ -23,9 +23,9 @@ #include "cu_appconfig_cli11_schema.h" #include "apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.h" #include "apps/services/logger/logger_appconfig_cli11_schema.h" +#include "apps/services/worker_manager/worker_manager_cli11_schema.h" #include "cu_appconfig.h" #include "srsran/support/cli11_utils.h" -#include "CLI/CLI11.hpp" using namespace srsran; @@ -60,6 +60,9 @@ void srsran::configure_cli11_with_cu_appconfig_schema(CLI::App& app, cu_appconfi // Buffer pool section. configure_cli11_with_buffer_pool_appconfig_schema(app, cu_cfg.buffer_pool_config); + // Expert execution section. + configure_cli11_with_worker_manager_appconfig_schema(app, cu_cfg.expert_execution_cfg); + // F1AP section. CLI::App* cu_cp_subcmd = add_subcommand(app, "cu_cp", "CU-UP parameters")->configurable(); CLI::App* f1ap_subcmd = add_subcommand(*cu_cp_subcmd, "f1ap", "F1AP parameters")->configurable(); diff --git a/apps/cu/cu_appconfig_validator.cpp b/apps/cu/cu_appconfig_validator.cpp index 1b4c7c2d5c..a33fbc3b05 100644 --- a/apps/cu/cu_appconfig_validator.cpp +++ b/apps/cu/cu_appconfig_validator.cpp @@ -28,5 +28,11 @@ using namespace srsran; bool srsran::validate_cu_appconfig(const cu_appconfig& config) { + if (config.e2_cfg.enable_du_e2) { + fmt::print("CU application cannot enable DU E2 agent\n"); + + return false; + } + return validate_logger_appconfig(config.log_cfg); } diff --git a/apps/du/CMakeLists.txt b/apps/du/CMakeLists.txt index 0b837c06c5..ba0c275b4b 100644 --- a/apps/du/CMakeLists.txt +++ b/apps/du/CMakeLists.txt @@ -31,7 +31,7 @@ install(TARGETS srsdu target_link_libraries(srsdu srsran_app_services - srsgnb_app_f1u_du_split_connector + srsran_f1u_du_split_connector srsran_cu_cp srsran_network srsran_e2 diff --git a/apps/du/du.cpp b/apps/du/du.cpp index b868a2a291..6b59bd9d8c 100644 --- a/apps/du/du.cpp +++ b/apps/du/du.cpp @@ -41,21 +41,13 @@ #include "du_appconfig_translators.h" #include "du_appconfig_validators.h" -#include "apps/services/worker_manager.h" +#include "apps/services/worker_manager/worker_manager.h" #include "apps/units/flexible_du/split_dynamic/dynamic_du_factory.h" #include "apps/du/adapters/e2_gateways.h" #include "apps/services/e2/e2_metric_connector_manager.h" #include "srsran/e2/gateways/e2_connection_client.h" -// Include ThreadSanitizer (TSAN) options if thread sanitization is enabled. -// This include is not unused - it helps prevent false alarms from the thread sanitizer. -#include "du_appconfig_yaml_writer.h" - -#include "srsran/support/tsan_options.h" - -#include - #include "apps/services/application_message_banners.h" #include "apps/services/application_tracer.h" #include "apps/services/buffer_pool/buffer_pool_manager.h" @@ -66,12 +58,15 @@ #include "apps/units/flexible_du/flexible_du_application_unit.h" #include "apps/units/flexible_du/o_du_high/du_high/du_high_config.h" #include "apps/units/flexible_du/o_du_high/du_high/pcap_factory.h" - +#include "du_appconfig_yaml_writer.h" #include "srsran/du/du_power_controller.h" - +#include #ifdef DPDK_FOUND #include "srsran/hal/dpdk/dpdk_eal_factory.h" #endif +// Include ThreadSanitizer (TSAN) options if thread sanitization is enabled. +// This include is not unused - it helps prevent false alarms from the thread sanitizer. +#include "srsran/support/tsan_options.h" using namespace srsran; diff --git a/apps/du/du_appconfig.h b/apps/du/du_appconfig.h index 147b648b5c..e7814f8048 100644 --- a/apps/du/du_appconfig.h +++ b/apps/du/du_appconfig.h @@ -22,11 +22,11 @@ #pragma once -#include "../gnb/gnb_appconfig.h" // TODO: Remove #include "apps/services/buffer_pool/buffer_pool_appconfig.h" +#include "apps/services/e2/e2_appconfig.h" +#include "apps/services/hal/hal_appconfig.h" #include "apps/services/logger/logger_appconfig.h" -#include "apps/services/os_sched_affinity_manager.h" -#include "srsran/support/executors/unique_thread.h" +#include "apps/services/worker_manager/worker_manager_appconfig.h" #include namespace srsran { diff --git a/apps/du/du_appconfig_cli11_schema.cpp b/apps/du/du_appconfig_cli11_schema.cpp index cb2ea1bc7a..a1bc0f10bb 100644 --- a/apps/du/du_appconfig_cli11_schema.cpp +++ b/apps/du/du_appconfig_cli11_schema.cpp @@ -22,25 +22,16 @@ #include "du_appconfig_cli11_schema.h" #include "apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.h" +#include "apps/services/e2/e2_cli11_schema.h" +#include "apps/services/hal/hal_cli11_schema.h" #include "apps/services/logger/logger_appconfig_cli11_schema.h" +#include "apps/services/worker_manager/worker_manager_cli11_schema.h" #include "du_appconfig.h" #include "srsran/adt/interval.h" #include "srsran/support/cli11_utils.h" using namespace srsran; -template -static expected parse_int(const std::string& value) -{ - try { - return std::stoi(value); - } catch (const std::invalid_argument& e) { - return make_unexpected(e.what()); - } catch (const std::out_of_range& e) { - return make_unexpected(e.what()); - } -} - static void configure_cli11_metrics_args(CLI::App& app, srs_du::metrics_appconfig& metrics_params) { app.add_option("--addr", metrics_params.addr, "Metrics address.")->capture_default_str()->check(CLI::ValidIPV4); @@ -49,178 +40,6 @@ static void configure_cli11_metrics_args(CLI::App& app, srs_du::metrics_appconfi ->check(CLI::Range(0, 65535)); } -static void configure_cli11_e2_args(CLI::App& app, e2_appconfig& e2_params) -{ - add_option(app, "--enable_du_e2", e2_params.enable_du_e2, "Enable DU E2 agent")->capture_default_str(); - add_option(app, "--addr", e2_params.ip_addr, "RIC IP address")->capture_default_str(); - add_option(app, "--port", e2_params.port, "RIC port")->capture_default_str()->check(CLI::Range(20000, 40000)); - add_option(app, "--bind_addr", e2_params.bind_addr, "Local IP address to bind for RIC connection") - ->capture_default_str() - ->check(CLI::ValidIPV4); - add_option(app, "--sctp_rto_initial", e2_params.sctp_rto_initial, "SCTP initial RTO value")->capture_default_str(); - add_option(app, "--sctp_rto_min", e2_params.sctp_rto_min, "SCTP RTO min")->capture_default_str(); - add_option(app, "--sctp_rto_max", e2_params.sctp_rto_max, "SCTP RTO max")->capture_default_str(); - add_option(app, "--sctp_init_max_attempts", e2_params.sctp_init_max_attempts, "SCTP init max attempts") - ->capture_default_str(); - add_option(app, "--sctp_max_init_timeo", e2_params.sctp_max_init_timeo, "SCTP max init timeout") - ->capture_default_str(); - add_option(app, "--e2sm_kpm_enabled", e2_params.e2sm_kpm_enabled, "Enable KPM service module")->capture_default_str(); - add_option(app, "--e2sm_rc_enabled", e2_params.e2sm_rc_enabled, "Enable RC service module")->capture_default_str(); -} - -static error_type is_valid_cpu_index(unsigned cpu_idx) -{ - std::string error_message = fmt::format("Invalid CPU core selected '{}'. Valid CPU ids: {}", - cpu_idx, - os_sched_affinity_bitmask::available_cpus().get_cpu_ids()); - - os_sched_affinity_bitmask one_cpu_mask; - if (cpu_idx >= one_cpu_mask.size()) { - return make_unexpected(error_message); - } - one_cpu_mask.set(cpu_idx); - if (not one_cpu_mask.subtract(os_sched_affinity_bitmask::available_cpus()).empty()) { - return make_unexpected(error_message); - } - return default_success_t(); -} - -static expected parse_one_cpu(const std::string& value) -{ - expected result = parse_int(value); - - if (not result.has_value()) { - return make_unexpected(fmt::format("Could not parse '{}' string as a CPU index", value)); - } - - error_type validation_result = is_valid_cpu_index(result.value()); - if (not validation_result.has_value()) { - return make_unexpected(validation_result.error()); - } - - return result.value(); -} - -static expected, std::string> parse_cpu_range(const std::string& value) -{ - std::vector range; - std::stringstream ss(value); - while (ss.good()) { - std::string str; - getline(ss, str, '-'); - auto parse_result = parse_one_cpu(str); - if (not parse_result.has_value()) { - return make_unexpected(fmt::format("{}. Could not parse '{}' as a range", parse_result.error(), value)); - } - - range.push_back(parse_result.value()); - } - - // A range is defined by two numbers. - if (range.size() != 2) { - return make_unexpected(fmt::format("Could not parse '{}' as a range", value)); - } - - if (range[1] <= range[0]) { - return make_unexpected(fmt::format("Invalid CPU core range detected [{}-{}]", range[0], range[1])); - } - - return interval(range[0], range[1]); -} - -static void -parse_affinity_mask(os_sched_affinity_bitmask& mask, const std::string& value, const std::string& property_name) -{ - std::stringstream ss(value); - - while (ss.good()) { - std::string str; - getline(ss, str, ','); - if (str.find('-') != std::string::npos) { - auto range = parse_cpu_range(str); - if (not range.has_value()) { - report_error("{} in the '{}' property", range.error(), property_name); - } - - // Add 1 to the stop value as the fill method excludes the end position. - mask.fill(range.value().start(), range.value().stop() + 1); - } else { - auto cpu_idx = parse_one_cpu(str); - if (not cpu_idx.has_value()) { - report_error("{} in the '{}' property", cpu_idx.error(), property_name); - } - - mask.set(cpu_idx.value()); - } - } -} - -static void configure_cli11_cpu_affinities_args(CLI::App& app, cpu_affinities_appconfig& config) -{ - auto parsing_isolated_cpus_fcn = [](std::optional& isolated_cpu_cfg, - const std::string& value, - const std::string& property_name) { - isolated_cpu_cfg.emplace(); - parse_affinity_mask(*isolated_cpu_cfg, value, property_name); - - if (isolated_cpu_cfg->all()) { - report_error("Error in '{}' property: can not assign all available CPUs to the gNB app", property_name); - } - }; - - add_option_function( - app, - "--isolated_cpus", - [&config, &parsing_isolated_cpus_fcn](const std::string& value) { - parsing_isolated_cpus_fcn(config.isolated_cpus, value, "isolated_cpus"); - }, - "CPU cores isolated for gNB application"); - - add_option_function( - app, - "--low_priority_cpus", - [&config](const std::string& value) { - parse_affinity_mask(config.low_priority_cpu_cfg.mask, value, "low_priority_cpus"); - }, - "CPU cores assigned to low priority tasks"); - - add_option_function( - app, - "--low_priority_pinning", - [&config](const std::string& value) { - config.low_priority_cpu_cfg.pinning_policy = to_affinity_mask_policy(value); - if (config.low_priority_cpu_cfg.pinning_policy == sched_affinity_mask_policy::last) { - report_error("Incorrect value={} used in {} property", value, "low_priority_pinning"); - } - }, - "Policy used for assigning CPU cores to low priority tasks"); -} - -static void configure_cli11_non_rt_threads_args(CLI::App& app, non_rt_threads_appconfig& config) -{ - add_option(app, - "--nof_non_rt_threads", - config.nof_non_rt_threads, - "Number of non real time threads for processing of CP and UP data in upper layers.") - ->capture_default_str() - ->check(CLI::Number); -} - -static void configure_cli11_expert_execution_args(CLI::App& app, expert_execution_appconfig& config) -{ - // Affinity section. - CLI::App* affinities_subcmd = add_subcommand(app, "affinities", "gNB CPU affinities configuration")->configurable(); - configure_cli11_cpu_affinities_args(*affinities_subcmd, config.affinities); - - // Threads section. - CLI::App* threads_subcmd = add_subcommand(app, "threads", "Threads configuration")->configurable(); - - // Non real time threads. - CLI::App* non_rt_threads_subcmd = - add_subcommand(*threads_subcmd, "non_rt", "Non real time thread configuration")->configurable(); - configure_cli11_non_rt_threads_args(*non_rt_threads_subcmd, config.threads.non_rt_threads); -} - static void configure_cli11_f1ap_args(CLI::App& app, srs_du::f1ap_appconfig& f1c_params) { app.add_option("--cu_cp_addr", f1c_params.cu_cp_address, "CU-CP F1-C address to connect to")->capture_default_str(); @@ -243,13 +62,6 @@ static void configure_cli11_f1u_args(CLI::App& app, srs_du::nru_appconfig& f1u_p ->capture_default_str(); } -static void configure_cli11_hal_args(CLI::App& app, std::optional& config) -{ - config.emplace(); - - add_option(app, "--eal_args", config->eal_args, "EAL configuration parameters used to initialize DPDK"); -} - void srsran::configure_cli11_with_du_appconfig_schema(CLI::App& app, du_appconfig& du_cfg) { add_option(app, "--du_multicell_enabled", du_cfg.du_multicell_enabled, "DU multicell enabled flag") @@ -261,6 +73,9 @@ void srsran::configure_cli11_with_du_appconfig_schema(CLI::App& app, du_appconfi // Buffer pool section. configure_cli11_with_buffer_pool_appconfig_schema(app, du_cfg.buffer_pool_config); + // Expert execution section. + configure_cli11_with_worker_manager_appconfig_schema(app, du_cfg.expert_execution_cfg); + // F1-C section. CLI::App* f1ap_subcmd = app.add_subcommand("f1ap", "F1AP interface configuration")->configurable(); configure_cli11_f1ap_args(*f1ap_subcmd, du_cfg.f1ap_cfg); @@ -274,16 +89,21 @@ void srsran::configure_cli11_with_du_appconfig_schema(CLI::App& app, du_appconfi configure_cli11_metrics_args(*metrics_subcmd, du_cfg.metrics_cfg); // E2 section. - CLI::App* e2_subcmd = add_subcommand(app, "e2", "E2 parameters")->configurable(); - configure_cli11_e2_args(*e2_subcmd, du_cfg.e2_cfg); - - // Expert section. - CLI::App* expert_subcmd = app.add_subcommand("expert_execution", "Expert execution configuration")->configurable(); - configure_cli11_expert_execution_args(*expert_subcmd, du_cfg.expert_execution_cfg); + configure_cli11_with_e2_appconfig_schema(app, du_cfg.e2_cfg); // HAL section. - CLI::App* hal_subcmd = add_subcommand(app, "hal", "HAL configuration")->configurable(); - configure_cli11_hal_args(*hal_subcmd, du_cfg.hal_config); + du_cfg.hal_config.emplace(); + configure_cli11_with_hal_appconfig_schema(app, *du_cfg.hal_config); +} + +static void manage_hal_optional(CLI::App& app, du_appconfig& du_cfg) +{ + if (!is_hal_section_present(app)) { + du_cfg.hal_config.reset(); + } } -void srsran::autoderive_du_parameters_after_parsing(CLI::App& app, du_appconfig& parsed_cfg) {} +void srsran::autoderive_du_parameters_after_parsing(CLI::App& app, du_appconfig& du_cfg) +{ + manage_hal_optional(app, du_cfg); +} diff --git a/apps/du/du_appconfig_cli11_schema.h b/apps/du/du_appconfig_cli11_schema.h index 64fa01ca41..d3bc1f0542 100644 --- a/apps/du/du_appconfig_cli11_schema.h +++ b/apps/du/du_appconfig_cli11_schema.h @@ -32,6 +32,6 @@ struct du_appconfig; void configure_cli11_with_du_appconfig_schema(CLI::App& app, du_appconfig& parsed_cfg); /// Auto derive DU parameters after the parsing. -void autoderive_du_parameters_after_parsing(CLI::App& app, du_appconfig& parsed_cfg); +void autoderive_du_parameters_after_parsing(CLI::App& app, du_appconfig& du_cfg); } // namespace srsran diff --git a/apps/du/du_appconfig_translators.cpp b/apps/du/du_appconfig_translators.cpp index 2660cecf5d..cd6b99eec5 100644 --- a/apps/du/du_appconfig_translators.cpp +++ b/apps/du/du_appconfig_translators.cpp @@ -21,47 +21,18 @@ */ #include "du_appconfig_translators.h" -#include "apps/services/worker_manager_config.h" +#include "apps/services/worker_manager/worker_manager_config.h" #include "du_appconfig.h" using namespace srsran; using namespace std::chrono_literals; -// TODO: refactor. -srsran::sctp_network_connector_config srsran::generate_e2ap_nw_config(const e2_appconfig& config, int ppid) -{ - srsran::sctp_network_connector_config out_cfg; - out_cfg.dest_name = "NearRT-RIC"; - out_cfg.if_name = "E2"; - out_cfg.connect_address = config.ip_addr; - out_cfg.connect_port = config.port; - out_cfg.bind_address = config.bind_addr; - out_cfg.ppid = ppid; - - if (config.sctp_rto_initial >= 0) { - out_cfg.rto_initial = config.sctp_rto_initial; - } - if (config.sctp_rto_min >= 0) { - out_cfg.rto_min = config.sctp_rto_min; - } - if (config.sctp_rto_max >= 0) { - out_cfg.rto_max = config.sctp_rto_max; - } - if (config.sctp_init_max_attempts >= 0) { - out_cfg.init_max_attempts = config.sctp_init_max_attempts; - } - if (config.sctp_max_init_timeo >= 0) { - out_cfg.max_init_timeo = config.sctp_max_init_timeo; - } - - return out_cfg; -} - void srsran::fill_du_worker_manager_config(worker_manager_config& config, const du_appconfig& unit_cfg) { srsran_assert(config.du_hi_cfg, "DU high worker config does not exist"); - config.du_hi_cfg->pdu_queue_size = unit_cfg.nru_cfg.pdu_queue_size; - config.nof_low_prio_threads = unit_cfg.expert_execution_cfg.threads.non_rt_threads.nof_non_rt_threads; - config.low_prio_sched_config = unit_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg; + config.du_hi_cfg->pdu_queue_size = unit_cfg.nru_cfg.pdu_queue_size; + config.du_hi_cfg->is_du_multicell_enabled = unit_cfg.du_multicell_enabled; + config.nof_low_prio_threads = unit_cfg.expert_execution_cfg.threads.non_rt_threads.nof_non_rt_threads; + config.low_prio_sched_config = unit_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg; } diff --git a/apps/du/du_appconfig_translators.h b/apps/du/du_appconfig_translators.h index d05400f28a..5cbf7182a7 100644 --- a/apps/du/du_appconfig_translators.h +++ b/apps/du/du_appconfig_translators.h @@ -22,17 +22,10 @@ #pragma once -#include "srsran/e2/e2ap_configuration.h" -#include "srsran/gateways/sctp_network_gateway.h" - namespace srsran { struct du_appconfig; struct worker_manager_config; -struct e2_appconfig; - -/// Converts and returns the given gnb application configuration to a E2AP Network Gateway configuration. -sctp_network_connector_config generate_e2ap_nw_config(const e2_appconfig& config, int ppid); /// Fills the DU worker manager parameters of the given worker manager configuration. void fill_du_worker_manager_config(worker_manager_config& config, const du_appconfig& unit_cfg); diff --git a/apps/du/du_appconfig_validators.cpp b/apps/du/du_appconfig_validators.cpp index bdecf9ef89..84408b4821 100644 --- a/apps/du/du_appconfig_validators.cpp +++ b/apps/du/du_appconfig_validators.cpp @@ -36,5 +36,11 @@ bool srsran::validate_appconfig(const du_appconfig& config) return false; } + if (config.e2_cfg.enable_cu_e2) { + fmt::print("DU application cannot enable CU E2 agent\n"); + + return false; + } + return true; } diff --git a/apps/gnb/CMakeLists.txt b/apps/gnb/CMakeLists.txt index 1d33d150dc..5d16351cb4 100644 --- a/apps/gnb/CMakeLists.txt +++ b/apps/gnb/CMakeLists.txt @@ -31,7 +31,7 @@ install(TARGETS gnb target_link_libraries(gnb srsran_app_services - srsgnb_app_f1u_connector + srsran_f1u_connector srsran_cu_cp srsran_network srsran_ngap diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp index eb68f57464..223a7c251c 100644 --- a/apps/gnb/gnb.cpp +++ b/apps/gnb/gnb.cpp @@ -20,29 +20,8 @@ * */ -#include "srsran/support/backtrace.h" -#include "srsran/support/config_parsers.h" -#include "srsran/support/cpu_features.h" -#include "srsran/support/dynlink_manager.h" -#include "srsran/support/io/io_broker_factory.h" -#include "srsran/support/signal_handling.h" -#include "srsran/support/tracing/event_tracing.h" -#include "srsran/support/versioning/build_info.h" -#include "srsran/support/versioning/version.h" - -#include "srsran/du/du_power_controller.h" -#include "srsran/e1ap/gateways/e1_local_connector_factory.h" -#include "srsran/f1ap/gateways/f1c_local_connector_factory.h" -#include "srsran/f1u/local_connector/f1u_local_connector.h" -#include "srsran/gtpu/ngu_gateway.h" -#include "srsran/ngap/gateways/n2_connection_client_factory.h" - -#include "gnb_appconfig.h" -#include "gnb_appconfig_cli11_schema.h" -#include "gnb_appconfig_translators.h" -#include "gnb_appconfig_validators.h" -#include "gnb_appconfig_yaml_writer.h" - +#include "apps/cu/adapters/e2_gateways.h" +#include "apps/du/adapters/e2_gateways.h" #include "apps/services/application_message_banners.h" #include "apps/services/application_tracer.h" #include "apps/services/buffer_pool/buffer_pool_manager.h" @@ -51,16 +30,7 @@ #include "apps/services/metrics/metrics_manager.h" #include "apps/services/metrics/metrics_notifier_proxy.h" #include "apps/services/stdin_command_dispatcher.h" -#include "apps/services/worker_manager.h" - -#include "apps/cu/adapters/e2_gateways.h" -#include "apps/du/adapters/e2_gateways.h" -#include "apps/services/e2/e2_metric_connector_manager.h" - -// Include ThreadSanitizer (TSAN) options if thread sanitization is enabled. -// This include is not unused - it helps prevent false alarms from the thread sanitizer. -#include "srsran/support/tsan_options.h" - +#include "apps/services/worker_manager/worker_manager.h" #include "apps/units/cu_cp/cu_cp_application_unit.h" #include "apps/units/cu_cp/cu_cp_config_translators.h" #include "apps/units/cu_cp/cu_cp_unit_config.h" @@ -71,13 +41,34 @@ #include "apps/units/flexible_du/flexible_du_application_unit.h" #include "apps/units/flexible_du/o_du_high/du_high/du_high_config.h" #include "apps/units/flexible_du/o_du_high/du_high/pcap_factory.h" - +#include "gnb_appconfig.h" +#include "gnb_appconfig_cli11_schema.h" +#include "gnb_appconfig_translators.h" +#include "gnb_appconfig_validators.h" +#include "gnb_appconfig_yaml_writer.h" +#include "srsran/du/du_power_controller.h" +#include "srsran/e1ap/gateways/e1_local_connector_factory.h" +#include "srsran/f1ap/gateways/f1c_local_connector_factory.h" +#include "srsran/f1u/local_connector/f1u_local_connector.h" +#include "srsran/gtpu/ngu_gateway.h" +#include "srsran/ngap/gateways/n2_connection_client_factory.h" +#include "srsran/support/backtrace.h" +#include "srsran/support/config_parsers.h" +#include "srsran/support/cpu_features.h" +#include "srsran/support/dynlink_manager.h" +#include "srsran/support/io/io_broker_factory.h" +#include "srsran/support/signal_handling.h" +#include "srsran/support/tracing/event_tracing.h" +#include "srsran/support/versioning/build_info.h" +#include "srsran/support/versioning/version.h" #include #include - #ifdef DPDK_FOUND #include "srsran/hal/dpdk/dpdk_eal_factory.h" #endif +// Include ThreadSanitizer (TSAN) options if thread sanitization is enabled. +// This include is not unused - it helps prevent false alarms from the thread sanitizer. +#include "srsran/support/tsan_options.h" using namespace srsran; @@ -173,8 +164,9 @@ static void autoderive_slicing_args(du_high_unit_config& du_hi_cfg, cu_cp_unit_c std::vector du_slices; for (const auto& cell_cfg : du_hi_cfg.cells_cfg) { for (const auto& slice : cell_cfg.cell.slice_cfg) { - if (du_slices.end() == std::find(du_slices.begin(), du_slices.end(), slice.s_nssai)) { - du_slices.push_back(slice.s_nssai); + s_nssai_t nssai{slice_service_type{slice.sst}, slice_differentiator::create(slice.sd).value()}; + if (du_slices.end() == std::find(du_slices.begin(), du_slices.end(), nssai)) { + du_slices.push_back(nssai); } } } @@ -241,7 +233,8 @@ int main(int argc, char** argv) // If test mode is enabled, we auto-enable "no_core" option and generate a amf config with no core. if (du_app_unit->get_du_high_unit_config().is_testmode_enabled()) { cu_cp_app_unit->get_cu_cp_unit_config().amf_config.no_core = true; - cu_cp_app_unit->get_cu_cp_unit_config().amf_config.amf.supported_tas = {{7, {{"00101", {s_nssai_t{1}}}}}}; + cu_cp_app_unit->get_cu_cp_unit_config().amf_config.amf.supported_tas = { + {7, {{"00101", {cu_cp_unit_plmn_item::tai_slice_t{1}}}}}}; } cu_cp_app_unit->on_configuration_parameters_autoderivation(app); diff --git a/apps/gnb/gnb_appconfig.h b/apps/gnb/gnb_appconfig.h index 7e6b787fa0..8e8935dbc1 100644 --- a/apps/gnb/gnb_appconfig.h +++ b/apps/gnb/gnb_appconfig.h @@ -24,10 +24,10 @@ #include "apps/services/buffer_pool/buffer_pool_appconfig.h" #include "apps/services/e2/e2_appconfig.h" +#include "apps/services/hal/hal_appconfig.h" #include "apps/services/logger/logger_appconfig.h" -#include "apps/services/os_sched_affinity_manager.h" +#include "apps/services/worker_manager/worker_manager_appconfig.h" #include "srsran/ran/gnb_id.h" -#include "srsran/support/executors/unique_thread.h" #include namespace srsran { @@ -44,42 +44,6 @@ struct metrics_appconfig { uint16_t port = 55555; }; -/// CPU affinities configuration for the gNB app. -struct cpu_affinities_appconfig { - /// CPUs isolation. - std::optional isolated_cpus; - /// Low priority workers CPU affinity mask. - os_sched_affinity_config low_priority_cpu_cfg = {sched_affinity_mask_types::low_priority, - {}, - sched_affinity_mask_policy::mask}; -}; - -/// Non real time thread configuration for the gNB. -struct non_rt_threads_appconfig { - /// Number of non real time threads for processing of CP and UP data in the upper layers - unsigned nof_non_rt_threads = 4; -}; - -/// Expert threads configuration of the gNB app. -struct expert_threads_appconfig { - /// Non real time thread configuration of the gNB app. - non_rt_threads_appconfig non_rt_threads; -}; - -/// Expert configuration of the gNB app. -struct expert_execution_appconfig { - /// gNB CPU affinities. - cpu_affinities_appconfig affinities; - /// Expert thread configuration of the gNB app. - expert_threads_appconfig threads; -}; - -/// HAL configuration of the gNB app. -struct hal_appconfig { - /// EAL configuration arguments. - std::string eal_args; -}; - /// Monolithic gnb application configuration. struct gnb_appconfig { /// Default constructor to update the log filename. diff --git a/apps/gnb/gnb_appconfig_cli11_schema.cpp b/apps/gnb/gnb_appconfig_cli11_schema.cpp index 8857fef1ac..c03fb871b5 100644 --- a/apps/gnb/gnb_appconfig_cli11_schema.cpp +++ b/apps/gnb/gnb_appconfig_cli11_schema.cpp @@ -22,27 +22,16 @@ #include "gnb_appconfig_cli11_schema.h" #include "apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.h" +#include "apps/services/e2/e2_cli11_schema.h" +#include "apps/services/hal/hal_cli11_schema.h" #include "apps/services/logger/logger_appconfig_cli11_schema.h" +#include "apps/services/worker_manager/worker_manager_cli11_schema.h" #include "gnb_appconfig.h" -#include "srsran/adt/interval.h" #include "srsran/support/cli11_utils.h" -#include "srsran/support/error_handling.h" #include "CLI/CLI11.hpp" using namespace srsran; -template -static expected parse_int(const std::string& value) -{ - try { - return std::stoi(value); - } catch (const std::invalid_argument& e) { - return make_unexpected(e.what()); - } catch (const std::out_of_range& e) { - return make_unexpected(e.what()); - } -} - static void configure_cli11_metrics_args(CLI::App& app, metrics_appconfig& metrics_params) { app.add_option("--addr", metrics_params.addr, "Metrics address.")->capture_default_str(); @@ -51,193 +40,10 @@ static void configure_cli11_metrics_args(CLI::App& app, metrics_appconfig& metri ->check(CLI::Range(0, 65535)); } -static void configure_cli11_e2_args(CLI::App& app, e2_appconfig& e2_params) -{ - add_option(app, "--enable_du_e2", e2_params.enable_du_e2, "Enable DU E2 agent")->capture_default_str(); - add_option(app, "--enable_cu_e2", e2_params.enable_cu_e2, "Enable CU E2 agent")->capture_default_str(); - add_option(app, "--addr", e2_params.ip_addr, "RIC IP address")->capture_default_str(); - add_option(app, "--port", e2_params.port, "RIC port")->check(CLI::Range(20000, 40000))->capture_default_str(); - add_option(app, "--bind_addr", e2_params.bind_addr, "Local IP address to bind for RIC connection") - ->capture_default_str() - ->check(CLI::ValidIPV4); - add_option(app, "--sctp_rto_initial", e2_params.sctp_rto_initial, "SCTP initial RTO value")->capture_default_str(); - add_option(app, "--sctp_rto_min", e2_params.sctp_rto_min, "SCTP RTO min")->capture_default_str(); - add_option(app, "--sctp_rto_max", e2_params.sctp_rto_max, "SCTP RTO max")->capture_default_str(); - add_option(app, "--sctp_init_max_attempts", e2_params.sctp_init_max_attempts, "SCTP init max attempts") - ->capture_default_str(); - add_option(app, "--sctp_max_init_timeo", e2_params.sctp_max_init_timeo, "SCTP max init timeout") - ->capture_default_str(); - add_option(app, "--e2sm_kpm_enabled", e2_params.e2sm_kpm_enabled, "Enable KPM service module")->capture_default_str(); - add_option(app, "--e2sm_rc_enabled", e2_params.e2sm_rc_enabled, "Enable RC service module")->capture_default_str(); -} - -static void configure_cli11_hal_args(CLI::App& app, std::optional& config) -{ - config.emplace(); - - add_option(app, "--eal_args", config->eal_args, "EAL configuration parameters used to initialize DPDK"); -} - -static error_type is_valid_cpu_index(unsigned cpu_idx) -{ - std::string error_message = fmt::format("Invalid CPU core selected '{}'. Valid CPU ids: {}", - cpu_idx, - os_sched_affinity_bitmask::available_cpus().get_cpu_ids()); - - os_sched_affinity_bitmask one_cpu_mask; - if (cpu_idx >= one_cpu_mask.size()) { - return make_unexpected(error_message); - } - one_cpu_mask.set(cpu_idx); - if (not one_cpu_mask.subtract(os_sched_affinity_bitmask::available_cpus()).empty()) { - return make_unexpected(error_message); - } - return default_success_t(); -} - -static expected parse_one_cpu(const std::string& value) -{ - expected result = parse_int(value); - - if (not result.has_value()) { - return make_unexpected(fmt::format("Could not parse '{}' string as a CPU index", value)); - } - - error_type validation_result = is_valid_cpu_index(result.value()); - if (not validation_result.has_value()) { - return make_unexpected(validation_result.error()); - } - - return result.value(); -} - -static expected, std::string> parse_cpu_range(const std::string& value) -{ - std::vector range; - std::stringstream ss(value); - while (ss.good()) { - std::string str; - getline(ss, str, '-'); - auto parse_result = parse_one_cpu(str); - if (not parse_result.has_value()) { - return make_unexpected(fmt::format("{}. Could not parse '{}' as a range", parse_result.error(), value)); - } - - range.push_back(parse_result.value()); - } - - // A range is defined by two numbers. - if (range.size() != 2) { - return make_unexpected(fmt::format("Could not parse '{}' as a range", value)); - } - - if (range[1] <= range[0]) { - return make_unexpected(fmt::format("Invalid CPU core range detected [{}-{}]", range[0], range[1])); - } - - return interval(range[0], range[1]); -} - -static void -parse_affinity_mask(os_sched_affinity_bitmask& mask, const std::string& value, const std::string& property_name) -{ - std::stringstream ss(value); - - while (ss.good()) { - std::string str; - getline(ss, str, ','); - if (str.find('-') != std::string::npos) { - auto range = parse_cpu_range(str); - if (not range.has_value()) { - report_error("{} in the '{}' property", range.error(), property_name); - } - - // Add 1 to the stop value as the fill method excludes the end position. - mask.fill(range.value().start(), range.value().stop() + 1); - } else { - auto cpu_idx = parse_one_cpu(str); - if (not cpu_idx.has_value()) { - report_error("{} in the '{}' property", cpu_idx.error(), property_name); - } - - mask.set(cpu_idx.value()); - } - } -} - -static void configure_cli11_non_rt_threads_args(CLI::App& app, non_rt_threads_appconfig& config) -{ - add_option(app, - "--nof_non_rt_threads", - config.nof_non_rt_threads, - "Number of non real time threads for processing of CP and UP data in upper layers.") - ->capture_default_str() - ->check(CLI::Number); -} - -static void configure_cli11_cpu_affinities_args(CLI::App& app, cpu_affinities_appconfig& config) -{ - auto parsing_isolated_cpus_fcn = [](std::optional& isolated_cpu_cfg, - const std::string& value, - const std::string& property_name) { - isolated_cpu_cfg.emplace(); - parse_affinity_mask(*isolated_cpu_cfg, value, property_name); - - if (isolated_cpu_cfg->all()) { - report_error("Error in '{}' property: can not assign all available CPUs to the gNB app", property_name); - } - }; - - add_option_function( - app, - "--isolated_cpus", - [&config, &parsing_isolated_cpus_fcn](const std::string& value) { - parsing_isolated_cpus_fcn(config.isolated_cpus, value, "isolated_cpus"); - }, - "CPU cores isolated for gNB application"); - - add_option_function( - app, - "--low_priority_cpus", - [&config](const std::string& value) { - parse_affinity_mask(config.low_priority_cpu_cfg.mask, value, "low_priority_cpus"); - }, - "CPU cores assigned to low priority tasks"); - - add_option_function( - app, - "--low_priority_pinning", - [&config](const std::string& value) { - config.low_priority_cpu_cfg.pinning_policy = to_affinity_mask_policy(value); - if (config.low_priority_cpu_cfg.pinning_policy == sched_affinity_mask_policy::last) { - report_error("Incorrect value={} used in {} property", value, "low_priority_pinning"); - } - }, - "Policy used for assigning CPU cores to low priority tasks"); -} - -static void configure_cli11_expert_execution_args(CLI::App& app, expert_execution_appconfig& config) -{ - // Affinity section. - CLI::App* affinities_subcmd = add_subcommand(app, "affinities", "gNB CPU affinities configuration")->configurable(); - configure_cli11_cpu_affinities_args(*affinities_subcmd, config.affinities); - - // Threads section. - CLI::App* threads_subcmd = add_subcommand(app, "threads", "Threads configuration")->configurable(); - - // Non real time threads. - CLI::App* non_rt_threads_subcmd = - add_subcommand(*threads_subcmd, "non_rt", "Non real time thread configuration")->configurable(); - configure_cli11_non_rt_threads_args(*non_rt_threads_subcmd, config.threads.non_rt_threads); -} - static void manage_hal_optional(CLI::App& app, gnb_appconfig& gnb_cfg) { - // Clean the HAL optional. - if (auto subcmd = app.get_subcommand("hal"); subcmd->count_all() == 0) { + if (!is_hal_section_present(app)) { gnb_cfg.hal_config.reset(); - // As HAL configuration is optional, disable the command when it is not present in the configuration. - subcmd->disabled(); } } @@ -259,21 +65,19 @@ void srsran::configure_cli11_with_gnb_appconfig_schema(CLI::App& app, gnb_appcon // Buffer pool section. configure_cli11_with_buffer_pool_appconfig_schema(app, gnb_cfg.buffer_pool_config); + // Expert execution section. + configure_cli11_with_worker_manager_appconfig_schema(app, gnb_cfg.expert_execution_cfg); + // Metrics section. CLI::App* metrics_subcmd = app.add_subcommand("metrics", "Metrics configuration")->configurable(); configure_cli11_metrics_args(*metrics_subcmd, gnb_cfg.metrics_cfg); // E2 section. - CLI::App* e2_subcmd = add_subcommand(app, "e2", "E2 parameters")->configurable(); - configure_cli11_e2_args(*e2_subcmd, gnb_cfg.e2_cfg); - - // Expert section. - CLI::App* expert_subcmd = app.add_subcommand("expert_execution", "Expert execution configuration")->configurable(); - configure_cli11_expert_execution_args(*expert_subcmd, gnb_cfg.expert_execution_cfg); + configure_cli11_with_e2_appconfig_schema(app, gnb_cfg.e2_cfg); // HAL section. - CLI::App* hal_subcmd = add_subcommand(app, "hal", "HAL configuration")->configurable(); - configure_cli11_hal_args(*hal_subcmd, gnb_cfg.hal_config); + gnb_cfg.hal_config.emplace(); + configure_cli11_with_hal_appconfig_schema(app, *gnb_cfg.hal_config); } void srsran::autoderive_gnb_parameters_after_parsing(CLI::App& app, gnb_appconfig& config) diff --git a/apps/gnb/gnb_appconfig_translators.cpp b/apps/gnb/gnb_appconfig_translators.cpp index a905319cd9..0fa612b70e 100644 --- a/apps/gnb/gnb_appconfig_translators.cpp +++ b/apps/gnb/gnb_appconfig_translators.cpp @@ -21,49 +21,19 @@ */ #include "gnb_appconfig_translators.h" -#include "apps/services/worker_manager_config.h" -#include "apps/units/cu_cp/cu_cp_unit_config.h" +#include "apps/services/worker_manager/worker_manager_config.h" #include "gnb_appconfig.h" -#include "srsran/ran/subcarrier_spacing.h" using namespace srsran; using namespace std::chrono_literals; -srsran::sctp_network_connector_config srsran::generate_e2ap_nw_config(const e2_appconfig& config, int ppid) -{ - srsran::sctp_network_connector_config out_cfg; - out_cfg.dest_name = "NearRT-RIC"; - out_cfg.if_name = "E2"; - out_cfg.connect_address = config.ip_addr; - out_cfg.connect_port = config.port; - out_cfg.bind_address = config.bind_addr; - out_cfg.ppid = ppid; - - if (config.sctp_rto_initial >= 0) { - out_cfg.rto_initial = config.sctp_rto_initial; - } - if (config.sctp_rto_min >= 0) { - out_cfg.rto_min = config.sctp_rto_min; - } - if (config.sctp_rto_max >= 0) { - out_cfg.rto_max = config.sctp_rto_max; - } - if (config.sctp_init_max_attempts >= 0) { - out_cfg.init_max_attempts = config.sctp_init_max_attempts; - } - if (config.sctp_max_init_timeo >= 0) { - out_cfg.max_init_timeo = config.sctp_max_init_timeo; - } - - return out_cfg; -} - void srsran::fill_gnb_worker_manager_config(worker_manager_config& config, const gnb_appconfig& unit_cfg) { srsran_assert(config.cu_up_cfg, "CU-UP worker config does not exist"); srsran_assert(config.du_hi_cfg, "DU high worker config does not exist"); - config.du_hi_cfg->pdu_queue_size = config.cu_up_cfg->gtpu_queue_size; - config.nof_low_prio_threads = unit_cfg.expert_execution_cfg.threads.non_rt_threads.nof_non_rt_threads; - config.low_prio_sched_config = unit_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg; + config.du_hi_cfg->pdu_queue_size = config.cu_up_cfg->gtpu_queue_size; + config.du_hi_cfg->is_du_multicell_enabled = unit_cfg.du_multicell_enabled; + config.nof_low_prio_threads = unit_cfg.expert_execution_cfg.threads.non_rt_threads.nof_non_rt_threads; + config.low_prio_sched_config = unit_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg; } diff --git a/apps/gnb/gnb_appconfig_translators.h b/apps/gnb/gnb_appconfig_translators.h index 7073d4ad3c..1251337414 100644 --- a/apps/gnb/gnb_appconfig_translators.h +++ b/apps/gnb/gnb_appconfig_translators.h @@ -22,39 +22,11 @@ #pragma once -#include "apps/services/e2/e2_appconfig.h" -#include "srsran/cu_cp/cu_cp_configuration.h" -#include "srsran/cu_up/cu_up_configuration.h" -#include "srsran/du/du_cell_config.h" -#include "srsran/du/du_high/du_qos_config.h" -#include "srsran/du/du_high/du_srb_config.h" -#include "srsran/gateways/sctp_network_gateway.h" -#include "srsran/mac/mac_config.h" -#include "srsran/phy/upper/upper_phy_factories.h" -#include "srsran/ru/ru_configuration.h" -#include -#include - namespace srsran { struct gnb_appconfig; -struct cu_cp_unit_amf_config; -struct cu_cp_unit_config; -struct cu_up_unit_config; -struct du_high_unit_config; -struct du_high_unit_cell_config; -struct du_low_unit_config; -struct dynamic_du_unit_config; -struct mac_lc_appconfig; -struct rlc_am_appconfig; struct worker_manager_config; -/// Converts and returns the subcarrier spacing. -subcarrier_spacing generate_subcarrier_spacing(unsigned sc_spacing); - -/// Converts and returns the given gnb application configuration to a E2AP Network Gateway configuration. -srsran::sctp_network_connector_config generate_e2ap_nw_config(const e2_appconfig& config, int ppid); - /// Fills the gNB worker manager parameters of the given worker manager configuration. void fill_gnb_worker_manager_config(worker_manager_config& config, const gnb_appconfig& unit_cfg); diff --git a/apps/services/CMakeLists.txt b/apps/services/CMakeLists.txt index cf2575acf6..3d03b7ffa5 100644 --- a/apps/services/CMakeLists.txt +++ b/apps/services/CMakeLists.txt @@ -19,15 +19,19 @@ # add_subdirectory(buffer_pool) +add_subdirectory(e2) +add_subdirectory(hal) add_subdirectory(logger) +add_subdirectory(worker_manager) set(SOURCES - stdin_command_dispatcher.cpp - worker_manager.cpp) + stdin_command_dispatcher.cpp) add_library(srsran_app_services STATIC ${SOURCES}) target_include_directories(srsran_app_services PRIVATE ${CMAKE_SOURCE_DIR}) target_link_libraries(srsran_app_services - srsran_logger_app_service srsran_buffer_pool_app_service - srsran_du_high) # TODO: Temp + srsran_e2_app_service + srsran_hal_app_service + srsran_logger_app_service + srsran_worker_manager_app_service) diff --git a/apps/services/e2/CMakeLists.txt b/apps/services/e2/CMakeLists.txt new file mode 100644 index 0000000000..fdde5f7083 --- /dev/null +++ b/apps/services/e2/CMakeLists.txt @@ -0,0 +1,26 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(SOURCES + e2_appconfig_translators.cpp + e2_cli11_schema.cpp) + +add_library(srsran_e2_app_service STATIC ${SOURCES}) +target_include_directories(srsran_e2_app_service PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/apps/services/e2/e2_appconfig_translators.cpp b/apps/services/e2/e2_appconfig_translators.cpp new file mode 100644 index 0000000000..4cc031fb27 --- /dev/null +++ b/apps/services/e2/e2_appconfig_translators.cpp @@ -0,0 +1,55 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "e2_appconfig_translators.h" +#include "e2_appconfig.h" + +using namespace srsran; + +sctp_network_connector_config srsran::generate_e2ap_nw_config(const e2_appconfig& config, int ppid) +{ + sctp_network_connector_config out_cfg; + out_cfg.dest_name = "NearRT-RIC"; + out_cfg.if_name = "E2"; + out_cfg.connect_address = config.ip_addr; + out_cfg.connect_port = config.port; + out_cfg.bind_address = config.bind_addr; + out_cfg.ppid = ppid; + + if (config.sctp_rto_initial >= 0) { + out_cfg.rto_initial = config.sctp_rto_initial; + } + if (config.sctp_rto_min >= 0) { + out_cfg.rto_min = config.sctp_rto_min; + } + if (config.sctp_rto_max >= 0) { + out_cfg.rto_max = config.sctp_rto_max; + } + if (config.sctp_init_max_attempts >= 0) { + out_cfg.init_max_attempts = config.sctp_init_max_attempts; + } + if (config.sctp_max_init_timeo >= 0) { + out_cfg.max_init_timeo = config.sctp_max_init_timeo; + } + + return out_cfg; +} diff --git a/lib/srsvec/aligned_vec.cpp b/apps/services/e2/e2_appconfig_translators.h similarity index 64% rename from lib/srsvec/aligned_vec.cpp rename to apps/services/e2/e2_appconfig_translators.h index 5621eedee2..1030a59a27 100644 --- a/lib/srsvec/aligned_vec.cpp +++ b/apps/services/e2/e2_appconfig_translators.h @@ -20,20 +20,15 @@ * */ -#include "srsran/srsvec/aligned_vec.h" -#include "srsran/srsvec/simd.h" -#include "srsran/support/error_handling.h" -#include +#pragma once -void* srsran::srsvec::detail::mem_alloc(std::size_t size) -{ - void* ptr = nullptr; - int ret = ::posix_memalign(&ptr, SIMD_BYTE_ALIGN * 8, size); - report_fatal_error_if_not(ret == 0 && ptr, "Failed posix_memalign."); - return ptr; -} +#include "srsran/gateways/sctp_network_gateway.h" -void srsran::srsvec::detail::mem_free(void* ptr) -{ - ::free(ptr); -} +namespace srsran { + +struct e2_appconfig; + +/// Converts and returns the given gnb application configuration to a E2AP Network Gateway configuration. +sctp_network_connector_config generate_e2ap_nw_config(const e2_appconfig& config, int ppid); + +} // namespace srsran diff --git a/apps/services/e2/e2_cli11_schema.cpp b/apps/services/e2/e2_cli11_schema.cpp new file mode 100644 index 0000000000..44822d6d9a --- /dev/null +++ b/apps/services/e2/e2_cli11_schema.cpp @@ -0,0 +1,53 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "e2_cli11_schema.h" +#include "e2_appconfig.h" +#include "srsran/support/cli11_utils.h" + +using namespace srsran; + +static void configure_cli11_e2_args(CLI::App& app, e2_appconfig& e2_params) +{ + add_option(app, "--enable_du_e2", e2_params.enable_du_e2, "Enable DU E2 agent")->capture_default_str(); + add_option(app, "--enable_cu_e2", e2_params.enable_cu_e2, "Enable CU E2 agent")->capture_default_str(); + add_option(app, "--addr", e2_params.ip_addr, "RIC IP address")->capture_default_str(); + add_option(app, "--port", e2_params.port, "RIC port")->check(CLI::Range(20000, 40000))->capture_default_str(); + add_option(app, "--bind_addr", e2_params.bind_addr, "Local IP address to bind for RIC connection") + ->capture_default_str() + ->check(CLI::ValidIPV4); + add_option(app, "--sctp_rto_initial", e2_params.sctp_rto_initial, "SCTP initial RTO value")->capture_default_str(); + add_option(app, "--sctp_rto_min", e2_params.sctp_rto_min, "SCTP RTO min")->capture_default_str(); + add_option(app, "--sctp_rto_max", e2_params.sctp_rto_max, "SCTP RTO max")->capture_default_str(); + add_option(app, "--sctp_init_max_attempts", e2_params.sctp_init_max_attempts, "SCTP init max attempts") + ->capture_default_str(); + add_option(app, "--sctp_max_init_timeo", e2_params.sctp_max_init_timeo, "SCTP max init timeout") + ->capture_default_str(); + add_option(app, "--e2sm_kpm_enabled", e2_params.e2sm_kpm_enabled, "Enable KPM service module")->capture_default_str(); + add_option(app, "--e2sm_rc_enabled", e2_params.e2sm_rc_enabled, "Enable RC service module")->capture_default_str(); +} + +void srsran::configure_cli11_with_e2_appconfig_schema(CLI::App& app, e2_appconfig& config) +{ + CLI::App* e2_subcmd = add_subcommand(app, "e2", "E2 parameters")->configurable(); + configure_cli11_e2_args(*e2_subcmd, config); +} diff --git a/apps/services/e2/e2_cli11_schema.h b/apps/services/e2/e2_cli11_schema.h new file mode 100644 index 0000000000..01acf1a0d5 --- /dev/null +++ b/apps/services/e2/e2_cli11_schema.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "CLI/CLI11.hpp" + +namespace srsran { + +struct e2_appconfig; + +/// Configures the given CLI11 application with the E2 application configuration schema. +void configure_cli11_with_e2_appconfig_schema(CLI::App& app, e2_appconfig& config); + +} // namespace srsran diff --git a/apps/units/flexible_du/support/CMakeLists.txt b/apps/services/hal/CMakeLists.txt similarity index 80% rename from apps/units/flexible_du/support/CMakeLists.txt rename to apps/services/hal/CMakeLists.txt index 5ed3f9dbb5..e1bf5f5936 100644 --- a/apps/units/flexible_du/support/CMakeLists.txt +++ b/apps/services/hal/CMakeLists.txt @@ -19,7 +19,7 @@ # set(SOURCES - cli11_cpu_affinities_parser_helper.cpp) + hal_cli11_schema.cpp) -add_library(srsran_flexible_du_support STATIC ${SOURCES}) -target_include_directories(srsran_flexible_du_support PRIVATE ${CMAKE_SOURCE_DIR}) +add_library(srsran_hal_app_service STATIC ${SOURCES}) +target_include_directories(srsran_hal_app_service PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/apps/services/hal/hal_appconfig.h b/apps/services/hal/hal_appconfig.h new file mode 100644 index 0000000000..75192ae98c --- /dev/null +++ b/apps/services/hal/hal_appconfig.h @@ -0,0 +1,35 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include + +namespace srsran { + +/// HAL configuration of the application. +struct hal_appconfig { + /// EAL configuration arguments. + std::string eal_args; +}; + +} // namespace srsran diff --git a/apps/services/hal/hal_cli11_schema.cpp b/apps/services/hal/hal_cli11_schema.cpp new file mode 100644 index 0000000000..cc6211e554 --- /dev/null +++ b/apps/services/hal/hal_cli11_schema.cpp @@ -0,0 +1,44 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "hal_cli11_schema.h" +#include "hal_appconfig.h" +#include "srsran/support/cli11_utils.h" + +using namespace srsran; + +static void configure_cli11_hal_args(CLI::App& app, hal_appconfig& config) +{ + add_option(app, "--eal_args", config.eal_args, "EAL configuration parameters used to initialize DPDK"); +} + +void srsran::configure_cli11_with_hal_appconfig_schema(CLI::App& app, hal_appconfig& config) +{ + CLI::App* hal_subcmd = add_subcommand(app, "hal", "HAL configuration")->configurable(); + configure_cli11_hal_args(*hal_subcmd, config); +} + +bool srsran::is_hal_section_present(CLI::App& app) +{ + auto subcmd = app.get_subcommand("hal"); + return subcmd->count_all() != 0; +} diff --git a/apps/services/hal/hal_cli11_schema.h b/apps/services/hal/hal_cli11_schema.h new file mode 100644 index 0000000000..d3f1198361 --- /dev/null +++ b/apps/services/hal/hal_cli11_schema.h @@ -0,0 +1,37 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "CLI/CLI11.hpp" + +namespace srsran { + +struct hal_appconfig; + +/// Configures the given CLI11 application with the HAL application configuration schema. +void configure_cli11_with_hal_appconfig_schema(CLI::App& app, hal_appconfig& config); + +/// Returns true if the HAL section is present in the given CLI11 application, otherwise false. +bool is_hal_section_present(CLI::App& app); + +} // namespace srsran diff --git a/apps/services/worker_manager/CMakeLists.txt b/apps/services/worker_manager/CMakeLists.txt new file mode 100644 index 0000000000..13450a1226 --- /dev/null +++ b/apps/services/worker_manager/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(SOURCES + worker_manager.cpp + worker_manager_cli11_schema.cpp) + +add_library(srsran_cpu_affinities_helper STATIC cli11_cpu_affinities_parser_helper.cpp) + +add_library(srsran_worker_manager_app_service STATIC ${SOURCES}) +target_include_directories(srsran_worker_manager_app_service PRIVATE ${CMAKE_SOURCE_DIR}) +target_link_libraries(srsran_worker_manager_app_service + srsran_cpu_affinities_helper + srsran_du_high # TODO: Temp + srsran_cu_up) # TODO: Temp diff --git a/apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.cpp b/apps/services/worker_manager/cli11_cpu_affinities_parser_helper.cpp similarity index 98% rename from apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.cpp rename to apps/services/worker_manager/cli11_cpu_affinities_parser_helper.cpp index ad428d0614..b2d740b4f1 100644 --- a/apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.cpp +++ b/apps/services/worker_manager/cli11_cpu_affinities_parser_helper.cpp @@ -21,7 +21,7 @@ */ #include "cli11_cpu_affinities_parser_helper.h" -#include "apps/services/os_sched_affinity_manager.h" +#include "os_sched_affinity_manager.h" #include "srsran/adt/expected.h" #include "srsran/adt/interval.h" diff --git a/apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.h b/apps/services/worker_manager/cli11_cpu_affinities_parser_helper.h similarity index 100% rename from apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.h rename to apps/services/worker_manager/cli11_cpu_affinities_parser_helper.h diff --git a/apps/services/os_sched_affinity_manager.h b/apps/services/worker_manager/os_sched_affinity_manager.h similarity index 100% rename from apps/services/os_sched_affinity_manager.h rename to apps/services/worker_manager/os_sched_affinity_manager.h diff --git a/apps/services/worker_manager.cpp b/apps/services/worker_manager/worker_manager.cpp similarity index 93% rename from apps/services/worker_manager.cpp rename to apps/services/worker_manager/worker_manager.cpp index b90056a011..f0f11aa587 100644 --- a/apps/services/worker_manager.cpp +++ b/apps/services/worker_manager/worker_manager.cpp @@ -255,15 +255,17 @@ void worker_manager::create_du_executors(const worker_manager_config::du_high_co } // Instantiate DU-high executor mapper. - du_high_executors.resize(du_hi.nof_cells); - for (unsigned i = 0; i != du_hi.nof_cells; ++i) { - auto& du_item = du_high_executors[i]; - const std::string cell_id_str = std::to_string(i); - - // DU-high executor mapper creation. - srs_du::du_high_executor_config cfg; - cfg.cell_executors = srs_du::du_high_executor_config::dedicated_cell_worker_list{ - {*exec_map.at("slot_exec#" + cell_id_str), *exec_map.at("cell_exec#" + cell_id_str)}}; + srs_du::du_high_executor_config cfg; + if (du_hi.is_du_multicell_enabled) { + // DU multicell enabled. Create one executor mapper. + du_high_executors.resize(1); + auto& du_item = du_high_executors[0]; + srs_du::du_high_executor_config::dedicated_cell_worker_list cell_workers; + for (unsigned i = 0; i != du_hi.nof_cells; ++i) { + const std::string cell_id_str = std::to_string(i); + cell_workers.push_back({*exec_map.at("slot_exec#" + cell_id_str), *exec_map.at("cell_exec#" + cell_id_str)}); + } + cfg.cell_executors.emplace(std::move(cell_workers)); cfg.ue_executors.policy = srs_du::du_high_executor_config::ue_executor_config::map_policy::per_cell; cfg.ue_executors.max_nof_strands = 1; cfg.ue_executors.ctrl_queue_size = task_worker_queue_size; @@ -275,6 +277,28 @@ void worker_manager::create_du_executors(const worker_manager_config::du_high_co cfg.trace_exec_tasks = false; du_item.du_high_exec_mapper = srs_du::create_du_high_executor_mapper(cfg); + } else { + // DU single cell enabled. Create one executor mapper per DU/cell. + du_high_executors.resize(du_hi.nof_cells); + for (unsigned i = 0; i != du_hi.nof_cells; ++i) { + auto& du_item = du_high_executors[i]; + const std::string cell_id_str = std::to_string(i); + + // DU-high executor mapper creation. + cfg.cell_executors = srs_du::du_high_executor_config::dedicated_cell_worker_list{ + {*exec_map.at("slot_exec#" + cell_id_str), *exec_map.at("cell_exec#" + cell_id_str)}}; + cfg.ue_executors.policy = srs_du::du_high_executor_config::ue_executor_config::map_policy::per_cell; + cfg.ue_executors.max_nof_strands = 1; + cfg.ue_executors.ctrl_queue_size = task_worker_queue_size; + cfg.ue_executors.pdu_queue_size = du_hi.pdu_queue_size; + cfg.ue_executors.pool_executor = exec_map.at("low_prio_exec"); + cfg.ctrl_executors.task_queue_size = task_worker_queue_size; + cfg.ctrl_executors.pool_executor = exec_map.at("high_prio_exec"); + cfg.is_rt_mode_enabled = du_hi.is_rt_mode_enabled; + cfg.trace_exec_tasks = false; + + du_item.du_high_exec_mapper = srs_du::create_du_high_executor_mapper(cfg); + } } if (du_low) { @@ -541,7 +565,7 @@ void worker_manager::create_lower_phy_executors(const worker_manager_config::ru_ { using namespace execution_config_helper; - // Radio Unit worker and executor. As the radio is unique per gNB, use the first cell of the affinity manager. + // Radio Unit worker and executor. As the radio is unique per application, use the first cell of the affinity manager. create_prio_worker("radio", task_worker_queue_size, {{"radio_exec"}}, diff --git a/apps/services/worker_manager.h b/apps/services/worker_manager/worker_manager.h similarity index 98% rename from apps/services/worker_manager.h rename to apps/services/worker_manager/worker_manager.h index 7aa208c693..a3fa93021e 100644 --- a/apps/services/worker_manager.h +++ b/apps/services/worker_manager/worker_manager.h @@ -22,8 +22,8 @@ #pragma once -#include "apps/services/worker_manager_config.h" -#include "apps/services/worker_manager_worker_getter.h" +#include "worker_manager_config.h" +#include "worker_manager_worker_getter.h" #include "srsran/cu_up/cu_up_executor_mapper.h" #include "srsran/du/du_high/du_high_executor_mapper.h" #include "srsran/support/executors/task_execution_manager.h" diff --git a/apps/services/worker_manager/worker_manager_appconfig.h b/apps/services/worker_manager/worker_manager_appconfig.h new file mode 100644 index 0000000000..094189f8bf --- /dev/null +++ b/apps/services/worker_manager/worker_manager_appconfig.h @@ -0,0 +1,59 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "os_sched_affinity_manager.h" + +namespace srsran { + +/// CPU affinities configuration for the application. +struct cpu_affinities_appconfig { + /// CPUs isolation. + std::optional isolated_cpus; + /// Low priority workers CPU affinity mask. + os_sched_affinity_config low_priority_cpu_cfg = {sched_affinity_mask_types::low_priority, + {}, + sched_affinity_mask_policy::mask}; +}; + +/// Non real time thread configuration for the application. +struct non_rt_threads_appconfig { + /// Number of non real time threads for processing of CP and UP data in the upper layers + unsigned nof_non_rt_threads = 4; +}; + +/// Expert threads configuration of the application. +struct expert_threads_appconfig { + /// Non real time thread configuration of the application. + non_rt_threads_appconfig non_rt_threads; +}; + +/// Expert configuration of the application. +struct expert_execution_appconfig { + /// Application CPU affinities. + cpu_affinities_appconfig affinities; + /// Expert thread configuration of the application. + expert_threads_appconfig threads; +}; + +} // namespace srsran diff --git a/apps/services/worker_manager/worker_manager_cli11_schema.cpp b/apps/services/worker_manager/worker_manager_cli11_schema.cpp new file mode 100644 index 0000000000..3c9dccb560 --- /dev/null +++ b/apps/services/worker_manager/worker_manager_cli11_schema.cpp @@ -0,0 +1,104 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "worker_manager_cli11_schema.h" +#include "cli11_cpu_affinities_parser_helper.h" +#include "worker_manager_appconfig.h" +#include "srsran/adt/expected.h" +#include "srsran/support/cli11_utils.h" +#include "srsran/support/error_handling.h" + +using namespace srsran; + +static void configure_cli11_non_rt_threads_args(CLI::App& app, non_rt_threads_appconfig& config) +{ + add_option(app, + "--nof_non_rt_threads", + config.nof_non_rt_threads, + "Number of non real time threads for processing of CP and UP data in upper layers.") + ->capture_default_str() + ->check(CLI::Number); +} + +static void configure_cli11_cpu_affinities_args(CLI::App& app, cpu_affinities_appconfig& config) +{ + auto parsing_isolated_cpus_fcn = [](std::optional& isolated_cpu_cfg, + const std::string& value, + const std::string& property_name) { + isolated_cpu_cfg.emplace(); + parse_affinity_mask(*isolated_cpu_cfg, value, property_name); + + if (isolated_cpu_cfg->all()) { + report_error("Error in '{}' property: can not assign all available CPUs to the application", property_name); + } + }; + + add_option_function( + app, + "--isolated_cpus", + [&config, &parsing_isolated_cpus_fcn](const std::string& value) { + parsing_isolated_cpus_fcn(config.isolated_cpus, value, "isolated_cpus"); + }, + "CPU cores isolated for application"); + + add_option_function( + app, + "--low_priority_cpus", + [&config](const std::string& value) { + parse_affinity_mask(config.low_priority_cpu_cfg.mask, value, "low_priority_cpus"); + }, + "CPU cores assigned to low priority tasks"); + + add_option_function( + app, + "--low_priority_pinning", + [&config](const std::string& value) { + config.low_priority_cpu_cfg.pinning_policy = to_affinity_mask_policy(value); + if (config.low_priority_cpu_cfg.pinning_policy == sched_affinity_mask_policy::last) { + report_error("Incorrect value={} used in {} property", value, "low_priority_pinning"); + } + }, + "Policy used for assigning CPU cores to low priority tasks"); +} + +static void configure_cli11_expert_execution_args(CLI::App& app, expert_execution_appconfig& config) +{ + // Affinity section. + CLI::App* affinities_subcmd = + add_subcommand(app, "affinities", "Application CPU affinities configuration")->configurable(); + configure_cli11_cpu_affinities_args(*affinities_subcmd, config.affinities); + + // Threads section. + CLI::App* threads_subcmd = add_subcommand(app, "threads", "Threads configuration")->configurable(); + + // Non real time threads. + CLI::App* non_rt_threads_subcmd = + add_subcommand(*threads_subcmd, "non_rt", "Non real time thread configuration")->configurable(); + configure_cli11_non_rt_threads_args(*non_rt_threads_subcmd, config.threads.non_rt_threads); +} + +void srsran::configure_cli11_with_worker_manager_appconfig_schema(CLI::App& app, expert_execution_appconfig& config) +{ + // Expert section. + CLI::App* expert_subcmd = app.add_subcommand("expert_execution", "Expert execution configuration")->configurable(); + configure_cli11_expert_execution_args(*expert_subcmd, config); +} diff --git a/apps/services/worker_manager/worker_manager_cli11_schema.h b/apps/services/worker_manager/worker_manager_cli11_schema.h new file mode 100644 index 0000000000..afb865323d --- /dev/null +++ b/apps/services/worker_manager/worker_manager_cli11_schema.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "CLI/CLI11.hpp" + +namespace srsran { + +struct expert_execution_appconfig; + +/// Configures the given CLI11 application with the worker manager application configuration schema. +void configure_cli11_with_worker_manager_appconfig_schema(CLI::App& app, expert_execution_appconfig& config); + +} // namespace srsran diff --git a/apps/services/worker_manager_config.h b/apps/services/worker_manager/worker_manager_config.h similarity index 98% rename from apps/services/worker_manager_config.h rename to apps/services/worker_manager/worker_manager_config.h index e3ce0a6359..51552a127f 100644 --- a/apps/services/worker_manager_config.h +++ b/apps/services/worker_manager/worker_manager_config.h @@ -83,6 +83,8 @@ struct worker_manager_config { unsigned nof_cells; /// Real-time mode enabled flag. bool is_rt_mode_enabled; + /// Multicell DU enabled flag. + bool is_du_multicell_enabled; }; // CU-UP worker configuration diff --git a/apps/services/worker_manager_worker_getter.h b/apps/services/worker_manager/worker_manager_worker_getter.h similarity index 100% rename from apps/services/worker_manager_worker_getter.h rename to apps/services/worker_manager/worker_manager_worker_getter.h diff --git a/apps/units/cu_cp/cu_cp_builder.cpp b/apps/units/cu_cp/cu_cp_builder.cpp index b968eaf6f9..d4c54312c7 100644 --- a/apps/units/cu_cp/cu_cp_builder.cpp +++ b/apps/units/cu_cp/cu_cp_builder.cpp @@ -25,7 +25,6 @@ #include "apps/units/cu_cp/cu_cp_unit_config.h" #include "cu_cp_commands.h" #include "cu_cp_config_translators.h" -#include "cu_cp_unit_config.h" #include "cu_cp_wrapper.h" #include "srsran/cu_cp/cu_cp_factory.h" #include "srsran/e2/e2_cu_metrics_connector.h" diff --git a/apps/units/cu_cp/cu_cp_config_translators.cpp b/apps/units/cu_cp/cu_cp_config_translators.cpp index daf9b9b1f2..c2b12f0276 100644 --- a/apps/units/cu_cp/cu_cp_config_translators.cpp +++ b/apps/units/cu_cp/cu_cp_config_translators.cpp @@ -21,7 +21,7 @@ */ #include "cu_cp_config_translators.h" -#include "apps/services/worker_manager_config.h" +#include "apps/services/worker_manager/worker_manager_config.h" #include "cu_cp_unit_config.h" #include "srsran/cu_cp/cu_cp_configuration_helpers.h" #include "srsran/ran/plmn_identity.h" @@ -349,7 +349,11 @@ srs_cu_cp::cu_cp_configuration srsran::generate_cu_cp_config(const cu_cp_unit_co for (const auto& plmn_item : supported_ta.plmn_list) { expected plmn = plmn_identity::parse(plmn_item.plmn_id); srsran_assert(plmn.has_value(), "Invalid PLMN: {}", plmn_item.plmn_id); - plmn_list.push_back({plmn.value(), plmn_item.tai_slice_support_list}); + plmn_list.push_back({plmn.value(), {}}); + for (const auto& elem : plmn_item.tai_slice_support_list) { + plmn_list.back().slice_support_list.push_back( + s_nssai_t{slice_service_type{elem.sst}, slice_differentiator::create(elem.sd).value()}); + } } supported_tas.push_back({supported_ta.tac, plmn_list}); } @@ -363,7 +367,11 @@ srs_cu_cp::cu_cp_configuration srsran::generate_cu_cp_config(const cu_cp_unit_co for (const auto& plmn_item : supported_ta.plmn_list) { expected plmn = plmn_identity::parse(plmn_item.plmn_id); srsran_assert(plmn.has_value(), "Invalid PLMN: {}", plmn_item.plmn_id); - plmn_list.push_back({plmn.value(), plmn_item.tai_slice_support_list}); + plmn_list.push_back({plmn.value(), {}}); + for (const auto& elem : plmn_item.tai_slice_support_list) { + plmn_list.back().slice_support_list.push_back( + s_nssai_t{slice_service_type{elem.sst}, slice_differentiator::create(elem.sd).value()}); + } } supported_tas.push_back({supported_ta.tac, plmn_list}); } @@ -389,7 +397,7 @@ srs_cu_cp::cu_cp_configuration srsran::generate_cu_cp_config(const cu_cp_unit_co // Timers out_cfg.ue.inactivity_timer = std::chrono::seconds{cu_cfg.inactivity_timer}; - out_cfg.ue.pdu_session_setup_timeout = std::chrono::seconds{cu_cfg.pdu_session_setup_timeout}; + out_cfg.ue.request_pdu_session_timeout = std::chrono::seconds{cu_cfg.request_pdu_session_timeout}; out_cfg.metrics.statistics_report_period = std::chrono::seconds{cu_cfg.metrics.cu_cp_statistics_report_period}; // Mobility diff --git a/apps/units/cu_cp/cu_cp_unit_config.h b/apps/units/cu_cp/cu_cp_unit_config.h index d3b0b324fa..e4217782df 100644 --- a/apps/units/cu_cp/cu_cp_unit_config.h +++ b/apps/units/cu_cp/cu_cp_unit_config.h @@ -26,7 +26,6 @@ #include "cu_cp_unit_logger_config.h" #include "srsran/e2/e2ap_configuration.h" #include "srsran/ran/nr_band.h" -#include "srsran/ran/nr_cell_identity.h" #include "srsran/ran/pci.h" #include "srsran/ran/qos/five_qi.h" #include "srsran/ran/s_nssai.h" @@ -35,9 +34,14 @@ namespace srsran { struct cu_cp_unit_plmn_item { + struct tai_slice_t { + uint8_t sst = 0; + uint32_t sd = 0xffffffU; + }; + std::string plmn_id; /// Supported Slices by the RAN node. - std::vector tai_slice_support_list; + std::vector tai_slice_support_list; }; struct cu_cp_unit_supported_ta_item { @@ -277,8 +281,8 @@ struct cu_cp_unit_config { uint8_t max_nof_drbs_per_ue = 8; /// Inactivity timer in seconds. int inactivity_timer = 120; - /// PDU session setup timeout in seconds (must be larger than T310). - unsigned pdu_session_setup_timeout = 3; + /// PDU session request timeout in seconds (must be larger than T310). + unsigned request_pdu_session_timeout = 3; /// Loggers configuration. cu_cp_unit_logger_config loggers; /// PCAPs configuration. @@ -300,7 +304,7 @@ struct cu_cp_unit_config { /// QoS configuration. std::vector qos_cfg; /// Network slice configuration. - std::vector slice_cfg = {s_nssai_t{1}}; + std::vector slice_cfg = {s_nssai_t{slice_service_type{1}}}; /// E2 configuration. e2_config e2_cfg; }; diff --git a/apps/units/cu_cp/cu_cp_unit_config_cli11_schema.cpp b/apps/units/cu_cp/cu_cp_unit_config_cli11_schema.cpp index 76a313d94a..b365bcb5b9 100644 --- a/apps/units/cu_cp/cu_cp_unit_config_cli11_schema.cpp +++ b/apps/units/cu_cp/cu_cp_unit_config_cli11_schema.cpp @@ -23,6 +23,7 @@ #include "cu_cp_unit_config_cli11_schema.h" #include "apps/services/logger/logger_appconfig_cli11_utils.h" #include "cu_cp_unit_config.h" +#include "srsran/ran/nr_cell_identity.h" #include "srsran/support/cli11_utils.h" #include "srsran/support/config_parsers.h" @@ -61,7 +62,7 @@ static void configure_cli11_pcap_args(CLI::App& app, cu_cp_unit_pcap_config& pca add_option(app, "--e1ap_enable", pcap_params.e1ap.enabled, "Enable E1AP packet capture")->always_capture_default(); } -static void configure_cli11_tai_slice_support_args(CLI::App& app, s_nssai_t& config) +static void configure_cli11_tai_slice_support_args(CLI::App& app, cu_cp_unit_plmn_item::tai_slice_t& config) { add_option(app, "--sst", config.sst, "Slice Service Type")->capture_default_str()->check(CLI::Range(0, 255)); add_option(app, "--sd", config.sd, "Service Differentiator")->capture_default_str()->check(CLI::Range(0, 0xffffff)); @@ -387,10 +388,10 @@ static void configure_cli11_cu_cp_args(CLI::App& app, cu_cp_unit_config& cu_cp_p ->check(CLI::Range(1, 7200)); add_option(app, - "--pdu_session_setup_timeout", - cu_cp_params.pdu_session_setup_timeout, - "Timeout for the setup of a PDU session after an InitialUeMessage was sent to the core, in " - "seconds. The timeout must be larger than T310. If the value is reached, the UE will be released") + "--request_pdu_session_timeout", + cu_cp_params.request_pdu_session_timeout, + "Timeout for requesting a PDU session after the InitialUeMessage was sent to the core, in " + "seconds. The timeout must be larger than T310. If the value is reached, the UE will be released.") ->capture_default_str(); CLI::App* amf_subcmd = app.add_subcommand("amf", "AMF configuration"); diff --git a/apps/units/cu_cp/cu_cp_unit_config_yaml_writer.cpp b/apps/units/cu_cp/cu_cp_unit_config_yaml_writer.cpp index c62a6819ff..8f44806163 100644 --- a/apps/units/cu_cp/cu_cp_unit_config_yaml_writer.cpp +++ b/apps/units/cu_cp/cu_cp_unit_config_yaml_writer.cpp @@ -26,13 +26,13 @@ using namespace srsran; -static YAML::Node build_cu_cp_tai_slice_section(const s_nssai_t& config) +static YAML::Node build_cu_cp_tai_slice_section(const cu_cp_unit_plmn_item::tai_slice_t& config) { YAML::Node node; - node["sst"] = static_cast(config.sst); + node["sst"] = config.sst; if (config.sd) { - node["sd"] = config.sd.value(); + node["sd"] = config.sd; } return node; @@ -243,12 +243,12 @@ static YAML::Node build_cu_cp_section(const cu_cp_unit_config& config) { YAML::Node node; - node["max_nof_dus"] = config.max_nof_dus; - node["max_nof_cu_ups"] = config.max_nof_cu_ups; - node["max_nof_ues"] = config.max_nof_ues; - node["max_nof_drbs_per_ue"] = static_cast(config.max_nof_drbs_per_ue); - node["inactivity_timer"] = config.inactivity_timer; - node["pdu_session_setup_timeout"] = config.pdu_session_setup_timeout; + node["max_nof_dus"] = config.max_nof_dus; + node["max_nof_cu_ups"] = config.max_nof_cu_ups; + node["max_nof_ues"] = config.max_nof_ues; + node["max_nof_drbs_per_ue"] = static_cast(config.max_nof_drbs_per_ue); + node["inactivity_timer"] = config.inactivity_timer; + node["request_pdu_session_timeout"] = config.request_pdu_session_timeout; node["amf"] = build_cu_cp_amf_section(config.amf_config); if (!config.extra_amfs.empty()) { @@ -394,8 +394,8 @@ static void build_cu_cp_slicing_section(YAML::Node node, span s { for (const auto& slice : slice_cfg) { YAML::Node node_entry; - node_entry["sst"] = static_cast(slice.sst); - if (slice.sd) { + node_entry["sst"] = slice.sst.value(); + if (slice.sd.is_set()) { node_entry["sd"] = slice.sd.value(); } node["slicing"].push_back(node_entry); diff --git a/apps/units/cu_cp/pcap_factory.h b/apps/units/cu_cp/pcap_factory.h index a362b4433b..c31b870454 100644 --- a/apps/units/cu_cp/pcap_factory.h +++ b/apps/units/cu_cp/pcap_factory.h @@ -22,7 +22,7 @@ #pragma once -#include "apps/services/worker_manager_worker_getter.h" +#include "apps/services/worker_manager/worker_manager_worker_getter.h" #include "apps/units/cu_cp/cu_cp_unit_pcap_config.h" #include "srsran/pcap/dlt_pcap.h" diff --git a/apps/units/cu_up/cu_up_builder.cpp b/apps/units/cu_up/cu_up_builder.cpp index e9ff06d22f..6e954a42b4 100644 --- a/apps/units/cu_up/cu_up_builder.cpp +++ b/apps/units/cu_up/cu_up_builder.cpp @@ -22,7 +22,7 @@ #include "cu_up_builder.h" #include "apps/services/e2/e2_metric_connector_manager.h" -#include "apps/services/worker_manager.h" +#include "apps/services/worker_manager/worker_manager.h" #include "apps/units/cu_up/metrics/cu_up_pdcp_metrics_consumers.h" #include "apps/units/cu_up/metrics/cu_up_pdcp_metrics_producer.h" #include "cu_up_unit_config.h" diff --git a/apps/units/cu_up/cu_up_unit_config_translators.cpp b/apps/units/cu_up/cu_up_unit_config_translators.cpp index 134cf67f0e..d1ab960cac 100644 --- a/apps/units/cu_up/cu_up_unit_config_translators.cpp +++ b/apps/units/cu_up/cu_up_unit_config_translators.cpp @@ -21,7 +21,7 @@ */ #include "cu_up_unit_config_translators.h" -#include "apps/services/worker_manager_config.h" +#include "apps/services/worker_manager/worker_manager_config.h" #include "cu_up_unit_config.h" #include "srsran/cu_up/cu_up_configuration_helpers.h" #include "srsran/e2/e2ap_configuration_helpers.h" diff --git a/apps/units/cu_up/pcap_factory.h b/apps/units/cu_up/pcap_factory.h index 1fabd866dc..6b73900b8e 100644 --- a/apps/units/cu_up/pcap_factory.h +++ b/apps/units/cu_up/pcap_factory.h @@ -22,7 +22,7 @@ #pragma once -#include "apps/services/worker_manager_worker_getter.h" +#include "apps/services/worker_manager/worker_manager_worker_getter.h" #include "apps/units/cu_up/cu_up_unit_pcap_config.h" #include "srsran/pcap/dlt_pcap.h" diff --git a/apps/units/flexible_du/CMakeLists.txt b/apps/units/flexible_du/CMakeLists.txt index 0586961e57..bdd50d4cf7 100644 --- a/apps/units/flexible_du/CMakeLists.txt +++ b/apps/units/flexible_du/CMakeLists.txt @@ -19,7 +19,6 @@ # add_subdirectory(o_du_high) -add_subdirectory(support) if (NOT DU_SPLIT_6) add_subdirectory(split_helpers) endif () diff --git a/apps/units/flexible_du/o_du_high/du_high/CMakeLists.txt b/apps/units/flexible_du/o_du_high/du_high/CMakeLists.txt index 387d447dfd..9dcdc81afa 100644 --- a/apps/units/flexible_du/o_du_high/du_high/CMakeLists.txt +++ b/apps/units/flexible_du/o_du_high/du_high/CMakeLists.txt @@ -28,4 +28,4 @@ set(SOURCES add_library(srsran_du_high_unit_helpers STATIC ${SOURCES}) target_include_directories(srsran_du_high_unit_helpers PRIVATE ${CMAKE_SOURCE_DIR}) -target_link_libraries(srsran_du_high_unit_helpers sched_config srsran_du_high_unit_metrics_helpers) +target_link_libraries(srsran_du_high_unit_helpers sched_config srsran_du_high_unit_metrics_helpers srsran_cpu_affinities_helper) diff --git a/apps/units/flexible_du/o_du_high/du_high/du_high_config.h b/apps/units/flexible_du/o_du_high/du_high/du_high_config.h index 989ec21f9e..befa09a1ea 100644 --- a/apps/units/flexible_du/o_du_high/du_high/du_high_config.h +++ b/apps/units/flexible_du/o_du_high/du_high/du_high_config.h @@ -22,7 +22,7 @@ #pragma once -#include "apps/services/os_sched_affinity_manager.h" +#include "apps/services/worker_manager/os_sched_affinity_manager.h" #include "srsran/e2/e2ap_configuration.h" #include "srsran/ran/band_helper.h" #include "srsran/ran/bs_channel_bandwidth.h" @@ -180,6 +180,8 @@ struct du_high_unit_pusch_config { unsigned max_consecutive_kos = 100; /// Redundancy version sequence to use. Each element can have one of the following values: {0, 1, 2, 3}. std::vector rv_sequence = {0}; + /// Maximum rank. Limits the number of layers for PUSCH transmissions. + unsigned max_rank = 4; /// MCS table to use for PUSCH pusch_mcs_table mcs_table = pusch_mcs_table::qam64; /// \c msg3-DeltaPreamble, TS 38.331. Values: {-1,...,6}. @@ -569,8 +571,10 @@ struct du_high_unit_cell_slice_sched_config { /// Slice configuration for a cell. struct du_high_unit_cell_slice_config { - /// Slice identifier. - s_nssai_t s_nssai = s_nssai_t{1}; + /// Slice/Service Type. + uint8_t sst; + /// Slice Differentiator. + uint32_t sd; /// Slice scheduling configuration. du_high_unit_cell_slice_sched_config sched_cfg; }; diff --git a/apps/units/flexible_du/o_du_high/du_high/du_high_config_cli11_schema.cpp b/apps/units/flexible_du/o_du_high/du_high/du_high_config_cli11_schema.cpp index 19c05b9bd1..1cddbcbca2 100644 --- a/apps/units/flexible_du/o_du_high/du_high/du_high_config_cli11_schema.cpp +++ b/apps/units/flexible_du/o_du_high/du_high/du_high_config_cli11_schema.cpp @@ -22,7 +22,7 @@ #include "du_high_config_cli11_schema.h" #include "apps/services/logger/logger_appconfig_cli11_utils.h" -#include "apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.h" +#include "apps/services/worker_manager/cli11_cpu_affinities_parser_helper.h" #include "du_high_config.h" #include "srsran/ran/du_types.h" #include "srsran/ran/duplex_mode.h" @@ -657,6 +657,13 @@ static void configure_cli11_pusch_args(CLI::App& app, du_high_unit_pusch_config& "MCS table to use PUSCH") ->default_str("qam64") ->check(CLI::IsMember({"qam64", "qam256"}, CLI::ignore_case)); + add_option(app, + "--max_rank", + pusch_params.max_rank, + "Maximum number of PUSCH transmission layers. The actual maximum is limited by the number of receive " + "ports and UE capabilities.") + ->capture_default_str() + ->check(CLI::Range(1, 4)); add_option(app, "--msg3_delta_preamble", pusch_params.msg3_delta_preamble, @@ -1165,10 +1172,8 @@ static void configure_cli11_slicing_scheduling_args(CLI::App& static void configure_cli11_slicing_args(CLI::App& app, du_high_unit_cell_slice_config& slice_params) { - add_option(app, "--sst", slice_params.s_nssai.sst, "Slice Service Type") - ->capture_default_str() - ->check(CLI::Range(0, 255)); - add_option(app, "--sd", slice_params.s_nssai.sd, "Service Differentiator") + add_option(app, "--sst", slice_params.sst, "Slice Service Type")->capture_default_str()->check(CLI::Range(0, 255)); + add_option(app, "--sd", slice_params.sd, "Service Differentiator") ->capture_default_str() ->check(CLI::Range(0, 0xffffff)); diff --git a/apps/units/flexible_du/o_du_high/du_high/du_high_config_translators.cpp b/apps/units/flexible_du/o_du_high/du_high/du_high_config_translators.cpp index 7e43880c45..6a9983f0b7 100644 --- a/apps/units/flexible_du/o_du_high/du_high/du_high_config_translators.cpp +++ b/apps/units/flexible_du/o_du_high/du_high/du_high_config_translators.cpp @@ -21,13 +21,14 @@ */ #include "du_high_config_translators.h" -#include "apps/services/worker_manager_config.h" +#include "apps/services/worker_manager/worker_manager_config.h" #include "du_high_config.h" #include "srsran/du/du_cell_config_helpers.h" #include "srsran/du/du_cell_config_validation.h" #include "srsran/du/du_high/du_qos_config_helpers.h" #include "srsran/du/du_update_config_helpers.h" #include "srsran/e2/e2ap_configuration_helpers.h" +#include "srsran/phy/upper/channel_processors/pusch/pusch_processor_phy_capabilities.h" #include "srsran/ran/duplex_mode.h" #include "srsran/ran/prach/prach_configuration.h" #include "srsran/rlc/rlc_srb_config_factory.h" @@ -443,6 +444,12 @@ std::vector srsran::generate_du_cell_config(const du_hig base_cell.pusch_cfg.p0_nominal_with_grant; out_cell.ul_cfg_common.init_ul_bwp.pusch_cfg_common.value().msg3_delta_power = base_cell.pusch_cfg.msg3_delta_power; + // Determine the PUSCH transmission maximum number of layers. Selects the most limiting factor among the physical + // layer, number of antennas and configured maximum rank. + pusch_processor_phy_capabilities phy_capabilities = get_pusch_processor_phy_capabilities(); + out_cell.pusch_max_nof_layers = + std::min(cell.cell.nof_antennas_ul, std::min(phy_capabilities.max_nof_layers, cell.cell.pusch_cfg.max_rank)); + if (config.ntn_cfg.has_value()) { out_cell.ntn_cs_koffset = config.ntn_cfg.value().cell_specific_koffset; } @@ -714,7 +721,8 @@ srsran::generate_du_slicing_rrm_policy_config(span for (const auto& plmn : plmns) { for (const auto& cfg : slice_cfg) { rrm_policy_cfgs.emplace_back(); - rrm_policy_cfgs.back().rrc_member.s_nssai = cfg.s_nssai; + rrm_policy_cfgs.back().rrc_member.s_nssai = + s_nssai_t{slice_service_type{cfg.sst}, slice_differentiator::create(cfg.sd).value()}; rrm_policy_cfgs.back().rrc_member.plmn_id = plmn_identity::parse(plmn).value(); rrm_policy_cfgs.back().min_prb = (nof_cell_crbs * cfg.sched_cfg.min_prb_policy_ratio) / 100; rrm_policy_cfgs.back().max_prb = (nof_cell_crbs * cfg.sched_cfg.max_prb_policy_ratio) / 100; diff --git a/apps/units/flexible_du/o_du_high/du_high/du_high_config_yaml_writer.cpp b/apps/units/flexible_du/o_du_high/du_high/du_high_config_yaml_writer.cpp index b0ecf5fdfd..778ceb3289 100644 --- a/apps/units/flexible_du/o_du_high/du_high/du_high_config_yaml_writer.cpp +++ b/apps/units/flexible_du/o_du_high/du_high/du_high_config_yaml_writer.cpp @@ -362,6 +362,7 @@ static YAML::Node build_du_high_pusch_section(const du_high_unit_pusch_config& c node["max_ue_mcs"] = config.max_ue_mcs; node["max_consecutive_kos"] = config.max_consecutive_kos; node["mcs_table"] = to_string(config.mcs_table); + node["max_rank"] = config.max_rank; node["msg3_delta_preamble"] = config.msg3_delta_preamble; node["p0_nominal_with_grant"] = config.p0_nominal_with_grant; node["max_puschs_per_slot"] = config.max_puschs_per_slot; diff --git a/apps/units/flexible_du/o_du_high/du_high/pcap_factory.h b/apps/units/flexible_du/o_du_high/du_high/pcap_factory.h index c1084d42df..6fc60770b4 100644 --- a/apps/units/flexible_du/o_du_high/du_high/pcap_factory.h +++ b/apps/units/flexible_du/o_du_high/du_high/pcap_factory.h @@ -22,7 +22,7 @@ #pragma once -#include "apps/services/worker_manager.h" +#include "apps/services/worker_manager/worker_manager.h" #include "srsran/pcap/dlt_pcap.h" #include "srsran/pcap/mac_pcap.h" #include "srsran/pcap/rlc_pcap.h" diff --git a/apps/units/flexible_du/o_du_high/fapi/fapi_config_translator.cpp b/apps/units/flexible_du/o_du_high/fapi/fapi_config_translator.cpp index eaeb3053bd..c5667bd947 100644 --- a/apps/units/flexible_du/o_du_high/fapi/fapi_config_translator.cpp +++ b/apps/units/flexible_du/o_du_high/fapi/fapi_config_translator.cpp @@ -21,7 +21,7 @@ */ #include "fapi_config_translator.h" -#include "apps/services/worker_manager_config.h" +#include "apps/services/worker_manager/worker_manager_config.h" #include "fapi_config.h" using namespace srsran; diff --git a/apps/units/flexible_du/o_du_low/CMakeLists.txt b/apps/units/flexible_du/o_du_low/CMakeLists.txt index 70e326335a..3c96c250ec 100644 --- a/apps/units/flexible_du/o_du_low/CMakeLists.txt +++ b/apps/units/flexible_du/o_du_low/CMakeLists.txt @@ -27,7 +27,7 @@ set(SOURCES add_library(srsran_o_du_low_unit_helpers STATIC ${SOURCES}) target_include_directories(srsran_o_du_low_unit_helpers PRIVATE ${CMAKE_SOURCE_DIR}) -set(DU_LOW_UNIT_HELPERS_LIBRARIES srsran_upper_phy) +set(DU_LOW_UNIT_HELPERS_LIBRARIES srsran_upper_phy srsran_cpu_affinities_helper) # Hardware acceleration for both PUSCH and PDSCH is enabled by default when using DPDK. if (DPDK_FOUND) diff --git a/apps/units/flexible_du/o_du_low/du_low_config.h b/apps/units/flexible_du/o_du_low/du_low_config.h index f84c36e048..f775c6521a 100644 --- a/apps/units/flexible_du/o_du_low/du_low_config.h +++ b/apps/units/flexible_du/o_du_low/du_low_config.h @@ -22,7 +22,7 @@ #pragma once -#include "apps/services/os_sched_affinity_manager.h" +#include "apps/services/worker_manager/os_sched_affinity_manager.h" #include #include #include diff --git a/apps/units/flexible_du/o_du_low/du_low_config_cli11_schema.cpp b/apps/units/flexible_du/o_du_low/du_low_config_cli11_schema.cpp index 732f0ca58d..4aae63b50e 100644 --- a/apps/units/flexible_du/o_du_low/du_low_config_cli11_schema.cpp +++ b/apps/units/flexible_du/o_du_low/du_low_config_cli11_schema.cpp @@ -22,7 +22,7 @@ #include "du_low_config_cli11_schema.h" #include "apps/services/logger/logger_appconfig_cli11_utils.h" -#include "apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.h" +#include "apps/services/worker_manager/cli11_cpu_affinities_parser_helper.h" #include "du_low_config.h" #include "srsran/adt/expected.h" #include "srsran/support/cli11_utils.h" diff --git a/apps/units/flexible_du/o_du_low/du_low_config_translator.cpp b/apps/units/flexible_du/o_du_low/du_low_config_translator.cpp index 95d04d2022..b2c29a6094 100644 --- a/apps/units/flexible_du/o_du_low/du_low_config_translator.cpp +++ b/apps/units/flexible_du/o_du_low/du_low_config_translator.cpp @@ -21,7 +21,7 @@ */ #include "du_low_config_translator.h" -#include "apps/services/worker_manager_config.h" +#include "apps/services/worker_manager/worker_manager_config.h" #include "du_low_config.h" #include "srsran/du/du_cell_config.h" #include "srsran/phy/upper/upper_phy_factories.h" @@ -122,6 +122,7 @@ static void generate_du_low_config(srs_du::du_low_config& out_config } upper_phy_cell.nof_slots_request_headroom = du_low.expert_phy_cfg.nof_slots_request_headroom; + upper_phy_cell.pusch_max_nof_layers = cell.pusch_max_nof_layers; upper_phy_cell.log_level = du_low.loggers.phy_level; upper_phy_cell.enable_logging_broadcast = du_low.loggers.broadcast_enabled; upper_phy_cell.rx_symbol_printer_filename = du_low.loggers.phy_rx_symbols_filename; diff --git a/apps/units/flexible_du/o_du_low/o_du_low_unit_factory.cpp b/apps/units/flexible_du/o_du_low/o_du_low_unit_factory.cpp index 4f713b97f0..160af37cb5 100644 --- a/apps/units/flexible_du/o_du_low/o_du_low_unit_factory.cpp +++ b/apps/units/flexible_du/o_du_low/o_du_low_unit_factory.cpp @@ -21,7 +21,7 @@ */ #include "o_du_low_unit_factory.h" -#include "apps/services/worker_manager.h" +#include "apps/services/worker_manager/worker_manager.h" #include "du_low_config.h" #include "du_low_config_translator.h" #include "srsran/du/du_low/o_du_low_factory.h" diff --git a/apps/units/flexible_du/split_7_2/helpers/CMakeLists.txt b/apps/units/flexible_du/split_7_2/helpers/CMakeLists.txt index 767de07f1c..4f7134f192 100644 --- a/apps/units/flexible_du/split_7_2/helpers/CMakeLists.txt +++ b/apps/units/flexible_du/split_7_2/helpers/CMakeLists.txt @@ -30,5 +30,5 @@ set(SOURCES ru_ofh_config_yaml_writer.cpp) add_library(srsran_split_7_2_app_unit_helpers STATIC ${SOURCES}) -target_link_libraries(srsran_split_7_2_app_unit_helpers srsran_ru_ofh srsran_flexible_du_support) +target_link_libraries(srsran_split_7_2_app_unit_helpers srsran_ru_ofh srsran_cpu_affinities_helper) target_include_directories(srsran_split_7_2_app_unit_helpers PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config.h b/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config.h index da9206c791..d8f473c6de 100644 --- a/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config.h +++ b/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config.h @@ -22,7 +22,7 @@ #pragma once -#include "apps/services/os_sched_affinity_manager.h" +#include "apps/services/worker_manager/os_sched_affinity_manager.h" #include "srsran/ran/bs_channel_bandwidth.h" #include "srsran/support/units.h" #include diff --git a/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_cli11_schema.cpp b/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_cli11_schema.cpp index 2ff8875e2b..6d8aa8f163 100644 --- a/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_cli11_schema.cpp +++ b/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_cli11_schema.cpp @@ -22,7 +22,7 @@ #include "ru_ofh_config_cli11_schema.h" #include "apps/services/logger/logger_appconfig_cli11_utils.h" -#include "apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.h" +#include "apps/services/worker_manager/cli11_cpu_affinities_parser_helper.h" #include "ru_ofh_config.h" #include "srsran/support/cli11_utils.h" #include "srsran/support/config_parsers.h" diff --git a/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_translator.cpp b/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_translator.cpp index 47c34fc41c..ca3f7c020e 100644 --- a/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_translator.cpp +++ b/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_translator.cpp @@ -21,7 +21,7 @@ */ #include "ru_ofh_config_translator.h" -#include "apps/services/worker_manager_config.h" +#include "apps/services/worker_manager/worker_manager_config.h" #include "ru_ofh_config.h" #include "srsran/du/du_cell_config.h" diff --git a/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_validator.h b/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_validator.h index 461b416f3e..178023d808 100644 --- a/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_validator.h +++ b/apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_validator.h @@ -22,7 +22,7 @@ #pragma once -#include "apps/services/os_sched_affinity_manager.h" +#include "apps/services/worker_manager/os_sched_affinity_manager.h" #include "ru_ofh_config.h" #include "srsran/ran/subcarrier_spacing.h" diff --git a/apps/units/flexible_du/split_7_2/helpers/ru_ofh_factories.cpp b/apps/units/flexible_du/split_7_2/helpers/ru_ofh_factories.cpp index ca32b75a5d..21e85f184a 100644 --- a/apps/units/flexible_du/split_7_2/helpers/ru_ofh_factories.cpp +++ b/apps/units/flexible_du/split_7_2/helpers/ru_ofh_factories.cpp @@ -21,7 +21,7 @@ */ #include "ru_ofh_factories.h" -#include "apps/services/worker_manager.h" +#include "apps/services/worker_manager/worker_manager.h" #include "apps/units/flexible_du/split_helpers/flexible_du_configs.h" #include "ru_ofh_config_translator.h" #include "srsran/ru/ru_ofh_factory.h" diff --git a/apps/units/flexible_du/split_7_2/split_7_2_du_unit_cli11_schema.cpp b/apps/units/flexible_du/split_7_2/split_7_2_du_unit_cli11_schema.cpp index fe3ec2779d..be21e5936d 100644 --- a/apps/units/flexible_du/split_7_2/split_7_2_du_unit_cli11_schema.cpp +++ b/apps/units/flexible_du/split_7_2/split_7_2_du_unit_cli11_schema.cpp @@ -24,10 +24,7 @@ #include "apps/units/flexible_du/o_du_high/o_du_high_unit_config_cli11_schema.h" #include "apps/units/flexible_du/o_du_low/du_low_config_cli11_schema.h" #include "apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_cli11_schema.h" -#include "apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.h" #include "split_7_2_du_unit_config.h" -#include "srsran/support/cli11_utils.h" -#include "srsran/support/config_parsers.h" using namespace srsran; diff --git a/apps/units/flexible_du/split_8/helpers/CMakeLists.txt b/apps/units/flexible_du/split_8/helpers/CMakeLists.txt index 00b4d20e7b..40147c628a 100644 --- a/apps/units/flexible_du/split_8/helpers/CMakeLists.txt +++ b/apps/units/flexible_du/split_8/helpers/CMakeLists.txt @@ -26,5 +26,5 @@ set(SOURCES ru_sdr_factories.cpp) add_library(srsran_split_8_app_unit_helpers STATIC ${SOURCES}) -target_link_libraries(srsran_split_8_app_unit_helpers srsran_ru_generic srsran_lower_phy srsran_flexible_du_support) +target_link_libraries(srsran_split_8_app_unit_helpers srsran_ru_generic srsran_lower_phy srsran_cpu_affinities_helper) target_include_directories(srsran_split_8_app_unit_helpers PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/apps/units/flexible_du/split_8/helpers/ru_sdr_config.h b/apps/units/flexible_du/split_8/helpers/ru_sdr_config.h index 94e7d85add..e7fd57fb30 100644 --- a/apps/units/flexible_du/split_8/helpers/ru_sdr_config.h +++ b/apps/units/flexible_du/split_8/helpers/ru_sdr_config.h @@ -22,8 +22,7 @@ #pragma once -#include "apps/services/os_sched_affinity_manager.h" - +#include "apps/services/worker_manager/os_sched_affinity_manager.h" #include namespace srsran { diff --git a/apps/units/flexible_du/split_8/helpers/ru_sdr_config_cli11_schema.cpp b/apps/units/flexible_du/split_8/helpers/ru_sdr_config_cli11_schema.cpp index aab667ff2c..9b92fb4a5a 100644 --- a/apps/units/flexible_du/split_8/helpers/ru_sdr_config_cli11_schema.cpp +++ b/apps/units/flexible_du/split_8/helpers/ru_sdr_config_cli11_schema.cpp @@ -22,7 +22,7 @@ #include "ru_sdr_config_cli11_schema.h" #include "apps/services/logger/logger_appconfig_cli11_utils.h" -#include "apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.h" +#include "apps/services/worker_manager/cli11_cpu_affinities_parser_helper.h" #include "ru_sdr_config.h" #include "srsran/support/cli11_utils.h" #include "srsran/support/config_parsers.h" diff --git a/apps/units/flexible_du/split_8/helpers/ru_sdr_config_translator.cpp b/apps/units/flexible_du/split_8/helpers/ru_sdr_config_translator.cpp index 97ea7f13d3..f6c9b49aee 100644 --- a/apps/units/flexible_du/split_8/helpers/ru_sdr_config_translator.cpp +++ b/apps/units/flexible_du/split_8/helpers/ru_sdr_config_translator.cpp @@ -21,7 +21,7 @@ */ #include "ru_sdr_config_translator.h" -#include "apps/services/worker_manager_config.h" +#include "apps/services/worker_manager/worker_manager_config.h" #include "ru_sdr_config.h" #include "srsran/du/du_cell_config.h" diff --git a/apps/units/flexible_du/split_8/helpers/ru_sdr_factories.cpp b/apps/units/flexible_du/split_8/helpers/ru_sdr_factories.cpp index e1bef66d2c..4b867f29f7 100644 --- a/apps/units/flexible_du/split_8/helpers/ru_sdr_factories.cpp +++ b/apps/units/flexible_du/split_8/helpers/ru_sdr_factories.cpp @@ -21,7 +21,7 @@ */ #include "ru_sdr_factories.h" -#include "apps/services/worker_manager.h" +#include "apps/services/worker_manager/worker_manager.h" #include "apps/units/flexible_du/split_helpers/flexible_du_configs.h" #include "ru_sdr_config_translator.h" #include "srsran/ru/ru_generic_factory.h" diff --git a/apps/units/flexible_du/split_8/split_8_du_unit_cli11_schema.cpp b/apps/units/flexible_du/split_8/split_8_du_unit_cli11_schema.cpp index a9e2b416b3..ae729bf8ec 100644 --- a/apps/units/flexible_du/split_8/split_8_du_unit_cli11_schema.cpp +++ b/apps/units/flexible_du/split_8/split_8_du_unit_cli11_schema.cpp @@ -25,7 +25,6 @@ #include "apps/units/flexible_du/o_du_low/du_low_config_cli11_schema.h" #include "apps/units/flexible_du/split_8/helpers/ru_sdr_config_cli11_schema.h" #include "split_8_du_unit_config.h" -#include "srsran/support/cli11_utils.h" using namespace srsran; diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp b/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp index 1028dbc92c..b83b770ed3 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp @@ -21,7 +21,7 @@ */ #include "dynamic_du_factory.h" -#include "apps/services/worker_manager.h" +#include "apps/services/worker_manager/worker_manager.h" #include "apps/units/flexible_du/split_7_2/helpers/ru_ofh_factories.h" #include "apps/units/flexible_du/split_8/helpers/ru_sdr_factories.h" #include "dynamic_du_translators.h" diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_translators.cpp b/apps/units/flexible_du/split_dynamic/dynamic_du_translators.cpp index 18a3809865..bca91875aa 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_translators.cpp +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_translators.cpp @@ -21,7 +21,7 @@ */ #include "dynamic_du_translators.h" -#include "apps/services/worker_manager_config.h" +#include "apps/services/worker_manager/worker_manager_config.h" #include "apps/units/flexible_du/o_du_high/du_high/du_high_config_translators.h" #include "apps/units/flexible_du/o_du_high/o_du_high_unit_config_translators.h" #include "apps/units/flexible_du/o_du_low/du_low_config_translator.h" diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.cpp b/apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.cpp index f82bb1d661..0e8a461f79 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.cpp +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.cpp @@ -21,11 +21,11 @@ */ #include "dynamic_du_unit_cli11_schema.h" +#include "apps/services/worker_manager/cli11_cpu_affinities_parser_helper.h" #include "apps/units/flexible_du/o_du_high/o_du_high_unit_config_cli11_schema.h" #include "apps/units/flexible_du/o_du_low/du_low_config_cli11_schema.h" #include "apps/units/flexible_du/split_7_2/helpers/ru_ofh_config_cli11_schema.h" #include "apps/units/flexible_du/split_8/helpers/ru_sdr_config_cli11_schema.h" -#include "apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.h" #include "dynamic_du_unit_config.h" #include "srsran/support/cli11_utils.h" #include "srsran/support/config_parsers.h" diff --git a/apps/units/flexible_du/split_dynamic/multicell_dynamic_du_factory.cpp b/apps/units/flexible_du/split_dynamic/multicell_dynamic_du_factory.cpp index 995ed320db..501cc88133 100644 --- a/apps/units/flexible_du/split_dynamic/multicell_dynamic_du_factory.cpp +++ b/apps/units/flexible_du/split_dynamic/multicell_dynamic_du_factory.cpp @@ -21,7 +21,7 @@ */ #include "multicell_dynamic_du_factory.h" -#include "apps/services/worker_manager.h" +#include "apps/services/worker_manager/worker_manager.h" #include "apps/units/flexible_du/split_7_2/helpers/ru_ofh_factories.h" #include "apps/units/flexible_du/split_8/helpers/ru_sdr_factories.h" #include "dynamic_du_translators.h" diff --git a/apps/units/flexible_du/split_helpers/flexible_du_factory.cpp b/apps/units/flexible_du/split_helpers/flexible_du_factory.cpp index 532b6f3b16..455105191a 100644 --- a/apps/units/flexible_du/split_helpers/flexible_du_factory.cpp +++ b/apps/units/flexible_du/split_helpers/flexible_du_factory.cpp @@ -22,7 +22,7 @@ #include "flexible_du_factory.h" #include "apps/services/e2/e2_metric_connector_manager.h" -#include "apps/services/worker_manager.h" +#include "apps/services/worker_manager/worker_manager.h" #include "apps/units/flexible_du/flexible_du_commands.h" #include "apps/units/flexible_du/o_du_high/du_high/du_high_config_translators.h" #include "apps/units/flexible_du/o_du_high/o_du_high_unit_factory.h" diff --git a/apps/units/flexible_du/split_helpers/multicell_flexible_du_factory.cpp b/apps/units/flexible_du/split_helpers/multicell_flexible_du_factory.cpp index dd5a98ab59..7966062b16 100644 --- a/apps/units/flexible_du/split_helpers/multicell_flexible_du_factory.cpp +++ b/apps/units/flexible_du/split_helpers/multicell_flexible_du_factory.cpp @@ -22,20 +22,14 @@ #include "multicell_flexible_du_factory.h" #include "apps/services/e2/e2_metric_connector_manager.h" -#include "apps/services/worker_manager.h" +#include "apps/services/worker_manager/worker_manager.h" #include "apps/units/flexible_du/flexible_du_commands.h" #include "apps/units/flexible_du/o_du_high/du_high/du_high_config_translators.h" #include "apps/units/flexible_du/o_du_high/o_du_high_unit_factory.h" #include "apps/units/flexible_du/o_du_low/o_du_low_unit_factory.h" -#include "apps/units/flexible_du/split_7_2/helpers/ru_ofh_factories.h" -#include "apps/units/flexible_du/split_8/helpers/ru_sdr_factories.h" #include "multicell_flexible_du_impl.h" - -#include "srsran/du/o_du.h" #include "srsran/du/o_du_factory.h" #include "srsran/e2/e2_du_metrics_connector.h" -#include "srsran/pcap/rlc_pcap.h" -#include "srsran/ru/ru_dummy_factory.h" using namespace srsran; diff --git a/apps/units/flexible_du/split_helpers/o_du_high_factory.cpp b/apps/units/flexible_du/split_helpers/o_du_high_factory.cpp index 3180913a3c..ce2cc1c080 100644 --- a/apps/units/flexible_du/split_helpers/o_du_high_factory.cpp +++ b/apps/units/flexible_du/split_helpers/o_du_high_factory.cpp @@ -21,7 +21,7 @@ */ #include "o_du_high_factory.h" -#include "apps/services/worker_manager.h" +#include "apps/services/worker_manager/worker_manager.h" #include "apps/units/flexible_du/o_du_high/du_high/du_high_config_translators.h" #include "apps/units/flexible_du/o_du_high/o_du_high_unit_config.h" diff --git a/include/srsran/adt/slotted_array.h b/include/srsran/adt/slotted_array.h index 79e46a8734..49115f8bf0 100644 --- a/include/srsran/adt/slotted_array.h +++ b/include/srsran/adt/slotted_array.h @@ -305,6 +305,19 @@ class slotted_array return false; } + /// Takes the element out of the container pointed by the given index + /// \param idx Position of the erased element in the array + T take(size_t idx) noexcept + { + srsran_assert(idx < this->vec.size(), "Out-of-bounds access to array: {}>={}", idx, this->vec.size()); + srsran_assert(this->contains(idx), "Empty position in index {}", idx); + + this->nof_elems--; + T obj = std::move(*this->vec[idx]); + this->vec[idx].reset(); + return obj; + } + /// Erase object pointed by the given iterator. Iterator must point to valid element /// \param it container iterator void erase(iterator it) noexcept { erase(this->extract_iterator_index(it)); } @@ -566,6 +579,10 @@ class slotted_id_table : private IdToIntConversion /// \param it container iterator void erase(iterator it) noexcept { sl_ar.erase(it); } + /// Takes the element out of the container pointed by the given index + /// \param id ID of the taken element in the table + T take(key_type id) noexcept { return sl_ar.take(id); } + /// Clear all elements of the container void clear() noexcept { sl_ar.clear(); } diff --git a/include/srsran/adt/unique_function.h b/include/srsran/adt/unique_function.h index 3482419913..622773d131 100644 --- a/include/srsran/adt/unique_function.h +++ b/include/srsran/adt/unique_function.h @@ -110,6 +110,19 @@ template struct is_unique_function> : std::true_type { }; +template +const auto& get_unique_heap_oper_ptr() +{ + static const task_details::heap_table_t heap_oper_table{}; + return heap_oper_table; +} +template +const auto& get_unique_small_oper_ptr() +{ + static const task_details::smallbuffer_table_t small_oper_table{}; + return small_oper_table; +} + } // namespace task_details template @@ -120,30 +133,30 @@ class unique_function using oper_table_t = task_details::oper_table_t; static const task_details::empty_table_t empty_table; - template - static const auto& get_heap_table() - { - static const task_details::heap_table_t heap_oper_table{}; - return heap_oper_table; - } - template - static const auto& get_small_table() - { - static const task_details::smallbuffer_table_t small_oper_table{}; - return small_oper_table; - } - public: static constexpr size_t capacity = Capacity; ///< size of buffer constexpr unique_function() noexcept : oper_ptr(&empty_table) {} + unique_function(const unique_function& rhs) = delete; template unique_function(const unique_function& rhs) = delete; + unique_function(unique_function&& rhs) noexcept + { + oper_ptr = rhs.oper_ptr; + rhs.oper_ptr = &empty_table; + oper_ptr->move(&rhs.buffer, &buffer); + } + template unique_function(unique_function&& rhs) noexcept { - using FunT = unique_function; + using OtherFunT = unique_function; + + if (rhs.oper_ptr == &empty_table) { + oper_ptr = &empty_table; + return; + } if constexpr (capacity >= Capacity2) { // The capacity of this is equal or higher. We can just move the buffer. @@ -155,13 +168,15 @@ class unique_function static_assert(not ForbidAlloc, "Failed to store the provided unique_function in unique_function specialization that forbids heap " "allocations."); - auto& heap_oper_table = get_heap_table(); - oper_ptr = &heap_oper_table; - if (rhs.oper_ptr == heap_oper_table) { + if (not rhs.is_in_small_buffer()) { + // The functor is in the heap. Just move it. + oper_ptr = rhs.oper_ptr; rhs.oper_ptr = &empty_table; oper_ptr->move(&rhs.buffer, &buffer); } else { - ptr = static_cast(new FunT{std::move(rhs)}); + // The functor is in the small buffer of the rhs. + oper_ptr = &task_details::get_unique_heap_oper_ptr(); + ptr = static_cast(new OtherFunT{std::move(rhs)}); } } } @@ -173,29 +188,44 @@ class unique_function if constexpr (sizeof(FunT) <= capacity) { // Fits in small buffer. - oper_ptr = &get_small_table(); + oper_ptr = &task_details::get_unique_small_oper_ptr(); ::new (&buffer) FunT(std::forward(rhs)); } else { // Does not fit in small buffer. static_assert( not ForbidAlloc, "Failed to store provided callback in unique_function specialization that forbids heap allocations."); - oper_ptr = &get_heap_table(); + oper_ptr = &task_details::get_unique_heap_oper_ptr(); ptr = static_cast(new FunT{std::forward(rhs)}); } } ~unique_function() { oper_ptr->dtor(&buffer); } + unique_function& operator=(const unique_function& rhs) = delete; template unique_function& operator=(const unique_function& rhs) = delete; + unique_function& operator=(unique_function&& rhs) noexcept + { + oper_ptr->dtor(&buffer); + oper_ptr = rhs.oper_ptr; + rhs.oper_ptr = &empty_table; + oper_ptr->move(&rhs.buffer, &buffer); + return *this; + } + template unique_function& operator=(unique_function&& rhs) noexcept { - using FunT = unique_function; + using OtherFunT = unique_function; oper_ptr->dtor(&buffer); + if (rhs.oper_ptr == &empty_table) { + oper_ptr = &empty_table; + return *this; + } + if constexpr (capacity >= Capacity2) { // The capacity of this is equal or higher. We can just move the buffer. oper_ptr = rhs.oper_ptr; @@ -206,13 +236,13 @@ class unique_function static_assert(not ForbidAlloc, "Failed to store the provided unique_function in unique_function specialization that forbids heap " "allocations."); - auto& heap_oper_table = get_heap_table(); - oper_ptr = &heap_oper_table; - if (rhs.oper_ptr == heap_oper_table) { + if (not rhs.is_in_small_buffer()) { + oper_ptr = rhs.oper_ptr; rhs.oper_ptr = &empty_table; oper_ptr->move(&rhs.buffer, &buffer); } else { - ptr = static_cast(new FunT{std::move(rhs)}); + oper_ptr = &task_details::get_unique_heap_oper_ptr(); + ptr = static_cast(new OtherFunT{std::move(rhs)}); } } return *this; @@ -226,14 +256,14 @@ class unique_function oper_ptr->dtor(&buffer); if constexpr (sizeof(FunT) <= capacity) { // Fits in small buffer. - oper_ptr = &get_small_table(); + oper_ptr = &task_details::get_unique_small_oper_ptr(); ::new (&buffer) FunT(std::forward(rhs)); } else { // Does not fit in small buffer. static_assert( not ForbidAlloc, "Failed to store provided callback in unique_function specialization that forbids heap allocations."); - oper_ptr = &get_heap_table(); + oper_ptr = &task_details::get_unique_heap_oper_ptr(); ptr = static_cast(new FunT{std::forward(rhs)}); } return *this; diff --git a/include/srsran/cu_cp/ue_configuration.h b/include/srsran/cu_cp/ue_configuration.h index c85dacd492..454e893246 100644 --- a/include/srsran/cu_cp/ue_configuration.h +++ b/include/srsran/cu_cp/ue_configuration.h @@ -30,8 +30,8 @@ namespace srs_cu_cp { /// UE configuration passed to CU-CP struct ue_configuration { std::chrono::seconds inactivity_timer{7200}; - /// Timeout for PDU session to be setup in seconds, before the UE is released. - std::chrono::seconds pdu_session_setup_timeout = std::chrono::seconds{2}; + /// Timeout for requesting a PDU session in seconds, before the UE is released. + std::chrono::seconds request_pdu_session_timeout = std::chrono::seconds{2}; }; } // namespace srs_cu_cp diff --git a/include/srsran/du/du_cell_config.h b/include/srsran/du/du_cell_config.h index 37ff45c81a..96414a2d90 100644 --- a/include/srsran/du/du_cell_config.h +++ b/include/srsran/du/du_cell_config.h @@ -218,6 +218,9 @@ struct du_cell_config { /// Defines the maximum allowable channel delay in slots when runnning in NTN mode. see TS38.300 section 16.14.2. unsigned ntn_cs_koffset = 0; + /// PUSCH Maximum of transmission layers. Limits the PUSCH maximum rank the UE is configrued with. + unsigned pusch_max_nof_layers = 1; + /// List of RAN slices to support in the scheduler. std::vector rrm_policy_members; }; diff --git a/include/srsran/ngap/ngap_configuration.h b/include/srsran/ngap/ngap_configuration.h index 8f6324d674..5e27aeafd4 100644 --- a/include/srsran/ngap/ngap_configuration.h +++ b/include/srsran/ngap/ngap_configuration.h @@ -38,7 +38,7 @@ struct ngap_configuration { gnb_id_t gnb_id{0, 22}; std::string ran_node_name; std::vector supported_tas; - std::chrono::seconds pdu_session_setup_timeout; // timeout for pdu session setup in seconds + std::chrono::seconds request_pdu_session_timeout; // timeout for requesting a pdu session in seconds }; } // namespace srs_cu_cp diff --git a/include/srsran/ngap/ngap_context.h b/include/srsran/ngap/ngap_context.h index d3b41940d0..abfd6aa692 100644 --- a/include/srsran/ngap/ngap_context.h +++ b/include/srsran/ngap/ngap_context.h @@ -39,8 +39,8 @@ struct ngap_context_t { std::string ran_node_name; std::vector supported_tas; std::vector served_guami_list; - uint16_t default_paging_drx = 256; // default paging drx - std::chrono::seconds pdu_session_setup_timeout; // timeout for PDU context setup in seconds + uint16_t default_paging_drx = 256; // default paging drx + std::chrono::seconds request_pdu_session_timeout; // timeout for requesting a PDU session in seconds std::vector get_supported_plmns() const { diff --git a/include/srsran/pdcp/pdcp_sn_size.h b/include/srsran/pdcp/pdcp_sn_size.h index a41857d6a9..90d975987f 100644 --- a/include/srsran/pdcp/pdcp_sn_size.h +++ b/include/srsran/pdcp/pdcp_sn_size.h @@ -22,6 +22,7 @@ #pragma once +#include "srsran/support/srsran_assert.h" #include "fmt/format.h" #include @@ -45,6 +46,7 @@ inline bool pdcp_sn_size_from_uint(pdcp_sn_size& sn_size, uint16_t num) /// \brief Convert PDCP SN size from enum to unsigned integer. constexpr uint8_t pdcp_sn_size_to_uint(pdcp_sn_size sn_size) { + srsran_assert(sn_size != pdcp_sn_size::invalid, "Invalid PDCP SN size"); return static_cast(sn_size); } diff --git a/include/srsran/phy/upper/channel_coding/ldpc/ldpc_encoder_buffer.h b/include/srsran/phy/upper/channel_coding/ldpc/ldpc_encoder_buffer.h index d38e27213c..ed19f80018 100644 --- a/include/srsran/phy/upper/channel_coding/ldpc/ldpc_encoder_buffer.h +++ b/include/srsran/phy/upper/channel_coding/ldpc/ldpc_encoder_buffer.h @@ -29,7 +29,7 @@ namespace srsran { /// \brief LDPC encoder output buffer interface. /// -/// This interface allows the LDPC encoder to provide the encoded bits to the rate matcher without using anintermediate +/// This interface allows the LDPC encoder to provide the encoded bits to the rate matcher without using an intermediate /// buffer. class ldpc_encoder_buffer { diff --git a/include/srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_buffer.h b/include/srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_buffer.h new file mode 100644 index 0000000000..934685352b --- /dev/null +++ b/include/srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_buffer.h @@ -0,0 +1,85 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "srsran/adt/span.h" +#include "srsran/adt/static_vector.h" +#include "srsran/phy/upper/codeblock_metadata.h" + +namespace srsran { + +/// \brief LDPC segmenter output buffer interface. +/// +/// This interface allows the LDPC segmenter to provide the CBs to the LDPC encoder without using an intermediate +/// buffer. +class ldpc_segmenter_buffer +{ +public: + /// Default destructor. + virtual ~ldpc_segmenter_buffer() = default; + + /// \brief Gets the codeblock metadata structure for a given codeblock. + /// + /// \param[in] cb_index Codeblock index (within the TB). + /// \return Codeblock metadata structure for the codeblock with index \c cb_index. + virtual codeblock_metadata get_cb_metadata(unsigned cb_index) const = 0; + + /// Gets the number of codeblocks resulting from TB segmentation. + virtual unsigned get_nof_codeblocks() const = 0; + + /// Gets the number of of segments that will have a short rate-matched length. + virtual unsigned get_nof_short_segments() const = 0; + + /// \brief Gets the number of information bits for a given segment. + /// + /// \param[in] cb_index Segment index. + /// \return Number of information bits for the segment with index \c cb_index. + virtual units::bits get_cb_info_bits(unsigned cb_index) const = 0; + + /// Gets the segment length resulting from TB segmentation. + virtual units::bits get_segment_length() const = 0; + + /// Gets the codeword length. + virtual units::bits get_cw_length() const = 0; + + /// Gets the zero padding length. + virtual units::bits get_zero_pad() const = 0; + + /// Gets the number of TB CRC bits. + virtual units::bits get_tb_crc_bits() const = 0; + + /// Gets the number of filler bits. + virtual units::bits get_nof_filler_bits() const = 0; + + /// Gets the codeblock length. + virtual unsigned get_rm_length(unsigned cb_index) const = 0; + + /// \brief Reads the segmented codeblock data, including the CB CRC, for the CB at index \c cb_index. + /// + /// \param[out] codeblock Read destination. + /// \param[in] transport_block Read source. + /// \param[in] cb_index Codeblock index (within the TB). + virtual void read_codeblock(bit_buffer codeblock, span transport_block, unsigned cb_index) const = 0; +}; + +} // namespace srsran diff --git a/include/srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx.h b/include/srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx.h index 10e44f6682..1c7da68277 100644 --- a/include/srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx.h +++ b/include/srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx.h @@ -25,12 +25,13 @@ #pragma once #include "srsran/adt/span.h" -#include "srsran/adt/static_vector.h" #include "srsran/phy/upper/channel_coding/ldpc/ldpc.h" #include "srsran/phy/upper/codeblock_metadata.h" namespace srsran { +class ldpc_segmenter_buffer; + /// Carries out the segmentation of a transport block into a number of codeblocks. class ldpc_segmenter_tx { @@ -38,20 +39,17 @@ class ldpc_segmenter_tx /// Default destructor. virtual ~ldpc_segmenter_tx() = default; - /// \brief Adds the CRC to the a transport block, carries out segmentation and computes all codeblock metadata for - /// later use (encoder and rate matching). + /// \brief Performs transport block segmentation on a codeblock basis without using an intermediate buffer. /// - /// First, the transport block CRC is attached, as per TS38.212 Section 7.2.1. Then, the transport block is split into - /// a number of segments and, if needed, a CRC is attached to each segment_tx. This is done according to TS38.212 - /// Section 5.2.2. The function also computes other segment_tx metadata (e.g., coded and rate-matched length) - /// according to TS38.212 Section 5.4.2.1. + /// If needed, a CRC is attached to the generated segment, according to TS38.212 Section 5.2.2. Moreover, the + /// transport block CRC is attached to the last segment, as per TS38.212 Section 7.2.1. The function also computes + /// relevant segment metadata (e.g., coded and rate-matched length) according to TS38.212 Section 5.4.2.1. /// - /// \param[out] described_segments Segments (unpacked, one bit per entry) and corresponding metadata. /// \param[in] transport_block The transport block to segment_tx (packed, one byte per entry). /// \param[in] cfg Parameters affecting splitting and codeblock metadata. - virtual void segment(static_vector& described_segments, - span transport_block, - const segmenter_config& cfg) = 0; + /// \return A reference to the LDPC segmenter buffer. + virtual const ldpc_segmenter_buffer& new_transmission(span transport_block, + const segmenter_config& cfg) = 0; }; } // namespace srsran diff --git a/include/srsran/phy/upper/channel_processors/pdsch/factories.h b/include/srsran/phy/upper/channel_processors/pdsch/factories.h index b2e3e7dadf..8538a82ab1 100644 --- a/include/srsran/phy/upper/channel_processors/pdsch/factories.h +++ b/include/srsran/phy/upper/channel_processors/pdsch/factories.h @@ -90,7 +90,7 @@ create_pdsch_processor_factory_sw(std::shared_ptr std::shared_ptr ptrs_factory); std::shared_ptr -create_pdsch_concurrent_processor_factory_sw(std::shared_ptr crc_factory, +create_pdsch_concurrent_processor_factory_sw(std::shared_ptr ldpc_segmenter_factory, std::shared_ptr ldpc_enc_factory, std::shared_ptr ldpc_rm_factory, std::shared_ptr prg_factory, @@ -118,4 +118,4 @@ create_pdsch_processor_asynchronous_pool(std::shared_ptr create_pdsch_processor_pool(std::shared_ptr pdsch_proc_factory, unsigned max_nof_processors); -} // namespace srsran \ No newline at end of file +} // namespace srsran diff --git a/include/srsran/phy/upper/channel_processors/pucch/pucch_demodulator.h b/include/srsran/phy/upper/channel_processors/pucch/pucch_demodulator.h index ac5e0149e5..bf440c9b6b 100644 --- a/include/srsran/phy/upper/channel_processors/pucch/pucch_demodulator.h +++ b/include/srsran/phy/upper/channel_processors/pucch/pucch_demodulator.h @@ -35,7 +35,7 @@ class pucch_demodulator public: /// Collects PUCCH Format 2 demodulation parameters. struct format2_configuration { - /// Port indexes used for the PUCCH reception. + /// Port indices used for the PUCCH reception. static_vector rx_ports; /// Lowest PRB index used for the PUCCH transmission within the resource grid {0, ..., 274}. unsigned first_prb; @@ -58,7 +58,7 @@ class pucch_demodulator /// Collects PUCCH Format 3 demodulation parameters. struct format3_configuration { - /// Port indexes used for the PUCCH reception. + /// Port indices used for the PUCCH reception. static_vector rx_ports; /// Lowest PRB index used for the PUCCH transmission within the resource grid {0, ..., 274}. unsigned first_prb; @@ -71,7 +71,7 @@ class pucch_demodulator unsigned nof_prb; /// Start symbol index within the slot {0, ..., 13}. unsigned start_symbol_index; - /// Number of symbols for the PUCCH transmission {4, ... , 14}. + /// Number of symbols for the PUCCH transmission {4, ..., 14}. unsigned nof_symbols; /// Radio Network Temporary Identifier, see parameter \f$n_{RNTI}\f$ in TS38.211 Section 6.3.2.5.1. uint16_t rnti; @@ -87,7 +87,35 @@ class pucch_demodulator /// Collects PUCCH Format 4 demodulation parameters. struct format4_configuration { - // Add here PUCCH demodulator parameters... + /// Port indices used for the PUCCH reception. + static_vector rx_ports; + /// Lowest PRB index used for the PUCCH transmission within the resource grid {0, ..., 274}. + unsigned first_prb; + /// \brief Index of the first PRB after frequency hopping as per TS38.213 Section 9.2.1. + /// + /// Lowest PRB index used for the PUCCH transmission within the BWP {0, ..., 274} if intra-slot frequency hopping is + /// enabled, empty otherwise. + std::optional second_hop_prb; + /// Start symbol index within the slot {0, ..., 13}. + unsigned start_symbol_index; + /// Number of symbols for the PUCCH transmission {4, ..., 14}. + unsigned nof_symbols; + /// Radio Network Temporary Identifier, see parameter \f$n_{RNTI}\f$ in TS38.211 Section 6.3.2.5.1. + uint16_t rnti; + /// Scrambling identifier, see parameter \f$n_{ID}\f$ in TS38.211 Section 6.3.2.5.1. Range is {0, ..., 1023}. + unsigned n_id; + /// Set to higher layer parameter \e additionalDMRS described in TS38.331 Section 6.3.2, Information Element \e + /// PUCCH-FormatConfig. + bool additional_dmrs; + /// Set to higher layer parameter \e pi2BPSK described in TS38.331 Section 6.3.2, Information Element \e + /// PUCCH-FormatConfig. + bool pi2_bpsk; + /// Set to higher layer parameter \e occ-Length described in TS38.331 Section 6.3.2, Information Element \e + /// PUCCH-format4. + unsigned occ_length; + /// Set to higher layer parameter \e occ-Index described in TS38.331 Section 6.3.2, Information Element \e + /// PUCCH-format4. + unsigned occ_index; }; /// Default destructor. diff --git a/include/srsran/phy/upper/pucch_formats_3_4_helpers.h b/include/srsran/phy/upper/pucch_formats_3_4_helpers.h index 98ef56b719..b450453719 100644 --- a/include/srsran/phy/upper/pucch_formats_3_4_helpers.h +++ b/include/srsran/phy/upper/pucch_formats_3_4_helpers.h @@ -27,6 +27,7 @@ #include "srsran/adt/bounded_integer.h" #include "srsran/phy/support/mask_types.h" +#include "srsran/phy/upper/equalization/modular_ch_est_list.h" #include "srsran/ran/pucch/pucch_constants.h" namespace srsran { @@ -131,4 +132,107 @@ inline symbol_slot_mask get_pucch_formats_3_4_dmrs_symbol_mask( return mask; } +/// \brief Gets REs and channel estimates given a PUCCH Format 3/4 resource, performs equalization and reverts +/// transform precoding. +/// +/// Extracts and loads the inner buffers with the PUCCH control data RE from the provided \c resource_grid, and their +/// corresponding channel estimates from \c channel_ests. The DM-RS RE are skipped. Equalization and transform +/// precoding reversion is done symbol by symbol. +/// +/// \param[out] eq_re Destination buffer for resource elements at the equalizer output. +/// \param[out] eq_noise_vars Destination buffer for noise variances at the equalizer output. +/// \param[out] equalizer Channel equalizer. +/// \param[out] precoder Transform precoder. +/// \param[in] grid Input resource grid. +/// \param[in] estimates Channel estimates. +/// \param[in] dmrs_symb_mask The symbol mask for symbols containing DM-RS for the PUCCH configuration. +/// \param[in] start_symbol_index Start symbol index within the slot {0, ..., 13}. +/// \param[in] nof_symbols Number of symbols assigned to PUCCH resource. +/// \param[in] nof_prb Number of PRB assigned to PUCCH resource. +/// \param[in] first_prb Lowest PRB index used for the PUCCH transmission within the resource grid. +/// \param[in] second_hop_prb Lowest PRB index used for the PUCCH transmission within the BWP {0, ..., 274} +/// if intra-slot frequency hopping is enabled, empty otherwise. +/// \param[in] rx_ports Port indexes used for the PUCCH reception. +inline void pucch_3_4_extract_and_equalize(span eq_re, + span eq_noise_vars, + channel_equalizer& equalizer, + transform_precoder& precoder, + const resource_grid_reader& grid, + const channel_estimate& estimates, + const symbol_slot_mask& dmrs_symb_mask, + unsigned start_symbol_index, + unsigned nof_symbols, + unsigned nof_prb, + unsigned first_prb, + std::optional second_hop_prb, + span rx_ports) +{ + // Number of receive antenna ports. + auto nof_rx_ports = static_cast(rx_ports.size()); + + // Number of REs per OFDM symbol. + unsigned nof_re_symb = nof_prb * NRE; + + // Index of the first symbol allocated to the second hop, when intra-slot frequency hopping is enabled. + unsigned second_hop_start = (nof_symbols / 2) + start_symbol_index; + + // Extract the Rx port noise variances from the channel estimation. + std::array noise_var_estimates; + for (unsigned i_port = 0; i_port != nof_rx_ports; ++i_port) { + noise_var_estimates[i_port] = estimates.get_noise_variance(i_port, 0); + } + + for (unsigned i_symbol = start_symbol_index, i_symbol_end = start_symbol_index + nof_symbols; + i_symbol != i_symbol_end; + ++i_symbol) { + // Skip DM-RS symbols. + if (dmrs_symb_mask.test(i_symbol - start_symbol_index)) { + continue; + } + + // Calculate the lowest resource element containing the PUCCH resource within the OFDM symbol. + unsigned first_subc = first_prb * NRE; + if (second_hop_prb.has_value() && (i_symbol >= second_hop_start)) { + // Intra-slot frequency hopping. + first_subc = second_hop_prb.value() * NRE; + } + + // Create modular buffers to hold the spans for this symbol. + modular_re_buffer_reader re_symb(nof_rx_ports, nof_re_symb); + modular_ch_est_list estimates_symb(nof_re_symb, nof_rx_ports, pucch_constants::MAX_LAYERS); + + for (unsigned i_port = 0, i_port_end = nof_rx_ports; i_port != i_port_end; ++i_port) { + // Extract data RE from the resource grid. + unsigned i_port_grid = rx_ports[i_port]; + span re_symb_view = grid.get_view(i_port_grid, i_symbol).subspan(first_subc, nof_re_symb); + re_symb.set_slice(i_port, re_symb_view); + + // Extract estimates from the estimates buffer. + estimates_symb.set_channel( + estimates.get_symbol_ch_estimate(i_symbol, i_port).subspan(first_subc, nof_re_symb), i_port, 0); + } + + // Get a view of the equalized RE buffer for a single symbol. + span eq_re_symb = eq_re.first(nof_re_symb); + + // Get a view of the equalized RE buffer for a single symbol. + span eq_noise_vars_symb = eq_noise_vars.first(nof_re_symb); + + // Equalize the data RE for a single symbol. + equalizer.equalize(eq_re_symb, + eq_noise_vars_symb, + re_symb, + estimates_symb, + span(noise_var_estimates).first(nof_rx_ports), + 1.0F); + + // Revert transform precoding for a single symbol. + precoder.deprecode_ofdm_symbol(eq_re_symb, eq_re_symb); + + // Advance the equalized RE and noise vars views. + eq_re = eq_re.last(eq_re.size() - nof_re_symb); + eq_noise_vars = eq_noise_vars.last(eq_noise_vars.size() - nof_re_symb); + } +} + } // namespace srsran diff --git a/include/srsran/phy/upper/pucch_orthogonal_sequence.h b/include/srsran/phy/upper/pucch_orthogonal_sequence.h index 9203b8b3a6..1bcd03f6f7 100644 --- a/include/srsran/phy/upper/pucch_orthogonal_sequence.h +++ b/include/srsran/phy/upper/pucch_orthogonal_sequence.h @@ -21,7 +21,7 @@ */ /// \file -/// \brief Generation of PUCCH Format 1 orthogonal sequences. +/// \brief Generation of PUCCH orthogonal sequences. #pragma once @@ -35,49 +35,11 @@ namespace srsran { /// Generator of orthogonal sequences \e w for PUCCH Format 1. -class pucch_orthogonal_sequence +class pucch_orthogonal_sequence_format1 { -private: - /// Alias for sequence table type. - using w_array = - std::array, pucch_constants::FORMAT1_N_MAX>, - pucch_constants::FORMAT1_N_MAX>; - - /// \brief Coefficients \f$\phi(m)\f$ for sequence generation. - /// - /// See TS38.211 Table 6.3.2.4.1-2: Orthogonal sequences for PUCCH Format 1. - static constexpr std::array< - std::array, pucch_constants::FORMAT1_N_MAX>, - pucch_constants::FORMAT1_N_MAX> - pucch_format1_phi = { - {{{{0}, {}, {}, {}, {}, {}, {}}}, - {{{0, 0}, {0, 1}, {}, {}, {}, {}, {}}}, - {{{0, 0, 0}, {0, 1, 2}, {0, 2, 1}, {}, {}, {}, {}}}, - {{{0, 0, 0, 0}, {0, 2, 0, 2}, {0, 0, 2, 2}, {0, 2, 2, 0}, {}, {}, {}}}, - {{{0, 0, 0, 0, 0}, {0, 1, 2, 3, 4}, {0, 2, 4, 1, 3}, {0, 3, 1, 4, 2}, {0, 4, 3, 2, 1}, {}, {}}}, - {{{0, 0, 0, 0, 0, 0}, - {0, 1, 2, 3, 4, 5}, - {0, 2, 4, 0, 2, 4}, - {0, 3, 0, 3, 0, 3}, - {0, 4, 2, 0, 4, 2}, - {0, 5, 4, 3, 2, 1}, - {}}}, - {{{0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 3, 4, 5, 6}, - {0, 2, 4, 6, 1, 3, 5}, - {0, 3, 6, 2, 5, 1, 4}, - {0, 4, 1, 5, 2, 6, 3}, - {0, 5, 3, 1, 6, 4, 2}, - {0, 6, 5, 4, 3, 2, 1}}}}}; - - /// Table with the actual sequences \e w. - w_array orthogonal_sequence; - /// Table with the conjugated sequences \e w. - w_array orthogonal_sequence_conj; - public: /// Constructor: builds the sequences \e w from the coefficients in \ref pucch_format1_phi. - pucch_orthogonal_sequence() + pucch_orthogonal_sequence_format1() { for (unsigned n_pucch = 0, max_n_pucch = pucch_constants::FORMAT1_N_MAX; n_pucch != max_n_pucch; ++n_pucch) { for (unsigned i_seq = 0; i_seq != pucch_constants::FORMAT1_N_MAX; ++i_seq) { @@ -128,6 +90,85 @@ class pucch_orthogonal_sequence return span(orthogonal_sequence_conj[n_pucch - 1][i]).first(n_pucch); } + +private: + /// Alias for sequence table type. + using w_array = + std::array, pucch_constants::FORMAT1_N_MAX>, + pucch_constants::FORMAT1_N_MAX>; + + /// \brief Coefficients \f$\phi(m)\f$ for sequence generation. + /// + /// See TS38.211 Table 6.3.2.4.1-2: Orthogonal sequences for PUCCH Format 1. + static constexpr std::array< + std::array, pucch_constants::FORMAT1_N_MAX>, + pucch_constants::FORMAT1_N_MAX> + pucch_format1_phi = { + {{{{0}, {}, {}, {}, {}, {}, {}}}, + {{{0, 0}, {0, 1}, {}, {}, {}, {}, {}}}, + {{{0, 0, 0}, {0, 1, 2}, {0, 2, 1}, {}, {}, {}, {}}}, + {{{0, 0, 0, 0}, {0, 2, 0, 2}, {0, 0, 2, 2}, {0, 2, 2, 0}, {}, {}, {}}}, + {{{0, 0, 0, 0, 0}, {0, 1, 2, 3, 4}, {0, 2, 4, 1, 3}, {0, 3, 1, 4, 2}, {0, 4, 3, 2, 1}, {}, {}}}, + {{{0, 0, 0, 0, 0, 0}, + {0, 1, 2, 3, 4, 5}, + {0, 2, 4, 0, 2, 4}, + {0, 3, 0, 3, 0, 3}, + {0, 4, 2, 0, 4, 2}, + {0, 5, 4, 3, 2, 1}, + {}}}, + {{{0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 3, 4, 5, 6}, + {0, 2, 4, 6, 1, 3, 5}, + {0, 3, 6, 2, 5, 1, 4}, + {0, 4, 1, 5, 2, 6, 3}, + {0, 5, 3, 1, 6, 4, 2}, + {0, 6, 5, 4, 3, 2, 1}}}}}; + + /// Table with the actual sequences \e w. + w_array orthogonal_sequence; + /// Table with the conjugated sequences \e w. + w_array orthogonal_sequence_conj; +}; + +/// Generator of orthogonal sequences \e w for PUCCH Format 4. +class pucch_orthogonal_sequence_format4 +{ +public: + /// \brief Gets an entire PUCCH Format 4 orthogonal sequence. + /// + /// \param[in] n_pucch Length of the PUCCH Format 4 sequence + /// \f$N_{\text{SF},m'}^{\text{PUCCH},4} \in \{2, 4\}\f$. + /// \param[in] i Sequence index \f$i \in \{0, \dots, N_{\text{SF},m'}^{\text{PUCCH},4} - 1\}\f$. + /// \returns The requested sequence. + /// \warning An assertion is thrown if the inputs do not match the limits above. + static span get_sequence(unsigned n_pucch, unsigned i) + { + srsran_assert((n_pucch == 2) || (n_pucch == 4), "Invalid n_pucch {}: valid values 2 or 4.", n_pucch); + srsran_assert(i < n_pucch, "Invalid sequence index i = {}, valid values from 0 to {}.", i, n_pucch - 1); + + if (n_pucch == 2) { + return pucch_format4_length2[i]; + } + return pucch_format4_length4[i]; + } + +private: + static constexpr auto one = cf_t(1.0F, 0.0F); + static constexpr auto neg_one = cf_t(-1.0F, 0.0F); + static constexpr auto j = cf_t(0.0F, 1.0F); + static constexpr auto neg_j = cf_t(0.0F, -1.0F); + + static constexpr std::array, 2> pucch_format4_length2 = {{ + {one, one, one, one, one, one, one, one, one, one, one, one}, + {one, one, one, one, one, one, neg_one, neg_one, neg_one, neg_one, neg_one, neg_one}, + }}; + + static constexpr std::array, 4> pucch_format4_length4 = {{ + {one, one, one, one, one, one, one, one, one, one, one, one}, + {one, one, one, neg_j, neg_j, neg_j, neg_one, neg_one, neg_one, j, j, j}, + {one, one, one, neg_one, neg_one, neg_one, one, one, one, neg_one, neg_one, neg_one}, + {one, one, one, j, j, j, neg_one, neg_one, neg_one, neg_j, neg_j, neg_j}, + }}; }; } // namespace srsran diff --git a/include/srsran/phy/upper/signal_processors/srs/formatters.h b/include/srsran/phy/upper/signal_processors/srs/formatters.h index 36805e2a5b..1e437fde44 100644 --- a/include/srsran/phy/upper/signal_processors/srs/formatters.h +++ b/include/srsran/phy/upper/signal_processors/srs/formatters.h @@ -27,6 +27,7 @@ #include "srsran/ran/srs/srs_channel_matrix.h" #include "srsran/ran/srs/srs_channel_matrix_formatters.h" #include "srsran/ran/srs/srs_resource_formatter.h" +#include namespace fmt { @@ -77,7 +78,9 @@ struct formatter { { helper.format_always(ctx, "t_align={:.1}us", config.time_alignment.time_alignment * 1e6); - helper.format_always(ctx, "noise_var={:.3e}", config.noise_variance); + helper.format_always(ctx, "epre={:.3e}dB", config.epre_dB.value_or(std::numeric_limits::quiet_NaN())); + helper.format_always( + ctx, "noise_var={:.3e}", config.noise_variance.value_or(std::numeric_limits::quiet_NaN())); // Get matrix Frobenius norm. float frobenius_norm = config.channel_matrix.frobenius_norm(); @@ -98,4 +101,4 @@ struct formatter { } }; -} // namespace fmt \ No newline at end of file +} // namespace fmt diff --git a/include/srsran/phy/upper/signal_processors/srs/srs_estimator_configuration_validator.h b/include/srsran/phy/upper/signal_processors/srs/srs_estimator_configuration_validator.h index 0bf7f7ac58..bf63947465 100644 --- a/include/srsran/phy/upper/signal_processors/srs/srs_estimator_configuration_validator.h +++ b/include/srsran/phy/upper/signal_processors/srs/srs_estimator_configuration_validator.h @@ -22,6 +22,9 @@ #pragma once +#include "srsran/adt/expected.h" +#include + namespace srsran { struct srs_estimator_configuration; @@ -34,8 +37,8 @@ class srs_estimator_configuration_validator virtual ~srs_estimator_configuration_validator() = default; /// \brief Validates SRS channel estimator configuration parameters. - /// \return True if the parameters contained in \c config are supported, false otherwise. - virtual bool is_valid(const srs_estimator_configuration& config) const = 0; + /// \return A success if the parameters contained in \c config are supported, an error message otherwise. + virtual error_type is_valid(const srs_estimator_configuration& config) const = 0; }; } // namespace srsran diff --git a/include/srsran/phy/upper/signal_processors/srs/srs_estimator_result.h b/include/srsran/phy/upper/signal_processors/srs/srs_estimator_result.h index 2270a37106..bc9f266e55 100644 --- a/include/srsran/phy/upper/signal_processors/srs/srs_estimator_result.h +++ b/include/srsran/phy/upper/signal_processors/srs/srs_estimator_result.h @@ -23,8 +23,8 @@ #pragma once #include "srsran/phy/support/time_alignment_estimator/time_alignment_measurement.h" -#include "srsran/ran/phy_time_unit.h" #include "srsran/ran/srs/srs_channel_matrix.h" +#include namespace srsran { @@ -32,8 +32,10 @@ namespace srsran { struct srs_estimator_result { /// Wideband estimated channel matrix. srs_channel_matrix channel_matrix; - /// Wideband measured noise variance. - float noise_variance; + /// Wideband energy per resource element (EPRE), in decibel. + std::optional epre_dB; + /// Wideband measured noise variance as a linear quantity. + std::optional noise_variance; /// Measured time alignment. time_alignment_measurement time_alignment; }; diff --git a/include/srsran/phy/upper/uplink_processor.h b/include/srsran/phy/upper/uplink_processor.h index 23fd96017c..37f27623d0 100644 --- a/include/srsran/phy/upper/uplink_processor.h +++ b/include/srsran/phy/upper/uplink_processor.h @@ -173,12 +173,12 @@ class uplink_pdu_validator virtual error_type is_valid(const pucch_processor::format4_configuration& config) const = 0; /// \brief Validates PUSCH configuration parameters. - /// \return A success if the parameters contained in \c pdu are supported, an error message otherwise. + /// \return A success if the parameters contained in \c config are supported, an error message otherwise. virtual error_type is_valid(const pusch_processor::pdu_t& config) const = 0; /// \brief Validates Sounding Reference Signals channel estimator configuration. - /// \return True if the parameters contained in \c config are supported, false otherwise. - virtual bool is_valid(const srs_estimator_configuration& config) const = 0; + /// \return A success if the parameters contained in \c config are supported, an error message otherwise. + virtual error_type is_valid(const srs_estimator_configuration& config) const = 0; }; /// \brief Pool of uplink processors. diff --git a/include/srsran/phy/upper/upper_phy_factories.h b/include/srsran/phy/upper/upper_phy_factories.h index 893b6bb82f..9ddf670331 100644 --- a/include/srsran/phy/upper/upper_phy_factories.h +++ b/include/srsran/phy/upper/upper_phy_factories.h @@ -326,6 +326,8 @@ struct upper_phy_config { unsigned ul_bw_rb; /// Request headroom size in slots. unsigned nof_slots_request_headroom; + /// Maximum number of layers for PUSCH transmissions. + unsigned pusch_max_nof_layers; /// List of active subcarrier spacing, indexed by numerology. std::array active_scs; /// Receive buffer pool configuration. diff --git a/include/srsran/ran/pucch/pucch_constants.h b/include/srsran/ran/pucch/pucch_constants.h index 08e5944c81..ae93f35cc0 100644 --- a/include/srsran/ran/pucch/pucch_constants.h +++ b/include/srsran/ran/pucch/pucch_constants.h @@ -71,6 +71,15 @@ static constexpr unsigned FORMAT3_MAX_NSYMB = 14; /// Maximum number of PRBs allocated for PUCCH Format 3. static constexpr unsigned FORMAT3_MAX_NPRB = 16; +/// Minimum number of OFDM symbols allocated to a PUCCH Format 4 transmission. +static constexpr unsigned FORMAT4_MIN_NSYMB = 4; + +/// Maximum number of symbols that PUCCH Format 4 can transmit. +static constexpr unsigned FORMAT4_MAX_NSYMB = 14; + +/// Maximum number of PRBs allocated for PUCCH Format 4. +static constexpr unsigned FORMAT4_MAX_NPRB = 1; + /// \brief Maximum number of resource elements used by PUCCH. /// /// It corresponds to PUCCH Format 3 with a bandwidth of 16 PRBs and a duration of 14 symbols, two of which are occupied diff --git a/include/srsran/ran/pusch/pusch_tpmi_select.h b/include/srsran/ran/pusch/pusch_tpmi_select.h index cca806de2a..469ac1d7bc 100644 --- a/include/srsran/ran/pusch/pusch_tpmi_select.h +++ b/include/srsran/ran/pusch/pusch_tpmi_select.h @@ -79,10 +79,12 @@ class pusch_tpmi_select_info /// /// \param[in] channel Channel coefficient matrix. /// \param[in] noise_variance Linear noise variance. +/// \param[in] max_rank Maximum number of layers. /// \param[in] codebook_subset Transmission scheme codebook subset. /// \return The TPMI information given the channel coefficients and noise variance. pusch_tpmi_select_info get_tpmi_select_info(const srs_channel_matrix& channel, float noise_variance, + unsigned max_rank, tx_scheme_codebook_subset codebook_subset); } // namespace srsran diff --git a/include/srsran/ran/s_nssai.h b/include/srsran/ran/s_nssai.h index 82f790915f..80048edf4c 100644 --- a/include/srsran/ran/s_nssai.h +++ b/include/srsran/ran/s_nssai.h @@ -22,18 +22,70 @@ #pragma once +#include "srsran/adt/expected.h" #include namespace srsran { -/// Network slice information, see TS 38.413, Sec. 9.3.1.24 +/// Slice/Service Type (SST), as per TS 23.003, 28.4. When set, it takes at most 8 bits. +class slice_service_type +{ +public: + constexpr slice_service_type() = default; + constexpr explicit slice_service_type(uint8_t val_) : val(val_) {} + + /// Determines whether the SST is within the range of standardized SSTs, as per TS 23.501. + bool is_standardized() const { return val < 128; } + bool is_operator_specific() const { return not is_standardized(); } + + uint8_t value() const { return val; } + + bool operator==(const slice_service_type& rhs) const { return val == rhs.val; } + bool operator!=(const slice_service_type& rhs) const { return not(val != rhs.val); } + +private: + uint8_t val = 0; +}; + +/// Slice Differentiator (SD) type, as per TS 23.003, 28.4. When set, it takes at most 24 bits. +class slice_differentiator +{ + static constexpr uint32_t default_val = 0xffffffU; + +public: + constexpr slice_differentiator() = default; + + bool is_default() const { return val == default_val; } + bool is_set() const { return val != default_val; } + + uint32_t value() const { return val; } + + bool operator==(const slice_differentiator& rhs) const { return val == rhs.val; } + bool operator!=(const slice_differentiator& rhs) const { return not(val != rhs.val); } + + static expected create(uint32_t new_val) + { + if (new_val > default_val) { + return make_unexpected(default_error_t{}); + } + slice_differentiator sd; + sd.val = new_val; + return sd; + } + +private: + uint32_t val = default_val; +}; + +/// Network Slice Selection Assistance Information, as per TS 23.003 and TS 23.501. struct s_nssai_t { - /// Slice/Service type (max 8bits). - uint8_t sst = 0; - /// Slice Differentiator (max 24bits). - std::optional sd; + /// Slice/Service type. + slice_service_type sst; + /// Slice Differentiator (SD). + slice_differentiator sd; bool operator==(const s_nssai_t& other) const { return sst == other.sst && sd == other.sd; } + bool operator!=(const s_nssai_t& other) const { return not(*this == other); } }; } // namespace srsran diff --git a/include/srsran/security/security.h b/include/srsran/security/security.h index a8cb192eac..b3a1a85604 100644 --- a/include/srsran/security/security.h +++ b/include/srsran/security/security.h @@ -190,6 +190,7 @@ using sec_short_mac_i = std::array; struct security_context { srslog::basic_logger& logger = srslog::fetch_basic_logger("SEC"); security::sec_key k; + uint8_t ncc = 0; security::supported_algorithms supported_int_algos; security::supported_algorithms supported_enc_algos; sec_selected_algos sel_algos; @@ -199,6 +200,7 @@ struct security_context { ~security_context() = default; security_context(const security_context& sec_ctxt) : k(sec_ctxt.k), + ncc(sec_ctxt.ncc), supported_int_algos(sec_ctxt.supported_int_algos), supported_enc_algos(sec_ctxt.supported_enc_algos), sel_algos(sec_ctxt.sel_algos), @@ -211,6 +213,7 @@ struct security_context { return *this; } k = sec_ctxt.k; + ncc = sec_ctxt.ncc; supported_int_algos = sec_ctxt.supported_int_algos; supported_enc_algos = sec_ctxt.supported_enc_algos; sel_algos = sec_ctxt.sel_algos; diff --git a/include/srsran/srsvec/aligned_vec.h b/include/srsran/srsvec/aligned_vec.h deleted file mode 100644 index 8dc63297ea..0000000000 --- a/include/srsran/srsvec/aligned_vec.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#pragma once - -#include "srsran/adt/span.h" - -namespace srsran { -namespace srsvec { - -namespace detail { -void* mem_alloc(std::size_t size); -void mem_free(void* ptr); -} // namespace detail - -/// Type to store a dynamic amount of aligned contiguous elements. -template -class aligned_vec : public span -{ - void dealloc() { detail::mem_free(this->data()); } - -public: - aligned_vec& operator=(aligned_vec&& other) = delete; - aligned_vec& operator=(const aligned_vec& other) = delete; - aligned_vec(const aligned_vec& other) = delete; - aligned_vec(aligned_vec&& other) noexcept = delete; - - aligned_vec() = default; - explicit aligned_vec(std::size_t size) { resize(size); } - ~aligned_vec() { dealloc(); } - - void resize(std::size_t new_size) - { - if (new_size == this->size()) { - return; - } - - dealloc(); - - T* p = reinterpret_cast(detail::mem_alloc(sizeof(T) * new_size)); - span::operator=(span(p, new_size)); - } -}; - -} // namespace srsvec -} // namespace srsran diff --git a/include/srsran/support/cli11_utils.h b/include/srsran/support/cli11_utils.h index ee0ab5c320..0b44d3ab63 100644 --- a/include/srsran/support/cli11_utils.h +++ b/include/srsran/support/cli11_utils.h @@ -23,6 +23,7 @@ #pragma once #include "external/CLI/CLI11.hpp" +#include namespace srsran { diff --git a/include/srsran/support/cpu_architecture_info.h b/include/srsran/support/cpu_architecture_info.h index fb99a98d7e..947c11e04f 100644 --- a/include/srsran/support/cpu_architecture_info.h +++ b/include/srsran/support/cpu_architecture_info.h @@ -54,7 +54,7 @@ class cpu_architecture_info static cpu_description discover_cpu_architecture(); /// Stores the CPU description. - static const cpu_description cpu_desc; + const cpu_description cpu_desc{discover_cpu_architecture()}; /// Default constructor. cpu_architecture_info() = default; diff --git a/include/srsran/support/rtsan.h b/include/srsran/support/rtsan.h new file mode 100644 index 0000000000..1e939da9ad --- /dev/null +++ b/include/srsran/support/rtsan.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/// \file +/// \brief Defines helper macros to use RTSAN, if supported. + +#pragma once + +#ifdef __has_feature +#if __has_feature(realtime_sanitizer) +#define SRSRAN_RTSAN_ENABLED +#endif +#endif + +#ifdef SRSRAN_RTSAN_ENABLED +#include + +#define SRSRAN_RTSAN_NONBLOCKING [[clang::nonblocking]] +#define SRSRAN_RTSAN_SCOPED_DISABLER(VAR) __rtsan::ScopedDisabler(VAR); +#else +#define SRSRAN_RTSAN_NONBLOCKING +#define SRSRAN_RTSAN_SCOPED_DISABLER(VAR) +#endif diff --git a/include/srsran/support/sdu_window.h b/include/srsran/support/sdu_window.h index 8e0875dcd5..e81c02581d 100644 --- a/include/srsran/support/sdu_window.h +++ b/include/srsran/support/sdu_window.h @@ -22,24 +22,66 @@ #pragma once +#include "srsran/adt/circular_map.h" +#include "srsran/support/srsran_assert.h" #include #include namespace srsran { -template +/// \brief This class provides a container for the Tx/Rx windows holding SDU info objects that are indexed by Sequence +/// Numbers (SN). +/// +/// \tparam T Storage Type. +/// \tparam PREFIXED_LOGGER An implementation of a prefixed_logger for logging. +template class sdu_window { public: - virtual ~sdu_window() = default; - virtual T& add_sn(size_t sn) = 0; - virtual void remove_sn(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; + sdu_window(PREFIXED_LOGGER& logger_, size_t size) : logger(logger_), window(size) {} + + T& add_sn(size_t sn) + { + if (has_sn(sn)) { + logger.log_error("sn={} already present in window, overwriting.", sn); + srsran_assertion_failure("sn={} already present in window.", sn); + } else { + logger.log_debug("Adding sn={} to window.", sn); + } + + window.overwrite(sn, T()); + + return window[sn]; + } + + void remove_sn(size_t sn) + { + if (not has_sn(sn)) { + logger.log_error("Cannot remove sn={} because not contained in the window.", sn); + srsran_assertion_failure("Cannot remove sn={} because not contained in the window.", sn); + return; + } + + logger.log_debug("Removing sn={} from window", sn); + window.erase(sn); + } + + T& operator[](size_t sn) { return window[sn]; } + const T& operator[](size_t sn) const { return window[sn]; } + + size_t size() const { return window.size(); } + + bool full() const { return window.full(); } + + bool empty() const { return window.empty(); } + + void clear() { window.clear(); } + + bool has_sn(uint32_t sn) const { return window.contains(sn); } + +private: + PREFIXED_LOGGER& logger; + circular_map window; }; } // namespace srsran diff --git a/include/srsran/support/tsan_options.h b/include/srsran/support/tsan_options.h index f36be25f6d..e431d269a6 100644 --- a/include/srsran/support/tsan_options.h +++ b/include/srsran/support/tsan_options.h @@ -52,6 +52,8 @@ const char* __tsan_default_suppressions() "called_from_lib:libpgm-5.2.so\n" "called_from_lib:libusb*\n" "called_from_lib:libuhd*\n" + "called_from_lib:librte_eal*\n" + "called_from_lib:librte_net_iavf*\n" // Races detected inside uninstrumented libraries. This may hide legit races if any of the libraries appear in the // backtrace "race:libusb*\n" diff --git a/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.cpp b/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.cpp index 3e9228bc28..373b04cb3e 100644 --- a/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.cpp +++ b/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.cpp @@ -199,14 +199,14 @@ std::optional get_ssb_rsrp(const rrc_meas_result_nr& meas_result) void cell_meas_manager::report_measurement(ue_index_t ue_index, const rrc_meas_results& meas_results) { - logger.debug("ue={} Received measurement result with meas_id={}", ue_index, meas_results.meas_id); + logger.debug("ue={}: Received measurement result with meas_id={}", ue_index, meas_results.meas_id); auto& ue_meas_context = ue_mng.get_measurement_context(ue_index); // Verify meas_id is valid. if (ue_meas_context.meas_id_to_meas_context.find(meas_results.meas_id) == ue_meas_context.meas_id_to_meas_context.end()) { - logger.debug("ue={} Measurement result for unknown meas_id={} received", ue_index, meas_results.meas_id); + logger.debug("ue={}: Measurement result for unknown meas_id={} received", ue_index, meas_results.meas_id); return; } diff --git a/lib/cu_cp/ngap_repository.cpp b/lib/cu_cp/ngap_repository.cpp index 74b4569651..eca1478b3b 100644 --- a/lib/cu_cp/ngap_repository.cpp +++ b/lib/cu_cp/ngap_repository.cpp @@ -48,7 +48,7 @@ ngap_interface* ngap_repository::add_ngap(amf_index_t amf_index, const cu_cp_con ngap_configuration ngap_cfg = {cfg.cu_cp.node.gnb_id, cfg.cu_cp.node.ran_node_name, config.supported_tas, - cfg.cu_cp.ue.pdu_session_setup_timeout}; + cfg.cu_cp.ue.request_pdu_session_timeout}; std::unique_ptr ngap_entity = create_ngap(ngap_cfg, ngap_ctxt.ngap_to_cu_cp_notifier, *config.n2_gw, diff --git a/lib/cu_cp/routines/mobility/intra_cu_handover_routine.cpp b/lib/cu_cp/routines/mobility/intra_cu_handover_routine.cpp index b8194af83d..0f72c68696 100644 --- a/lib/cu_cp/routines/mobility/intra_cu_handover_routine.cpp +++ b/lib/cu_cp/routines/mobility/intra_cu_handover_routine.cpp @@ -200,7 +200,7 @@ void intra_cu_handover_routine::operator()(coro_contextget_rrc_ue()->generate_meas_config(source_rrc_context.meas_cfg), true, /* Reestablish SRBs */ true /* Reestablish DRBs */, - true, /* Update keys */ + target_ue->get_security_manager().get_ncc(), /* Update keys */ target_cell_sib1.copy(), logger)) { logger.warning("ue={}: \"{}\" Failed to fill RrcReconfiguration", request.source_ue_index, name()); @@ -338,4 +338,4 @@ bool intra_cu_handover_routine::add_security_context_to_bearer_context_modificat } return true; -} \ No newline at end of file +} diff --git a/lib/cu_cp/routines/pdu_session_resource_modification_routine.cpp b/lib/cu_cp/routines/pdu_session_resource_modification_routine.cpp index 3f3674d40a..8f53764a36 100644 --- a/lib/cu_cp/routines/pdu_session_resource_modification_routine.cpp +++ b/lib/cu_cp/routines/pdu_session_resource_modification_routine.cpp @@ -23,6 +23,7 @@ #include "pdu_session_resource_modification_routine.h" #include "pdu_session_routine_helpers.h" #include "srsran/cu_cp/ue_task_scheduler.h" +#include "srsran/ran/cause/e1ap_cause_converters.h" using namespace srsran; using namespace srsran::srs_cu_cp; @@ -32,26 +33,28 @@ using namespace asn1::rrc_nr; // sub-procedures and update the succeeded/failed fields. /// \brief Handle first Bearer Context Modification response and prepare subsequent UE context modification request. -bool handle_procedure_response(cu_cp_pdu_session_resource_modify_response& response_msg, - f1ap_ue_context_modification_request& ue_context_mod_request, - const cu_cp_pdu_session_resource_modify_request modify_request, - const e1ap_bearer_context_modification_response& bearer_context_modification_response, - up_config_update& next_config, - srslog::basic_logger& logger); +bool handle_bearer_context_modification_response( + cu_cp_pdu_session_resource_modify_response& response_msg, + f1ap_ue_context_modification_request& ue_context_mod_request, + const cu_cp_pdu_session_resource_modify_request& modify_request, + const e1ap_bearer_context_modification_response& bearer_context_modification_response, + up_config_update& next_config, + srslog::basic_logger& logger); /// \brief Handle UE context modification response and prepare second Bearer Context Modification. -bool handle_procedure_response(cu_cp_pdu_session_resource_modify_response& response_msg, - e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, - const cu_cp_pdu_session_resource_modify_request modify_request, - const f1ap_ue_context_modification_response& ue_context_modification_response, - const up_config_update& next_config, - const srslog::basic_logger& logger); - -/// \brief Handle RRC reconfiguration result -bool handle_procedure_response(cu_cp_pdu_session_resource_modify_response& response_msg, - const cu_cp_pdu_session_resource_modify_request& modify_request, - bool rrc_reconfig_result, - const srslog::basic_logger& logger); +bool handle_ue_context_modification_response( + cu_cp_pdu_session_resource_modify_response& response_msg, + e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, + const cu_cp_pdu_session_resource_modify_request& modify_request, + const f1ap_ue_context_modification_response& ue_context_modification_response, + const up_config_update& next_config, + const srslog::basic_logger& logger); + +/// \brief Handle RRC reconfiguration result. +bool handle_rrc_reconfiguration_response(cu_cp_pdu_session_resource_modify_response& response_msg, + const cu_cp_pdu_session_resource_modify_request& modify_request, + bool rrc_reconfig_result, + const srslog::basic_logger& logger); pdu_session_resource_modification_routine::pdu_session_resource_modification_routine( const cu_cp_pdu_session_resource_modify_request& modify_request_, @@ -92,72 +95,72 @@ void pdu_session_resource_modification_routine::operator()( } { - // prepare first BearerContextModificationRequest + // Prepare first BearerContextModificationRequest. bearer_context_modification_request.ng_ran_bearer_context_mod_request.emplace(); // initialize fresh message fill_initial_e1ap_bearer_context_modification_request(bearer_context_modification_request); - // call E1AP procedure and wait for BearerContextModificationResponse + // Call E1AP procedure and wait for BearerContextModificationResponse. CORO_AWAIT_VALUE( bearer_context_modification_response, e1ap_bearer_ctxt_mng.handle_bearer_context_modification_request(bearer_context_modification_request)); - // Handle BearerContextModificationResponse and fill subsequent UE context modification - if (handle_procedure_response(response_msg, - ue_context_mod_request, - modify_request, - bearer_context_modification_response, - next_config, - logger) == false) { + // Handle BearerContextModificationResponse and fill subsequent UEContextModificationRequest. + if (!handle_bearer_context_modification_response(response_msg, + ue_context_mod_request, + modify_request, + bearer_context_modification_response, + next_config, + logger)) { logger.warning("ue={}: \"{}\" failed to modify bearer at CU-UP", modify_request.ue_index, name()); CORO_EARLY_RETURN(generate_pdu_session_resource_modify_response(false)); } } { - // prepare UE Context Modification Request and call F1 notifier + // Prepare UEContextModificationRequest and call F1 notifier. ue_context_mod_request.ue_index = modify_request.ue_index; CORO_AWAIT_VALUE(ue_context_modification_response, f1ap_ue_ctxt_mng.handle_ue_context_modification_request(ue_context_mod_request)); - // Handle UE Context Modification Response - if (handle_procedure_response(response_msg, - bearer_context_modification_request, - modify_request, - ue_context_modification_response, - next_config, - logger) == false) { + // Handle UEContextModificationResponse. + if (!handle_ue_context_modification_response(response_msg, + bearer_context_modification_request, + modify_request, + ue_context_modification_response, + next_config, + logger)) { logger.warning("ue={}: \"{}\" failed to modify UE context at DU", modify_request.ue_index, name()); CORO_EARLY_RETURN(generate_pdu_session_resource_modify_response(false)); } } - // If needed, inform CU-UP about the new TEID for UL F1u traffic + // If needed, inform CU-UP about the new TEID for UL F1u traffic. if (bearer_context_modification_request.ng_ran_bearer_context_mod_request.has_value()) { - // add remaining fields to BearerContextModificationRequest + // Add remaining fields to BearerContextModificationRequest. bearer_context_modification_request.ue_index = modify_request.ue_index; - // call E1AP procedure and wait for BearerContextModificationResponse + // Call E1AP procedure and wait for BearerContextModificationResponse. CORO_AWAIT_VALUE( bearer_context_modification_response, e1ap_bearer_ctxt_mng.handle_bearer_context_modification_request(bearer_context_modification_request)); - // Handle BearerContextModificationResponse - if (handle_procedure_response(response_msg, - ue_context_mod_request, - modify_request, - bearer_context_modification_response, - next_config, - logger) == false) { + // Handle BearerContextModificationResponse. + if (!handle_bearer_context_modification_response(response_msg, + ue_context_mod_request, + modify_request, + bearer_context_modification_response, + next_config, + logger)) { logger.warning("ue={}: \"{}\" failed to modify bearer at CU-UP", modify_request.ue_index, name()); CORO_EARLY_RETURN(generate_pdu_session_resource_modify_response(false)); } } { - // prepare RRC Reconfiguration and call RRC UE notifier + // Prepare RRCReconfiguration and call RRC UE notifier. { - // get NAS PDUs as received by AMF + // Get NAS PDUs as received by AMF. std::vector nas_pdus; for (const auto& pdu_session : modify_request.pdu_session_res_modify_items) { if (!pdu_session.nas_pdu.empty()) { @@ -174,7 +177,7 @@ void pdu_session_resource_modification_routine::operator()( rrc_ue->generate_meas_config(), false, false, - false, + std::nullopt, {}, logger)) { logger.warning("ue={}: \"{}\" Failed to fill RrcReconfiguration", modify_request.ue_index, name()); @@ -184,17 +187,17 @@ void pdu_session_resource_modification_routine::operator()( CORO_AWAIT_VALUE(rrc_reconfig_result, rrc_ue->handle_rrc_reconfiguration_request(rrc_reconfig_args)); - // Handle RRC Reconfiguration result. - if (handle_procedure_response(response_msg, modify_request, rrc_reconfig_result, logger) == false) { + // Handle RRCReconfiguration result. + if (!handle_rrc_reconfiguration_response(response_msg, modify_request, rrc_reconfig_result, logger)) { logger.warning("ue={}: \"{}\" RRC reconfiguration failed", modify_request.ue_index, name()); - // Notify NGAP to request UE context release from AMF + // Notify NGAP to request UE context release from AMF. ue_task_sched.schedule_async_task(cu_cp_notifier.handle_ue_context_release( {modify_request.ue_index, {}, ngap_cause_radio_network_t::release_due_to_ngran_generated_reason})); CORO_EARLY_RETURN(generate_pdu_session_resource_modify_response(false)); } } - // we are done + // We are done. CORO_RETURN(generate_pdu_session_resource_modify_response(true)); } @@ -254,47 +257,214 @@ void pdu_session_resource_modification_routine::fill_initial_e1ap_bearer_context } } -// \brief Handle first Bearer Context Modification response and prepare subsequent UE context modification request. -bool handle_procedure_response(cu_cp_pdu_session_resource_modify_response& response_msg, - f1ap_ue_context_modification_request& ue_context_mod_request, - const cu_cp_pdu_session_resource_modify_request modify_request, - const e1ap_bearer_context_modification_response& bearer_context_modification_response, - up_config_update& next_config, - srslog::basic_logger& logger) +/// \brief Processes the result of a Bearer Context Modification Result's PDU session modify list. +/// \param[out] ngap_response_list Reference to the final NGAP response. +/// \param[out] ue_context_mod_request Reference to the next request message - a UE context modification. +/// \param[in] ngap_modify_list Const reference to the original NGAP request. +/// \param[in] bearer_context_modification_response Const reference to the response of the previous subprocedure. +/// \param[in] next_config Const reference to the calculated config update. +/// \param[in] logger Reference to the logger. +/// \return True on success, false otherwise. +static bool update_modify_list_with_bearer_ctxt_mod_response( + slotted_id_vector& ngap_response_list, + f1ap_ue_context_modification_request& ue_context_mod_request, + const slotted_id_vector& ngap_modify_list, + const e1ap_bearer_context_modification_response& bearer_context_modification_response, + up_config_update& next_config, + const srslog::basic_logger& logger) +{ + for (const auto& e1ap_item : bearer_context_modification_response.pdu_session_resource_modified_list) { + const auto& psi = e1ap_item.pdu_session_id; + // Sanity check - make sure this session ID is present in the original modify message. + if (!ngap_modify_list.contains(psi)) { + logger.warning("PduSessionResourceSetupRequest doesn't include setup for {}", psi); + return false; + } + // Also check if PDU session is included in expected next configuration. + if (next_config.pdu_sessions_to_modify_list.find(psi) == next_config.pdu_sessions_to_modify_list.end()) { + logger.warning("Didn't expect modification for {}", psi); + return false; + } + + if (ngap_response_list.contains(psi)) { + // Load existing response item from previous call. + logger.debug("Amend to existing NGAP response item for {}", psi); + } else { + // Add empty new item. + cu_cp_pdu_session_resource_modify_response_item new_item; + new_item.pdu_session_id = psi; + ngap_response_list.emplace(new_item.pdu_session_id, new_item); + logger.debug("Insert new NGAP response item for {}", psi); + } + + // Start/continue filling response item. + cu_cp_pdu_session_resource_modify_response_item& ngap_item = ngap_response_list[psi]; + for (const auto& e1ap_drb_item : e1ap_item.drb_setup_list_ng_ran) { + const auto& drb_id = e1ap_drb_item.drb_id; + if (next_config.pdu_sessions_to_modify_list.at(psi).drb_to_add.find(drb_id) == + next_config.pdu_sessions_to_modify_list.at(psi).drb_to_add.end()) { + logger.warning("{} not part of next configuration", drb_id); + return false; + } + + const auto& request_transfer = ngap_modify_list[psi].transfer; + + // Prepare DRB creation at DU. + f1ap_drb_to_setup drb_setup_mod_item; + if (!fill_f1ap_drb_setup_mod_item(drb_setup_mod_item, + nullptr, + psi, + drb_id, + next_config.pdu_sessions_to_modify_list.at(psi).drb_to_add.at(drb_id), + e1ap_drb_item, + request_transfer.qos_flow_add_or_modify_request_list, + logger)) { + logger.warning("Couldn't populate DRB setup/mod item {}", e1ap_drb_item.drb_id); + return false; + } + + // Note: this extra handling for the Modification could be optimized. + for (const auto& e1ap_flow : e1ap_drb_item.flow_setup_list) { + // Fill added flows in NGAP response transfer. + if (!ngap_item.transfer.qos_flow_add_or_modify_response_list.has_value()) { + // Add list if it's not present yet. + ngap_item.transfer.qos_flow_add_or_modify_response_list.emplace(); + } + + qos_flow_add_or_mod_response_item qos_flow; + qos_flow.qos_flow_id = e1ap_flow.qos_flow_id; + ngap_item.transfer.qos_flow_add_or_modify_response_list.value().emplace(qos_flow.qos_flow_id, qos_flow); + } + + // Finally add DRB to setup to UE context modification. + ue_context_mod_request.drbs_to_be_setup_mod_list.push_back(drb_setup_mod_item); + } + + // Add DRB to be removed to UE context modification. + for (const auto& drb_id : next_config.pdu_sessions_to_modify_list.at(psi).drb_to_remove) { + ue_context_mod_request.drbs_to_be_released_list.push_back(drb_id); + } + + // Fail on any DRB that fails to be setup. + if (!e1ap_item.drb_failed_list_ng_ran.empty()) { + logger.warning("Non-empty DRB failed list not supported"); + return false; + } + } + + return true; +} + +/// \brief Processes the response of a Bearer Context Modification Request. +/// \param[out] response_msg Reference to the final NGAP response. +/// \param[out] next_config Const reference to the calculated config update. +/// \param[in] pdu_session_resource_failed_list Const reference to the failed PDU sessions of the Bearer Context +/// Modification Response. +static void update_failed_list_with_bearer_ctxt_mod_response( + cu_cp_pdu_session_resource_modify_response& response_msg, + up_config_update& next_config, + const slotted_id_vector& pdu_session_resource_failed_list) { - // Traverse modify list - if (update_modify_list(response_msg.pdu_session_res_modify_list, - ue_context_mod_request, - modify_request.pdu_session_res_modify_items, - bearer_context_modification_response.pdu_session_resource_modified_list, - next_config, - logger) == false) { + for (const auto& e1ap_item : pdu_session_resource_failed_list) { + // Remove from next config. + next_config.pdu_sessions_to_setup_list.erase(e1ap_item.pdu_session_id); + response_msg.pdu_session_res_modify_list.erase(e1ap_item.pdu_session_id); + + // Add to list taking cause received from CU-UP. + cu_cp_pdu_session_res_setup_failed_item failed_item; + failed_item.pdu_session_id = e1ap_item.pdu_session_id; + failed_item.unsuccessful_transfer.cause = e1ap_to_ngap_cause(e1ap_item.cause); + response_msg.pdu_session_res_failed_to_modify_list.emplace(failed_item.pdu_session_id, failed_item); + } +} + +// \brief Handle first BearerContextModificationResponse and prepare subsequent UEContextModificationRequest. +bool handle_bearer_context_modification_response( + cu_cp_pdu_session_resource_modify_response& response_msg, + f1ap_ue_context_modification_request& ue_context_mod_request, + const cu_cp_pdu_session_resource_modify_request& modify_request, + const e1ap_bearer_context_modification_response& bearer_context_modification_response, + up_config_update& next_config, + srslog::basic_logger& logger) +{ + // Traverse modify list. + if (!update_modify_list_with_bearer_ctxt_mod_response(response_msg.pdu_session_res_modify_list, + ue_context_mod_request, + modify_request.pdu_session_res_modify_items, + bearer_context_modification_response, + next_config, + logger)) { return false; } - // Traverse failed list - update_failed_list(response_msg.pdu_session_res_failed_to_modify_list, - bearer_context_modification_response.pdu_session_resource_failed_list, - next_config); + // Traverse failed list. + update_failed_list_with_bearer_ctxt_mod_response( + response_msg, next_config, bearer_context_modification_response.pdu_session_resource_failed_list); return bearer_context_modification_response.success; } -// Handle UE context modification response and prepare second Bearer Context Modification. -bool handle_procedure_response(cu_cp_pdu_session_resource_modify_response& response_msg, - e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, - const cu_cp_pdu_session_resource_modify_request modify_request, - const f1ap_ue_context_modification_response& ue_context_modification_response, - const up_config_update& next_config, - const srslog::basic_logger& logger) +/// \brief Processes the response of a UE Context Modification Request. +/// \param[out] ngap_response_list Reference to the final NGAP response. +/// \param[out] ue_context_mod_request Reference to the next request message - a Bearer context modification request. +/// \param[in] ngap_modify_list Const reference to the original NGAP request. +/// \param[in] ue_context_modification_response Const reference to the response of the UE context modification request. +/// \param[in] next_config Const reference to the calculated config update. +/// \param[in] logger Reference to the logger. +/// \return True on success, false otherwise. +static bool update_modify_list_with_ue_ctxt_mod_response( + slotted_id_vector& ngap_response_list, + e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, + const slotted_id_vector& ngap_modify_list, + const f1ap_ue_context_modification_response& ue_context_modification_response, + const up_config_update& next_config, + const srslog::basic_logger& logger) +{ + // Fail procedure if (single) DRB couldn't be setup. + if (!ue_context_modification_response.drbs_failed_to_be_setup_list.empty()) { + logger.warning("Couldn't setup {} DRBs at DU", + ue_context_modification_response.drbs_failed_to_be_setup_list.size()); + return false; + } + + // Only prepare bearer context modification request if needed. + if (ue_context_modification_response.drbs_setup_list.empty() and + ue_context_modification_response.drbs_modified_list.empty()) { + // No DRB added or updated. + logger.debug("Skipping preparation of bearer context modification request"); + bearer_ctxt_mod_request.ng_ran_bearer_context_mod_request.reset(); + return ue_context_modification_response.success; + } + + // Start with empty message. + e1ap_ng_ran_bearer_context_mod_request& e1ap_bearer_context_mod = + bearer_ctxt_mod_request.ng_ran_bearer_context_mod_request.emplace(); + + fill_e1ap_bearer_context_list(e1ap_bearer_context_mod.pdu_session_res_to_modify_list, + ue_context_modification_response.drbs_setup_list, + next_config.pdu_sessions_to_modify_list); + + // TODO: traverse other fields + + return ue_context_modification_response.success; +} + +// Handle UEContextModificationResponse and prepare second BearerContextModificationRequest. +bool handle_ue_context_modification_response( + cu_cp_pdu_session_resource_modify_response& response_msg, + e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, + const cu_cp_pdu_session_resource_modify_request& modify_request, + const f1ap_ue_context_modification_response& ue_context_modification_response, + const up_config_update& next_config, + const srslog::basic_logger& logger) { - // Traverse modify list - if (update_modify_list(response_msg.pdu_session_res_modify_list, - bearer_ctxt_mod_request, - modify_request.pdu_session_res_modify_items, - ue_context_modification_response, - next_config, - logger) == false) { + // Traverse modify list. + if (!update_modify_list_with_ue_ctxt_mod_response(response_msg.pdu_session_res_modify_list, + bearer_ctxt_mod_request, + modify_request.pdu_session_res_modify_items, + ue_context_modification_response, + next_config, + logger)) { return false; } @@ -313,14 +483,14 @@ void fill_modify_failed_list(cu_cp_pdu_session_resource_modify_response& re } } -// Handle RRC reconfiguration result -bool handle_procedure_response(cu_cp_pdu_session_resource_modify_response& response_msg, - const cu_cp_pdu_session_resource_modify_request& modify_request, - bool rrc_reconfig_result, - const srslog::basic_logger& logger) +// Handle RRCReconfiguration result. +bool handle_rrc_reconfiguration_response(cu_cp_pdu_session_resource_modify_response& response_msg, + const cu_cp_pdu_session_resource_modify_request& modify_request, + bool rrc_reconfig_result, + const srslog::basic_logger& logger) { // Let all PDU sessions fail if response is negative. - if (rrc_reconfig_result == false) { + if (!rrc_reconfig_result) { fill_modify_failed_list(response_msg, modify_request); } diff --git a/lib/cu_cp/routines/pdu_session_resource_release_routine.cpp b/lib/cu_cp/routines/pdu_session_resource_release_routine.cpp index c4dc620ae9..2a6fa5e3c3 100644 --- a/lib/cu_cp/routines/pdu_session_resource_release_routine.cpp +++ b/lib/cu_cp/routines/pdu_session_resource_release_routine.cpp @@ -48,10 +48,10 @@ pdu_session_resource_release_routine::pdu_session_resource_release_routine( } // Handle RRC reconfiguration result. -bool handle_procedure_response(cu_cp_pdu_session_resource_release_response& response_msg, - const cu_cp_pdu_session_resource_release_command& release_cmd, - bool rrc_reconfig_result, - const srslog::basic_logger& logger) +bool handle_rrc_reconfiguration_response(cu_cp_pdu_session_resource_release_response& response_msg, + const cu_cp_pdu_session_resource_release_command& release_cmd, + bool rrc_reconfig_result, + const srslog::basic_logger& logger) { // Let all PDU sessions fail if response is negative. if (!rrc_reconfig_result) { @@ -79,19 +79,19 @@ void pdu_session_resource_release_routine::operator()( next_config = up_resource_mng.calculate_update(release_cmd); } - // Inform the CU-UP about the release of the bearer context + // Inform the CU-UP about the release of the bearer context. if (next_config.pdu_sessions_to_remove_list.size() == up_resource_mng.get_nof_pdu_sessions()) { bearer_context_release_command.ue_index = release_cmd.ue_index; bearer_context_release_command.cause = e1ap_cause_radio_network_t::unspecified; /// NOTE: Only the Bearer Context at the CU-UP will be released. We don't want to release the UE. - // Request BearerContextRelease + // Request BearerContextRelease. CORO_AWAIT(e1ap_bearer_ctxt_mng.handle_bearer_context_release_command(bearer_context_release_command)); - } else { // Inform CU-UP about the release of a bearer + } else { // Inform CU-UP about the release of a bearer. - // prepare BearerContextModificationRequest and call e1 notifier + // Prepare BearerContextModificationRequest and call E1 notifier. bearer_context_modification_request.ue_index = release_cmd.ue_index; for (const auto& pdu_session_res_to_release : next_config.pdu_sessions_to_remove_list) { @@ -100,20 +100,20 @@ void pdu_session_resource_release_routine::operator()( bearer_context_modification_request.ng_ran_bearer_context_mod_request = bearer_context_mod_request; } - // call E1AP procedure and wait for BearerContextModificationResponse + // Call E1AP procedure and wait for BearerContextModificationResponse. CORO_AWAIT_VALUE( bearer_context_modification_response, e1ap_bearer_ctxt_mng.handle_bearer_context_modification_request(bearer_context_modification_request)); - // Handle BearerContextModificationResponse + // Handle BearerContextModificationResponse. if (not bearer_context_modification_response.success) { logger.warning("ue={}: \"{}\" failed to release bearer(s) at CU-UP", release_cmd.ue_index, name()); } } - // Release DRB resources at DU + // Release DRB resources at DU. { - // prepare UeContextModificationRequest and call F1 notifier + // Prepare UeContextModificationRequest and call F1 notifier. ue_context_mod_request.ue_index = release_cmd.ue_index; for (const auto& drb_id : next_config.drb_to_remove_list) { ue_context_mod_request.drbs_to_be_released_list.push_back(drb_id); @@ -122,16 +122,16 @@ void pdu_session_resource_release_routine::operator()( CORO_AWAIT_VALUE(ue_context_modification_response, f1ap_ue_ctxt_mng.handle_ue_context_modification_request(ue_context_mod_request)); - // Handle UE Context Modification Response + // Handle UEContextModificationResponse. if (not ue_context_modification_response.success) { logger.warning("ue={}: \"{}\" failed to release bearer(s) at DU", release_cmd.ue_index, name()); } } { - // prepare RRC Reconfiguration and call RRC UE notifier + // Prepare RRCReconfiguration and call RRC UE notifier. { - // get NAS PDUs as received by AMF + // Get NAS PDUs as received by AMF. std::vector nas_pdus; if (!release_cmd.nas_pdu.empty()) { nas_pdus.push_back(release_cmd.nas_pdu); @@ -146,7 +146,7 @@ void pdu_session_resource_release_routine::operator()( rrc_ue->generate_meas_config(), false, false, - false, + std::nullopt, {}, logger)) { logger.warning("ue={}: \"{}\" Failed to fill RrcReconfiguration", release_cmd.ue_index, name()); @@ -156,8 +156,8 @@ void pdu_session_resource_release_routine::operator()( CORO_AWAIT_VALUE(rrc_reconfig_result, rrc_ue->handle_rrc_reconfiguration_request(rrc_reconfig_args)); - // Handle RRC Reconfiguration result. - if (not handle_procedure_response(response_msg, release_cmd, rrc_reconfig_result, logger)) { + // Handle RRCReconfiguration result. + if (not handle_rrc_reconfiguration_response(response_msg, release_cmd, rrc_reconfig_result, logger)) { logger.warning("ue={}: \"{}\" RRC reconfiguration failed", release_cmd.ue_index, name()); CORO_EARLY_RETURN(handle_pdu_session_resource_release_response(false)); } diff --git a/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp b/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp index 7bb612dcb8..c84ac9961c 100644 --- a/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp +++ b/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp @@ -22,6 +22,7 @@ #include "pdu_session_resource_setup_routine.h" #include "pdu_session_routine_helpers.h" +#include "srsran/ran/cause/e1ap_cause_converters.h" #include "srsran/ran/cause/ngap_cause.h" using namespace srsran; @@ -30,39 +31,41 @@ using namespace asn1::rrc_nr; // Free function to amend to the final procedure response message. This will take the results from the various // sub-procedures and update the succeeded/failed fields. -bool handle_procedure_response(cu_cp_pdu_session_resource_setup_response& response_msg, - f1ap_ue_context_modification_request& ue_context_mod_request, - const cu_cp_pdu_session_resource_setup_request setup_msg, - const e1ap_bearer_context_modification_response& bearer_context_modification_response, - up_config_update& next_config, - up_resource_manager& up_resource_mng_, - const security_indication_t& default_security_indication, - srslog::basic_logger& logger); - -// Same as above but taking the result from E1AP Bearer Context Setup message -bool handle_procedure_response(cu_cp_pdu_session_resource_setup_response& response_msg, - f1ap_ue_context_modification_request& ue_context_mod_request, - const cu_cp_pdu_session_resource_setup_request& setup_msg, - const e1ap_bearer_context_setup_response& bearer_context_setup_response, - up_config_update& next_config, - up_resource_manager& up_resource_mng_, - const security_indication_t& default_security_indication, - srslog::basic_logger& logger); +bool handle_bearer_context_modification_response( + cu_cp_pdu_session_resource_setup_response& response_msg, + f1ap_ue_context_modification_request& ue_context_mod_request, + up_config_update& next_config, + const cu_cp_pdu_session_resource_setup_request& setup_msg, + const e1ap_bearer_context_modification_response& bearer_context_modification_response, + up_resource_manager& up_resource_mng, + const security_indication_t& default_security_indication, + srslog::basic_logger& logger); + +// Same as above but taking the result from E1AP Bearer Context Setup message. +bool handle_bearer_context_setup_response(cu_cp_pdu_session_resource_setup_response& response_msg, + f1ap_ue_context_modification_request& ue_context_mod_request, + const cu_cp_pdu_session_resource_setup_request& setup_msg, + const e1ap_bearer_context_setup_response& bearer_context_setup_response, + up_config_update& next_config, + up_resource_manager& up_resource_mng_, + const security_indication_t& default_security_indication, + srslog::basic_logger& logger); // This method takes the F1AP UE Context Modification Response message and pre-fills the subsequent // bearer context modification message to be send to the CU-UP. // In case of a negative outcome it also prefills the final PDU session resource setup respone message. -bool handle_procedure_response(cu_cp_pdu_session_resource_setup_response& response_msg, - e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, - const cu_cp_pdu_session_resource_setup_request& setup_msg, - const f1ap_ue_context_modification_response& ue_context_modification_response, - const up_config_update& next_config, - const srslog::basic_logger& logger); - -bool handle_procedure_response(cu_cp_pdu_session_resource_setup_response& response_msg, - const cu_cp_pdu_session_resource_setup_request& setup_msg, - bool rrc_reconfig_result, - const srslog::basic_logger& logger); +bool handle_ue_context_modification_response( + cu_cp_pdu_session_resource_setup_response& response_msg, + e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, + const cu_cp_pdu_session_resource_setup_request& setup_msg, + const f1ap_ue_context_modification_response& ue_context_modification_response, + const up_config_update& next_config, + const srslog::basic_logger& logger); + +bool handle_rrc_reconfiguration_response(cu_cp_pdu_session_resource_setup_response& response_msg, + const cu_cp_pdu_session_resource_setup_request& setup_msg, + bool rrc_reconfig_result, + const srslog::basic_logger& logger); pdu_session_resource_setup_routine::pdu_session_resource_setup_routine( const cu_cp_pdu_session_resource_setup_request& setup_msg_, @@ -108,57 +111,57 @@ void pdu_session_resource_setup_routine::operator()( next_config = up_resource_mng.calculate_update(setup_msg.pdu_session_res_setup_items); } - // sanity check passed, decide whether we have to create a Bearer Context at the CU-UP or modify an existing one. + // Sanity check passed, decide whether we have to create a Bearer Context at the CU-UP or modify an existing one. if (next_config.initial_context_creation) { - // prepare BearerContextSetupRequest + // Prepare BearerContextSetupRequest. if (!fill_e1ap_bearer_context_setup_request(bearer_context_setup_request)) { logger.warning("ue={}: \"{}\" failed to fill bearer context at CU-UP", setup_msg.ue_index, name()); CORO_EARLY_RETURN(handle_pdu_session_resource_setup_result(false)); } - // call E1AP procedure + // Call E1AP procedure. CORO_AWAIT_VALUE(bearer_context_setup_response, e1ap_bearer_ctxt_mng.handle_bearer_context_setup_request(bearer_context_setup_request)); - // Handle BearerContextSetupResponse - if (!handle_procedure_response(response_msg, - ue_context_mod_request, - setup_msg, - bearer_context_setup_response, - next_config, - up_resource_mng, - default_security_indication, - logger)) { + // Handle BearerContextSetupResponse. + if (!handle_bearer_context_setup_response(response_msg, + ue_context_mod_request, + setup_msg, + bearer_context_setup_response, + next_config, + up_resource_mng, + default_security_indication, + logger)) { logger.warning("ue={}: \"{}\" failed to setup bearer at CU-UP", setup_msg.ue_index, name()); CORO_EARLY_RETURN(handle_pdu_session_resource_setup_result(false)); } } else { - // prepare BearerContextModificationRequest and modify existing bearer + // Prepare BearerContextModificationRequest and modify existing bearer. bearer_context_modification_request.ng_ran_bearer_context_mod_request.emplace(); // initialize fresh message fill_initial_e1ap_bearer_context_modification_request(bearer_context_modification_request); - // call E1AP procedure and wait for BearerContextModificationResponse + // Call E1AP procedure and wait for BearerContextModificationResponse. CORO_AWAIT_VALUE( bearer_context_modification_response, e1ap_bearer_ctxt_mng.handle_bearer_context_modification_request(bearer_context_modification_request)); - // Handle BearerContextModificationResponse - if (!handle_procedure_response(response_msg, - ue_context_mod_request, - setup_msg, - bearer_context_modification_response, - next_config, - up_resource_mng, - default_security_indication, - logger)) { + // Handle BearerContextModificationResponse. + if (!handle_bearer_context_modification_response(response_msg, + ue_context_mod_request, + next_config, + setup_msg, + bearer_context_modification_response, + up_resource_mng, + default_security_indication, + logger)) { logger.warning("ue={}: \"{}\" failed to modify bearer at CU-UP", setup_msg.ue_index, name()); CORO_EARLY_RETURN(handle_pdu_session_resource_setup_result(false)); } } - // Register required SRB and DRB resources at DU + // Register required SRB and DRB resources at DU. { - // prepare UE Context Modification Request and call F1 + // Prepare UE Context Modification Request and call F1. ue_context_mod_request.ue_index = setup_msg.ue_index; ue_context_mod_request.cu_to_du_rrc_info.emplace(); ue_context_mod_request.cu_to_du_rrc_info.value().ue_cap_rat_container_list = @@ -168,47 +171,47 @@ void pdu_session_resource_setup_routine::operator()( CORO_AWAIT_VALUE(ue_context_modification_response, f1ap_ue_ctxt_mng.handle_ue_context_modification_request(ue_context_mod_request)); - // Handle UE Context Modification Response - if (!handle_procedure_response(response_msg, - bearer_context_modification_request, - setup_msg, - ue_context_modification_response, - next_config, - logger)) { + // Handle UE Context Modification Response. + if (!handle_ue_context_modification_response(response_msg, + bearer_context_modification_request, + setup_msg, + ue_context_modification_response, + next_config, + logger)) { logger.warning("ue={}: \"{}\" failed to modify UE context at DU", setup_msg.ue_index, name()); CORO_EARLY_RETURN(handle_pdu_session_resource_setup_result(false)); } } - // Inform CU-UP about the new TEID for UL F1u traffic + // Inform CU-UP about the new TEID for UL F1u traffic. { - // add remaining fields to BearerContextModificationRequest + // Add remaining fields to BearerContextModificationRequest. bearer_context_modification_request.ue_index = setup_msg.ue_index; - // call E1AP procedure and wait for BearerContextModificationResponse + // Call E1AP procedure and wait for BearerContextModificationResponse. CORO_AWAIT_VALUE( bearer_context_modification_response, e1ap_bearer_ctxt_mng.handle_bearer_context_modification_request(bearer_context_modification_request)); - // Handle BearerContextModificationResponse - if (!handle_procedure_response(response_msg, - ue_context_mod_request, - setup_msg, - bearer_context_modification_response, - next_config, - up_resource_mng, - default_security_indication, - logger)) { + // Handle BearerContextModificationResponse. + if (!handle_bearer_context_modification_response(response_msg, + ue_context_mod_request, + next_config, + setup_msg, + bearer_context_modification_response, + up_resource_mng, + default_security_indication, + logger)) { logger.warning("ue={}: \"{}\" failed to modify bearer at CU-UP", setup_msg.ue_index, name()); CORO_EARLY_RETURN(handle_pdu_session_resource_setup_result(false)); } } { - // prepare RRC Reconfiguration and call RRC UE notifier - // if default DRB is being setup, SRB2 needs to be setup as well + // Prepare RRC Reconfiguration and call RRC UE notifier. + // If default DRB is being setup, SRB2 needs to be setup as well. { - // get NAS PDUs as received by AMF + // Get NAS PDUs as received by AMF. std::vector nas_pdus; if (!setup_msg.nas_pdu.empty()) { nas_pdus.push_back(setup_msg.nas_pdu); @@ -230,7 +233,7 @@ void pdu_session_resource_setup_routine::operator()( : std::optional{}, false, false, - false, + std::nullopt, {}, logger)) { logger.warning("ue={}: \"{}\" Failed to fill RrcReconfiguration", setup_msg.ue_index, name()); @@ -240,89 +243,341 @@ void pdu_session_resource_setup_routine::operator()( CORO_AWAIT_VALUE(rrc_reconfig_result, rrc_ue->handle_rrc_reconfiguration_request(rrc_reconfig_args)); - // Handle RRC Reconfiguration Response - if (!handle_procedure_response(response_msg, setup_msg, rrc_reconfig_result, logger)) { + // Handle RRCReconfiguration result. + if (!handle_rrc_reconfiguration_response(response_msg, setup_msg, rrc_reconfig_result, logger)) { logger.warning("ue={}: \"{}\" RRC reconfiguration failed", setup_msg.ue_index, name()); - // Notify NGAP to request UE context release from AMF + // Notify NGAP to request UE context release from AMF. ue_task_sched.schedule_async_task(cu_cp_notifier.handle_ue_context_release( {setup_msg.ue_index, {}, ngap_cause_radio_network_t::release_due_to_ngran_generated_reason})); CORO_EARLY_RETURN(handle_pdu_session_resource_setup_result(false)); } } - // we are done, all good + // We are done, all good. CORO_RETURN(handle_pdu_session_resource_setup_result(true)); } +/// \brief Processes the response of a Bearer Context Setup/Modification Request. +/// \param[out] ngap_response_list Reference to the final NGAP response. +/// \param[out] srb_setup_mod_list Reference to the successful SRB setup list. +/// \param[out] drb_setup_mod_list Reference to the successful DRB setup list. +/// \param[out] next_config Const reference to the calculated config update. +/// \param[out] ngap_setup_list Const reference to the original NGAP request. +/// \param[in] pdu_session_resource_setup_list Const reference to the PDU sessions of the Bearer Context +/// Setup/Modification Response. +/// \param[in] up_resource_mng Reference to the UP resource manager. +/// \param[in] default_security_indication Const reference to the default security indication. +/// \param[in] logger Reference to the logger. +/// \return True on success, false otherwise. +static bool update_setup_list_with_bearer_ctxt_setup_mod_response( + slotted_id_vector& ngap_response_list, + std::vector& srb_setup_mod_list, + std::vector& drb_setup_mod_list, + up_config_update& next_config, + const slotted_id_vector& ngap_setup_list, + const slotted_id_vector& + pdu_session_resource_setup_list, + up_resource_manager& up_resource_mng, + const security_indication_t& default_security_indication, + const srslog::basic_logger& logger) +{ + // Set up SRB2 if this is the first DRB to be setup. + if (up_resource_mng.get_nof_drbs() == 0) { + f1ap_srb_to_setup srb2; + srb2.srb_id = srb_id_t::srb2; + srb_setup_mod_list.push_back(srb2); + } + + for (const auto& e1ap_item : pdu_session_resource_setup_list) { + const auto& psi = e1ap_item.pdu_session_id; + + // Sanity check - make sure this session ID is present in the original setup message. + if (!ngap_setup_list.contains(psi)) { + logger.warning("PduSessionResourceSetupRequest doesn't include setup for {}", psi); + return false; + } + // Also check if PDU session is included in expected next configuration. + if (next_config.pdu_sessions_to_setup_list.find(psi) == next_config.pdu_sessions_to_setup_list.end()) { + logger.warning("Didn't expect setup for {}", psi); + return false; + } + + cu_cp_pdu_session_res_setup_response_item item; + item.pdu_session_id = psi; + + auto& transfer = item.pdu_session_resource_setup_response_transfer; + transfer.dlqos_flow_per_tnl_info.up_tp_layer_info = e1ap_item.ng_dl_up_tnl_info; + + // Determine security settings for this PDU session and decide whether we have to send the security_result via NGAP. + bool integrity_enabled = false; + bool ciphering_enabled = false; + + if (ngap_setup_list[psi].security_ind.has_value()) { + // TS 38.413 Sec. 8.2.1.2: + // For each PDU session for which the Security Indication IE is included in the PDU Session Resource Setup Request + // Transfer IE of the PDU SESSION RESOURCE SETUP REQUEST message, and the Integrity Protection Indication IE + // or Confidentiality Protection Indication IE is set to "preferred", then the NG-RAN node should, if supported, + // perform user plane integrity protection or ciphering, respectively, for the concerned PDU session and shall + // notify whether it performed the user plane integrity protection or ciphering by including the Integrity + // Protection Result IE or Confidentiality Protection Result IE, respectively, in the PDU Session Resource Setup + // Response Transfer IE of the PDU SESSION RESOURCE SETUP RESPONSE message. + const auto& ngap_sec_ind = ngap_setup_list[psi].security_ind.value(); + if (security_result_required(ngap_sec_ind)) { + // Apply security settings according to the decision in the CU-UP. + if (!e1ap_item.security_result.has_value()) { + logger.warning("Missing security result in E1AP response for {}", psi); + return false; + } + const auto& sec_res = e1ap_item.security_result.value(); + integrity_enabled = sec_res.integrity_protection_result == integrity_protection_result_t::performed; + ciphering_enabled = sec_res.confidentiality_protection_result == confidentiality_protection_result_t::performed; + // Add result to NGAP response + transfer.security_result = sec_res; + } else { + // Apply security settings that were requested via NGAP and do not require an explicit reponse. + integrity_enabled = ngap_sec_ind.integrity_protection_ind == integrity_protection_indication_t::required; + ciphering_enabled = + ngap_sec_ind.confidentiality_protection_ind == confidentiality_protection_indication_t::required; + } + } else { + // Security settings were not signaled via NGAP, we have used the defaults of CU-CP. + const auto sec_ind = default_security_indication; + if (security_result_required(sec_ind)) { + // Apply security settings according to the decision in the CU-UP. + if (!e1ap_item.security_result.has_value()) { + logger.warning("Missing security result in E1AP response for {}", psi); + return false; + } + const auto& sec_res = e1ap_item.security_result.value(); + integrity_enabled = sec_res.integrity_protection_result == integrity_protection_result_t::performed; + ciphering_enabled = sec_res.confidentiality_protection_result == confidentiality_protection_result_t::performed; + // No result in NGAP response needed here. + } else { + // Apply default security settings that do not require an explicit response. + integrity_enabled = sec_ind.integrity_protection_ind == integrity_protection_indication_t::required; + ciphering_enabled = sec_ind.confidentiality_protection_ind == confidentiality_protection_indication_t::required; + } + } + + auto& next_cfg_pdu_session = next_config.pdu_sessions_to_setup_list.at(psi); + + for (const auto& e1ap_drb_item : e1ap_item.drb_setup_list_ng_ran) { + const auto& drb_id = e1ap_drb_item.drb_id; + if (next_config.pdu_sessions_to_setup_list.at(psi).drb_to_add.find(drb_id) == + next_config.pdu_sessions_to_setup_list.at(psi).drb_to_add.end()) { + logger.warning("DRB id {} not part of next configuration", drb_id); + return false; + } + + // Update security settings of each DRB. + next_cfg_pdu_session.drb_to_add.find(drb_id)->second.pdcp_cfg.integrity_protection_required = integrity_enabled; + next_cfg_pdu_session.drb_to_add.find(drb_id)->second.pdcp_cfg.ciphering_required = ciphering_enabled; + + // Prepare DRB item for DU. + f1ap_drb_to_setup drb_setup_mod_item; + if (!fill_f1ap_drb_setup_mod_item(drb_setup_mod_item, + &transfer.dlqos_flow_per_tnl_info.associated_qos_flow_list, + item.pdu_session_id, + drb_id, + next_config.pdu_sessions_to_setup_list.at(psi).drb_to_add.at(drb_id), + e1ap_drb_item, + ngap_setup_list[item.pdu_session_id].qos_flow_setup_request_items, + logger)) { + logger.warning("Couldn't populate DRB setup/mod item {}", e1ap_drb_item.drb_id); + return false; + } + drb_setup_mod_list.push_back(drb_setup_mod_item); + } + + // Fail on any DRB that fails to be setup. + if (!e1ap_item.drb_failed_list_ng_ran.empty()) { + logger.warning("Non-empty DRB failed list not supported"); + return false; + } + + ngap_response_list.emplace(item.pdu_session_id, item); + } + + return true; +} + +/// \brief Processes the response of a Bearer Context Setup/Modification Request. +/// \param[out] response_msg Reference to the final NGAP response. +/// \param[out] next_config Const reference to the calculated config update. +/// \param[in] pdu_session_resource_failed_list Const reference to the failed PDU sessions of the Bearer Context +/// Setup/Modification Response. +static void update_failed_list( + + cu_cp_pdu_session_resource_setup_response& response_msg, + up_config_update& next_config, + const slotted_id_vector& pdu_session_resource_failed_list) +{ + for (const auto& e1ap_item : pdu_session_resource_failed_list) { + // Remove from next config. + next_config.pdu_sessions_to_setup_list.erase(e1ap_item.pdu_session_id); + response_msg.pdu_session_res_setup_response_items.erase(e1ap_item.pdu_session_id); + + // Add to list taking cause received from CU-UP. + cu_cp_pdu_session_res_setup_failed_item failed_item; + failed_item.pdu_session_id = e1ap_item.pdu_session_id; + failed_item.unsuccessful_transfer.cause = e1ap_to_ngap_cause(e1ap_item.cause); + response_msg.pdu_session_res_failed_to_setup_items.emplace(failed_item.pdu_session_id, failed_item); + } +} + +/// \brief Processes the response of a UE Context Modification Request. +/// \param[in] drb_setup_mod_list Reference to the DRBs originally requested to be setup. +/// \param[in] ngap_setup_list Const reference to the original NGAP request. +/// \param[in] bearer_contxt_mod_response Const reference to the response of the Bearer Context Modification Response. +/// \param[in] next_config Const reference to the calculated config update. +/// \param[in] logger Reference to the logger. +/// \return True on success, false otherwise. +static bool validate_next_config_with_bearer_ctxt_mod_response( + const std::vector& drb_setup_mod_list, + const slotted_id_vector& ngap_setup_list, + const e1ap_bearer_context_modification_response& bearer_contxt_mod_response, + const up_config_update& next_config, + const srslog::basic_logger& logger) +{ + for (const auto& e1ap_item : bearer_contxt_mod_response.pdu_session_resource_modified_list) { + const auto& psi = e1ap_item.pdu_session_id; + + // Sanity check - make sure this session ID is present in the original setup message. + if (!ngap_setup_list.contains(psi)) { + logger.warning("PduSessionResourceSetupRequest doesn't include setup for {}", psi); + return false; + } + // Also check if PDU session is included in expected next configuration. + if (next_config.pdu_sessions_to_setup_list.find(psi) == next_config.pdu_sessions_to_setup_list.end()) { + logger.warning("Didn't expect setup for {}", psi); + return false; + } + + const auto& next_cfg_pdu_session = next_config.pdu_sessions_to_setup_list.at(psi); + + for (const auto& e1ap_drb_item : e1ap_item.drb_setup_list_ng_ran) { + const auto& drb_id = e1ap_drb_item.drb_id; + if (next_cfg_pdu_session.drb_to_add.find(drb_id) == next_cfg_pdu_session.drb_to_add.end()) { + logger.warning("DRB id {} not part of next configuration", drb_id); + return false; + } + + // Check that all modified DRBs are already part of the next configuration. + for (const auto& drb_mod_item : e1ap_item.drb_modified_list_ng_ran) { + auto it = + std::find_if(drb_setup_mod_list.begin(), + drb_setup_mod_list.end(), + [&](const f1ap_drb_to_setup& setup_item) { return setup_item.drb_id == drb_mod_item.drb_id; }); + if (it == drb_setup_mod_list.end()) { + return false; + } + } + } + + // Fail on any DRB that failed modification. + if (!e1ap_item.drb_failed_to_modify_list_ng_ran.empty()) { + logger.warning("Non-empty DRB failed to modify list not supported"); + return false; + } + } + + return true; +} + +/// \brief Processes the response of a Bearer Context Modification Request. +/// \param[out] response_msg Reference to the final NGAP response. +/// \param[out] next_config Const reference to the calculated config update. +/// \param[in] bearer_context_modification_response Const reference to the response of the Bearer Context Modification +/// Request. +static void update_failed_to_modify_list_with_bearer_ctxt_mod_resp( + cu_cp_pdu_session_resource_setup_response& response_msg, + up_config_update& next_config, + const e1ap_bearer_context_modification_response& bearer_context_modification_response) +{ + for (const auto& e1ap_item : bearer_context_modification_response.pdu_session_resource_failed_to_modify_list) { + // Remove from next config. + next_config.pdu_sessions_to_setup_list.erase(e1ap_item.pdu_session_id); + response_msg.pdu_session_res_setup_response_items.erase(e1ap_item.pdu_session_id); + + // Add to list taking cause received from CU-UP. + cu_cp_pdu_session_res_setup_failed_item failed_item; + failed_item.pdu_session_id = e1ap_item.pdu_session_id; + failed_item.unsuccessful_transfer.cause = e1ap_to_ngap_cause(e1ap_item.cause); + response_msg.pdu_session_res_failed_to_setup_items.emplace(failed_item.pdu_session_id, failed_item); + } +} + // Free function to amend to the final procedure response message. This will take the results from the various // sub-procedures and update the succeeded/failed fields. -bool handle_procedure_response(cu_cp_pdu_session_resource_setup_response& response_msg, - f1ap_ue_context_modification_request& ue_context_mod_request, - const cu_cp_pdu_session_resource_setup_request setup_msg, - const e1ap_bearer_context_modification_response& bearer_context_modification_response, - up_config_update& next_config, - up_resource_manager& up_resource_mng_, - const security_indication_t& default_security_indication, - srslog::basic_logger& logger) +bool handle_bearer_context_modification_response( + cu_cp_pdu_session_resource_setup_response& response_msg, + f1ap_ue_context_modification_request& ue_context_mod_request, + up_config_update& next_config, + const cu_cp_pdu_session_resource_setup_request& setup_msg, + const e1ap_bearer_context_modification_response& bearer_context_modification_response, + up_resource_manager& up_resource_mng, + const security_indication_t& default_security_indication, + srslog::basic_logger& logger) { - // Traverse setup list - if (!update_setup_list(response_msg.pdu_session_res_setup_response_items, - ue_context_mod_request.srbs_to_be_setup_mod_list, - ue_context_mod_request.drbs_to_be_setup_mod_list, - setup_msg.pdu_session_res_setup_items, - bearer_context_modification_response.pdu_session_resource_setup_list, - next_config, - up_resource_mng_, - default_security_indication, - logger)) { + // Traverse setup list. + if (!update_setup_list_with_bearer_ctxt_setup_mod_response( + response_msg.pdu_session_res_setup_response_items, + ue_context_mod_request.srbs_to_be_setup_mod_list, + ue_context_mod_request.drbs_to_be_setup_mod_list, + next_config, + setup_msg.pdu_session_res_setup_items, + bearer_context_modification_response.pdu_session_resource_setup_list, + up_resource_mng, + default_security_indication, + logger)) { return false; } - // Traverse failed list - update_failed_list(response_msg.pdu_session_res_failed_to_setup_items, - bearer_context_modification_response.pdu_session_resource_failed_list, - next_config); + // Traverse failed list. + update_failed_list(response_msg, next_config, bearer_context_modification_response.pdu_session_resource_failed_list); - for (const auto& e1ap_item : bearer_context_modification_response.pdu_session_resource_modified_list) { - // modified list - logger.info("Implement handling of resource modified item with {}", e1ap_item.pdu_session_id); + // Traverse modified list. + if (!validate_next_config_with_bearer_ctxt_mod_response(ue_context_mod_request.drbs_to_be_setup_mod_list, + setup_msg.pdu_session_res_setup_items, + bearer_context_modification_response, + next_config, + logger)) { + return false; } - for (const auto& e1ap_item : bearer_context_modification_response.pdu_session_resource_failed_to_modify_list) { - // failed to modify list - logger.info("Implement handling of resource failed to modify item with {}", e1ap_item.pdu_session_id); - } + // Traverse failed to modify list. + update_failed_to_modify_list_with_bearer_ctxt_mod_resp( + response_msg, next_config, bearer_context_modification_response); return bearer_context_modification_response.success; } -// Same as above but taking the result from E1AP Bearer Context Setup message -bool handle_procedure_response(cu_cp_pdu_session_resource_setup_response& response_msg, - f1ap_ue_context_modification_request& ue_context_mod_request, - const cu_cp_pdu_session_resource_setup_request& setup_msg, - const e1ap_bearer_context_setup_response& bearer_context_setup_response, - up_config_update& next_config, - up_resource_manager& up_resource_mng_, - const security_indication_t& default_security_indication, - srslog::basic_logger& logger) +// Same as above but taking the result from E1AP Bearer Context Setup message. +bool handle_bearer_context_setup_response(cu_cp_pdu_session_resource_setup_response& response_msg, + f1ap_ue_context_modification_request& ue_context_mod_request, + const cu_cp_pdu_session_resource_setup_request& setup_msg, + const e1ap_bearer_context_setup_response& bearer_context_setup_response, + up_config_update& next_config, + up_resource_manager& up_resource_mng_, + const security_indication_t& default_security_indication, + srslog::basic_logger& logger) { - // Traverse setup list - if (!update_setup_list(response_msg.pdu_session_res_setup_response_items, - ue_context_mod_request.srbs_to_be_setup_mod_list, - ue_context_mod_request.drbs_to_be_setup_mod_list, - setup_msg.pdu_session_res_setup_items, - bearer_context_setup_response.pdu_session_resource_setup_list, - next_config, - up_resource_mng_, - default_security_indication, - logger)) { + // Traverse setup list. + if (!update_setup_list_with_bearer_ctxt_setup_mod_response( + response_msg.pdu_session_res_setup_response_items, + ue_context_mod_request.srbs_to_be_setup_mod_list, + ue_context_mod_request.drbs_to_be_setup_mod_list, + next_config, + setup_msg.pdu_session_res_setup_items, + bearer_context_setup_response.pdu_session_resource_setup_list, + up_resource_mng_, + default_security_indication, + logger)) { return false; } - // Traverse failed list - update_failed_list(response_msg.pdu_session_res_failed_to_setup_items, - bearer_context_setup_response.pdu_session_resource_failed_list, - next_config); + // Traverse failed list. + update_failed_list(response_msg, next_config, bearer_context_setup_response.pdu_session_resource_failed_list); return bearer_context_setup_response.success; } @@ -342,21 +597,22 @@ void fill_setup_failed_list(cu_cp_pdu_session_resource_setup_response& resp // This method takes the F1AP UE Context Modification Response message and pre-fills the subsequent // bearer context modification message to be send to the CU-UP. // In case of a negative outcome it also prefills the final PDU session resource setup respone message. -bool handle_procedure_response(cu_cp_pdu_session_resource_setup_response& response_msg, - e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, - const cu_cp_pdu_session_resource_setup_request& setup_msg, - const f1ap_ue_context_modification_response& ue_context_modification_response, - const up_config_update& next_config, - const srslog::basic_logger& logger) +bool handle_ue_context_modification_response( + cu_cp_pdu_session_resource_setup_response& response_msg, + e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, + const cu_cp_pdu_session_resource_setup_request& setup_msg, + const f1ap_ue_context_modification_response& ue_context_modification_response, + const up_config_update& next_config, + const srslog::basic_logger& logger) { - // Fail procedure if (single) DRB couldn't be setup + // Fail procedure if (single) DRB couldn't be setup. if (!ue_context_modification_response.drbs_failed_to_be_setup_list.empty()) { logger.warning("Couldn't setup {} DRBs at DU", ue_context_modification_response.drbs_failed_to_be_setup_list.size()); return false; } - if (!update_setup_list( + if (!update_setup_list_with_ue_ctxt_setup_response( bearer_ctxt_mod_request, ue_context_modification_response.drbs_setup_list, next_config, logger)) { return false; } @@ -371,10 +627,10 @@ bool handle_procedure_response(cu_cp_pdu_session_resource_setup_response& r return ue_context_modification_response.success; } -bool handle_procedure_response(cu_cp_pdu_session_resource_setup_response& response_msg, - const cu_cp_pdu_session_resource_setup_request& setup_msg, - bool rrc_reconfig_result, - const srslog::basic_logger& logger) +bool handle_rrc_reconfiguration_response(cu_cp_pdu_session_resource_setup_response& response_msg, + const cu_cp_pdu_session_resource_setup_request& setup_msg, + bool rrc_reconfig_result, + const srslog::basic_logger& logger) { // Let all PDU sessions fail if response is negative. if (!rrc_reconfig_result) { @@ -397,7 +653,7 @@ void mark_all_sessions_as_failed(cu_cp_pdu_session_resource_setup_response& fail_item.cause = cause; failed_list.emplace(setup_item.pdu_session_id, fail_item); } - update_failed_list(response_msg.pdu_session_res_failed_to_setup_items, failed_list, next_config); + update_failed_list(response_msg, next_config, failed_list); // No PDU session setup can be successful at the same time. response_msg.pdu_session_res_setup_response_items.clear(); } @@ -429,7 +685,7 @@ bool pdu_session_resource_setup_routine::fill_e1ap_bearer_context_setup_request( { e1ap_request.ue_index = setup_msg.ue_index; - // security info + // Security info. e1ap_request.security_info.security_algorithm.ciphering_algo = security_cfg.cipher_algo; e1ap_request.security_info.security_algorithm.integrity_protection_algorithm = security_cfg.integ_algo; auto k_enc_buffer = byte_buffer::create(security_cfg.k_enc); diff --git a/lib/cu_cp/routines/pdu_session_routine_helpers.cpp b/lib/cu_cp/routines/pdu_session_routine_helpers.cpp index 27a3122165..09c54da057 100644 --- a/lib/cu_cp/routines/pdu_session_routine_helpers.cpp +++ b/lib/cu_cp/routines/pdu_session_routine_helpers.cpp @@ -22,7 +22,6 @@ #include "pdu_session_routine_helpers.h" #include "srsran/asn1/rrc_nr/cell_group_config.h" -#include "srsran/ran/cause/e1ap_cause_converters.h" using namespace srsran; using namespace srsran::srs_cu_cp; @@ -50,12 +49,12 @@ void srsran::srs_cu_cp::fill_e1ap_qos_flow_param_item(e1ap_qos_flow_qos_param_it request_item.qos_flow_level_qos_params.alloc_retention_prio; } -bool srsran::srs_cu_cp::verify_and_log_cell_group_config(const byte_buffer& packed_config, +bool srsran::srs_cu_cp::verify_and_log_cell_group_config(const byte_buffer& packed_cell_group_cfg, const srslog::basic_logger& logger) { // Unpack DU to CU container. asn1::rrc_nr::cell_group_cfg_s cell_group_cfg; - asn1::cbit_ref bref_cell({packed_config.begin(), packed_config.end()}); + asn1::cbit_ref bref_cell({packed_cell_group_cfg.begin(), packed_cell_group_cfg.end()}); if (cell_group_cfg.unpack(bref_cell) != asn1::SRSASN_SUCCESS) { logger.warning("Failed to unpack cellGroupConfig"); return false; @@ -80,7 +79,7 @@ bool srsran::srs_cu_cp::fill_rrc_reconfig_args( const std::optional rrc_meas_cfg, bool reestablish_srbs, bool reestablish_drbs, - bool update_keys, + std::optional ncc, byte_buffer sib1, const srslog::basic_logger& logger) { @@ -170,10 +169,10 @@ bool srsran::srs_cu_cp::fill_rrc_reconfig_args( rrc_recfg_v1530_ies.ded_nas_msg_list.push_back(nas_pdu.copy()); } - if (update_keys) { + if (ncc.has_value()) { rrc_recfg_v1530_ies.master_key_upd.emplace(); rrc_recfg_v1530_ies.master_key_upd.value().key_set_change_ind = false; - rrc_recfg_v1530_ies.master_key_upd.value().next_hop_chaining_count = 0; // TODO: remove hard-coded value + rrc_recfg_v1530_ies.master_key_upd.value().next_hop_chaining_count = ncc.value(); // TODO: remove hard-coded value } rrc_reconfig_args.non_crit_ext = rrc_recfg_v1530_ies; @@ -192,15 +191,16 @@ bool srsran::srs_cu_cp::fill_rrc_reconfig_args( return true; } -bool fill_f1ap_drb_setup_mod_item(f1ap_drb_to_setup& drb_setup_mod_item, // Request to setup DRB at DU. - slotted_id_vector* response_flow_list, - pdu_session_id_t psi, - drb_id_t drb_id, - up_drb_context& next_drb_config, // DRB config (info is written back). - const e1ap_drb_setup_item_ng_ran& e1ap_drb_item, // Response from CU-UP. - const slotted_id_vector& - ngap_qos_flow_setup_items, // Initial request from AMF. - const srslog::basic_logger& logger) +bool srsran::srs_cu_cp::fill_f1ap_drb_setup_mod_item( + f1ap_drb_to_setup& drb_setup_mod_item, // Request to setup DRB at DU. + slotted_id_vector* response_flow_list, + pdu_session_id_t psi, + drb_id_t drb_id, + up_drb_context& next_drb_config, // DRB config (info is written back). + const e1ap_drb_setup_item_ng_ran& e1ap_drb_item, // Response from CU-UP. + const slotted_id_vector& + ngap_qos_flow_setup_items, // Initial request from AMF. + const srslog::basic_logger& logger) { // Catch implementation limitations. if (!e1ap_drb_item.flow_failed_list.empty()) { @@ -237,7 +237,7 @@ bool fill_f1ap_drb_setup_mod_item(f1ap_drb_to_setup& drb_setup_mod_item, // Requ // QoS flows. for (const auto& e1ap_flow : e1ap_drb_item.flow_setup_list) { // Verify the QoS flow ID is present in original setup message. - if (ngap_qos_flow_setup_items.contains(e1ap_flow.qos_flow_id) == false) { + if (!ngap_qos_flow_setup_items.contains(e1ap_flow.qos_flow_id)) { logger.warning("PduSessionResourceSetupRequest doesn't include setup for {} in {}", e1ap_flow.qos_flow_id, psi); return false; } @@ -272,205 +272,6 @@ bool fill_f1ap_drb_setup_mod_item(f1ap_drb_to_setup& drb_setup_mod_item, // Requ return true; } -bool srsran::srs_cu_cp::update_setup_list( - slotted_id_vector& ngap_response_list, - std::vector& srb_setup_mod_list, - std::vector& drb_setup_mod_list, - const slotted_id_vector& ngap_setup_list, - const slotted_id_vector& - pdu_session_resource_setup_list, - up_config_update& next_config, - up_resource_manager& up_resource_mng, - const security_indication_t& default_security_indication, - const srslog::basic_logger& logger) -{ - // Set up SRB2 if this is the first DRB to be setup. - if (up_resource_mng.get_nof_drbs() == 0) { - f1ap_srb_to_setup srb2; - srb2.srb_id = srb_id_t::srb2; - srb_setup_mod_list.push_back(srb2); - } - - for (const auto& e1ap_item : pdu_session_resource_setup_list) { - const auto& psi = e1ap_item.pdu_session_id; - - // Sanity check - make sure this session ID is present in the original setup message. - if (ngap_setup_list.contains(psi) == false) { - logger.warning("PduSessionResourceSetupRequest doesn't include setup for {}", psi); - return false; - } - // Also check if PDU session is included in expected next configuration. - if (next_config.pdu_sessions_to_setup_list.find(psi) == next_config.pdu_sessions_to_setup_list.end()) { - logger.warning("Didn't expect setup for {}", psi); - return false; - } - - cu_cp_pdu_session_res_setup_response_item item; - item.pdu_session_id = psi; - - auto& transfer = item.pdu_session_resource_setup_response_transfer; - transfer.dlqos_flow_per_tnl_info.up_tp_layer_info = e1ap_item.ng_dl_up_tnl_info; - - // Determine security settings for this PDU session and decide whether we have to send the security_result via NGAP. - bool integrity_enabled = false; - bool ciphering_enabled = false; - - if (ngap_setup_list[psi].security_ind.has_value()) { - // TS 38.413 Sec. 8.2.1.2: - // For each PDU session for which the Security Indication IE is included in the PDU Session Resource Setup Request - // Transfer IE of the PDU SESSION RESOURCE SETUP REQUEST message, and the Integrity Protection Indication IE - // or Confidentiality Protection Indication IE is set to "preferred", then the NG-RAN node should, if supported, - // perform user plane integrity protection or ciphering, respectively, for the concerned PDU session and shall - // notify whether it performed the user plane integrity protection or ciphering by including the Integrity - // Protection Result IE or Confidentiality Protection Result IE, respectively, in the PDU Session Resource Setup - // Response Transfer IE of the PDU SESSION RESOURCE SETUP RESPONSE message. - const auto& ngap_sec_ind = ngap_setup_list[psi].security_ind.value(); - if (security_result_required(ngap_sec_ind)) { - // Apply security settings according to the decision in the CU-UP. - if (!e1ap_item.security_result.has_value()) { - logger.warning("Missing security result in E1AP response for {}", psi); - return false; - } - auto& sec_res = e1ap_item.security_result.value(); - integrity_enabled = sec_res.integrity_protection_result == integrity_protection_result_t::performed; - ciphering_enabled = sec_res.confidentiality_protection_result == confidentiality_protection_result_t::performed; - // Add result to NGAP response - transfer.security_result = sec_res; - } else { - // Apply security settings that were requested via NGAP and do not require an explicit reponse. - integrity_enabled = ngap_sec_ind.integrity_protection_ind == integrity_protection_indication_t::required; - ciphering_enabled = - ngap_sec_ind.confidentiality_protection_ind == confidentiality_protection_indication_t::required; - } - } else { - // Security settings were not signaled via NGAP, we have used the defaults of CU-CP. - const auto sec_ind = default_security_indication; - if (security_result_required(sec_ind)) { - // Apply security settings according to the decision in the CU-UP. - if (!e1ap_item.security_result.has_value()) { - logger.warning("Missing security result in E1AP response for {}", psi); - return false; - } - const auto& sec_res = e1ap_item.security_result.value(); - integrity_enabled = sec_res.integrity_protection_result == integrity_protection_result_t::performed; - ciphering_enabled = sec_res.confidentiality_protection_result == confidentiality_protection_result_t::performed; - // No result in NGAP response needed here. - } else { - // Apply default security settings that do not require an explicit response. - integrity_enabled = sec_ind.integrity_protection_ind == integrity_protection_indication_t::required; - ciphering_enabled = sec_ind.confidentiality_protection_ind == confidentiality_protection_indication_t::required; - } - } - - auto& next_cfg_pdu_session = next_config.pdu_sessions_to_setup_list.at(psi); - - for (const auto& e1ap_drb_item : e1ap_item.drb_setup_list_ng_ran) { - const auto& drb_id = e1ap_drb_item.drb_id; - if (next_config.pdu_sessions_to_setup_list.at(psi).drb_to_add.find(drb_id) == - next_config.pdu_sessions_to_setup_list.at(psi).drb_to_add.end()) { - logger.warning("DRB id {} not part of next configuration", drb_id); - return false; - } - - // Update security settings of each DRB. - next_cfg_pdu_session.drb_to_add.find(drb_id)->second.pdcp_cfg.integrity_protection_required = integrity_enabled; - next_cfg_pdu_session.drb_to_add.find(drb_id)->second.pdcp_cfg.ciphering_required = ciphering_enabled; - - // Prepare DRB item for DU. - f1ap_drb_to_setup drb_setup_mod_item; - if (!fill_f1ap_drb_setup_mod_item(drb_setup_mod_item, - &transfer.dlqos_flow_per_tnl_info.associated_qos_flow_list, - item.pdu_session_id, - drb_id, - next_config.pdu_sessions_to_setup_list.at(psi).drb_to_add.at(drb_id), - e1ap_drb_item, - ngap_setup_list[item.pdu_session_id].qos_flow_setup_request_items, - logger)) { - logger.warning("Couldn't populate DRB setup/mod item {}", e1ap_drb_item.drb_id); - return false; - } - drb_setup_mod_list.push_back(drb_setup_mod_item); - } - - // Fail on any DRB that fails to be setup. - if (!e1ap_item.drb_failed_list_ng_ran.empty()) { - logger.warning("Non-empty DRB failed list not supported"); - return false; - } - - ngap_response_list.emplace(item.pdu_session_id, item); - } - - return true; -} - -bool srsran::srs_cu_cp::update_setup_list( - std::vector& srb_setup_mod_list, - std::vector& drb_setup_mod_list, - const slotted_id_vector& ngap_setup_list, - const slotted_id_vector& - pdu_session_resource_setup_list, - up_config_update& next_config, - up_resource_manager& up_resource_mng, - const srslog::basic_logger& logger) -{ - // Set up SRB1 and SRB2 (this is for inter CU handover, so no SRBs are setup yet). - // TODO: Do we need to setup SRB0 here as well? - for (unsigned srb_id = 1; srb_id < 3; ++srb_id) { - f1ap_srb_to_setup srb_item; - srb_item.srb_id = int_to_srb_id(srb_id); - srb_setup_mod_list.push_back(srb_item); - } - - for (const auto& e1ap_item : pdu_session_resource_setup_list) { - const auto& psi = e1ap_item.pdu_session_id; - - // Sanity check - make sure this session ID is present in the original setup message. - if (ngap_setup_list.contains(e1ap_item.pdu_session_id) == false) { - logger.warning("PduSessionResourceSetupRequest doesn't include setup for {}", e1ap_item.pdu_session_id); - return false; - } - // Also check if PDU session is included in expected next configuration. - if (next_config.pdu_sessions_to_setup_list.find(e1ap_item.pdu_session_id) == - next_config.pdu_sessions_to_setup_list.end()) { - logger.warning("Didn't expect setup for {}", e1ap_item.pdu_session_id); - return false; - } - - for (const auto& e1ap_drb_item : e1ap_item.drb_setup_list_ng_ran) { - const auto& drb_id = e1ap_drb_item.drb_id; - if (next_config.pdu_sessions_to_setup_list.at(psi).drb_to_add.find(drb_id) == - next_config.pdu_sessions_to_setup_list.at(psi).drb_to_add.end()) { - logger.warning("{} not part of next configuration", drb_id); - return false; - } - - // Prepare DRB item for DU. - f1ap_drb_to_setup drb_setup_mod_item; - if (!fill_f1ap_drb_setup_mod_item(drb_setup_mod_item, - {}, - e1ap_item.pdu_session_id, - drb_id, - next_config.pdu_sessions_to_setup_list.at(psi).drb_to_add.at(drb_id), - e1ap_drb_item, - ngap_setup_list[e1ap_item.pdu_session_id].qos_flow_setup_request_items, - logger)) { - logger.warning("Couldn't populate DRB setup/mod item {}", e1ap_drb_item.drb_id); - return false; - } - drb_setup_mod_list.push_back(drb_setup_mod_item); - } - - // Fail on any DRB that fails to be setup. - if (!e1ap_item.drb_failed_list_ng_ran.empty()) { - logger.warning("Non-empty DRB failed list not supported"); - return false; - } - } - - return true; -} - void srsran::srs_cu_cp::fill_drb_to_setup_list( slotted_id_vector& e1ap_drb_to_setup_list, const slotted_id_vector& qos_flow_list, @@ -538,114 +339,6 @@ void srsran::srs_cu_cp::fill_drb_to_remove_list(std::vector& e1a } } -void srsran::srs_cu_cp::update_failed_list( - slotted_id_vector& ngap_failed_list, - const slotted_id_vector& pdu_session_resource_failed_list, - up_config_update& next_config) -{ - for (const auto& e1ap_item : pdu_session_resource_failed_list) { - // Remove from next config. - next_config.pdu_sessions_to_setup_list.erase(e1ap_item.pdu_session_id); - - // Add to list taking cause received from CU-UP. - cu_cp_pdu_session_res_setup_failed_item failed_item; - failed_item.pdu_session_id = e1ap_item.pdu_session_id; - failed_item.unsuccessful_transfer.cause = e1ap_to_ngap_cause(e1ap_item.cause); - ngap_failed_list.emplace(failed_item.pdu_session_id, failed_item); - } -} - -bool srsran::srs_cu_cp::update_modify_list( - slotted_id_vector& ngap_response_list, - f1ap_ue_context_modification_request& ue_context_mod_request, - const slotted_id_vector& ngap_modify_list, - const slotted_id_vector& - e1ap_pdu_session_resource_modify_list, - up_config_update& next_config, - const srslog::basic_logger& logger) -{ - for (const auto& e1ap_item : e1ap_pdu_session_resource_modify_list) { - const auto& psi = e1ap_item.pdu_session_id; - // Sanity check - make sure this session ID is present in the original modify message. - if (ngap_modify_list.contains(psi) == false) { - logger.warning("PduSessionResourceSetupRequest doesn't include setup for {}", psi); - return false; - } - // Also check if PDU session is included in expected next configuration. - if (next_config.pdu_sessions_to_modify_list.find(psi) == next_config.pdu_sessions_to_modify_list.end()) { - logger.warning("Didn't expect modification for {}", psi); - return false; - } - - if (ngap_response_list.contains(psi)) { - // Load existing response item from previous call. - logger.debug("Amend to existing NGAP response item for {}", psi); - } else { - // Add empty new item. - cu_cp_pdu_session_resource_modify_response_item new_item; - new_item.pdu_session_id = psi; - ngap_response_list.emplace(new_item.pdu_session_id, new_item); - logger.debug("Insert new NGAP response item for {}", psi); - } - - // Start/continue filling response item. - cu_cp_pdu_session_resource_modify_response_item& ngap_item = ngap_response_list[psi]; - for (const auto& e1ap_drb_item : e1ap_item.drb_setup_list_ng_ran) { - const auto& drb_id = e1ap_drb_item.drb_id; - if (next_config.pdu_sessions_to_modify_list.at(psi).drb_to_add.find(drb_id) == - next_config.pdu_sessions_to_modify_list.at(psi).drb_to_add.end()) { - logger.warning("{} not part of next configuration", drb_id); - return false; - } - - const auto& request_transfer = ngap_modify_list[psi].transfer; - - // Prepare DRB creation at DU. - f1ap_drb_to_setup drb_setup_mod_item; - if (!fill_f1ap_drb_setup_mod_item(drb_setup_mod_item, - nullptr, - psi, - drb_id, - next_config.pdu_sessions_to_modify_list.at(psi).drb_to_add.at(drb_id), - e1ap_drb_item, - request_transfer.qos_flow_add_or_modify_request_list, - logger)) { - logger.warning("Couldn't populate DRB setup/mod item {}", e1ap_drb_item.drb_id); - return false; - } - - // Note: this extra handling for the Modification could be optimized. - for (const auto& e1ap_flow : e1ap_drb_item.flow_setup_list) { - // Fill added flows in NGAP response transfer. - if (!ngap_item.transfer.qos_flow_add_or_modify_response_list.has_value()) { - // Add list if it's not present yet. - ngap_item.transfer.qos_flow_add_or_modify_response_list.emplace(); - } - - qos_flow_add_or_mod_response_item qos_flow; - qos_flow.qos_flow_id = e1ap_flow.qos_flow_id; - ngap_item.transfer.qos_flow_add_or_modify_response_list.value().emplace(qos_flow.qos_flow_id, qos_flow); - } - - // Finally add DRB to setup to UE context modification. - ue_context_mod_request.drbs_to_be_setup_mod_list.push_back(drb_setup_mod_item); - } - - // Add DRB to be removed to UE context modifcation. - for (const auto& drb_id : next_config.pdu_sessions_to_modify_list.at(psi).drb_to_remove) { - ue_context_mod_request.drbs_to_be_released_list.push_back(drb_id); - } - - // Fail on any DRB that fails to be setup. - if (!e1ap_item.drb_failed_list_ng_ran.empty()) { - logger.warning("Non-empty DRB failed list not supported"); - return false; - } - } - - return true; -} - void srsran::srs_cu_cp::fill_e1ap_bearer_context_list( slotted_id_vector& e1ap_list, const std::vector& drb_setup_items, @@ -716,54 +409,11 @@ void srsran::srs_cu_cp::fill_e1ap_pdu_session_res_to_setup_list( } } -bool srsran::srs_cu_cp::update_modify_list( - slotted_id_vector& ngap_response_list, - e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, - const slotted_id_vector& ngap_modify_list, - const f1ap_ue_context_modification_response& ue_context_modification_response, - const up_config_update& next_config, - const srslog::basic_logger& logger) -{ - // Fail procedure if (single) DRB couldn't be setup. - if (!ue_context_modification_response.drbs_failed_to_be_setup_list.empty()) { - logger.warning("Couldn't setup {} DRBs at DU", - ue_context_modification_response.drbs_failed_to_be_setup_list.size()); - return false; - } - - // Only prepare bearer context modifcation request if needed. - if (ue_context_modification_response.drbs_setup_list.empty() and - ue_context_modification_response.drbs_modified_list.empty()) { - // No DRB added or updated. - logger.debug("Skipping preparation of bearer context modification request"); - bearer_ctxt_mod_request.ng_ran_bearer_context_mod_request.reset(); - return ue_context_modification_response.success; - } - - // Start with empty message. - e1ap_ng_ran_bearer_context_mod_request& e1ap_bearer_context_mod = - bearer_ctxt_mod_request.ng_ran_bearer_context_mod_request.emplace(); - - fill_e1ap_bearer_context_list(e1ap_bearer_context_mod.pdu_session_res_to_modify_list, - ue_context_modification_response.drbs_setup_list, - next_config.pdu_sessions_to_modify_list); - -#if 0 - // Let all PDU sessions fail if response is negative. - if (ue_context_modification_response.success == false) { - fill_response_failed_list(ngap_response_list, ngap_modify_list); - } -#endif - - // TODO: traverse other fields - - return ue_context_modification_response.success; -} - -bool srsran::srs_cu_cp::update_setup_list(e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, - const std::vector& drb_setup_mod_list, - const up_config_update& next_config, - const srslog::basic_logger& logger) +bool srsran::srs_cu_cp::update_setup_list_with_ue_ctxt_setup_response( + e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, + const std::vector& drb_setup_mod_list, + const up_config_update& next_config, + const srslog::basic_logger& logger) { // Start with empty message. e1ap_ng_ran_bearer_context_mod_request& e1ap_bearer_context_mod = diff --git a/lib/cu_cp/routines/pdu_session_routine_helpers.h b/lib/cu_cp/routines/pdu_session_routine_helpers.h index ee5b9c599a..1c684163cb 100644 --- a/lib/cu_cp/routines/pdu_session_routine_helpers.h +++ b/lib/cu_cp/routines/pdu_session_routine_helpers.h @@ -34,7 +34,26 @@ namespace srsran { namespace srs_cu_cp { -bool verify_and_log_cell_group_config(const byte_buffer& cell_group_cfg, const srslog::basic_logger& logger); +bool verify_and_log_cell_group_config(const byte_buffer& packed_cell_group_cfg, const srslog::basic_logger& logger); + +/// \brief Fill thew DRB setup mod item for the DU. +/// \param[out] drb_setup_mod_item The DRB setup item to fill. +/// \param[in] response_flow_list The list of QoS flows to be added to the NGAP response. +/// \param[in] psi The PDU session ID. +/// \param[in] drb_id The DRB ID. +/// \param[in] next_drb_config The DRB config to be updated. +/// \param[in] e1ap_drb_item The DRB setup response from CU-UP. +/// \param[in] ngap_qos_flow_setup_items The initial QoS flow setup request from AMF. +/// \param[in] logger The logger. +bool fill_f1ap_drb_setup_mod_item(f1ap_drb_to_setup& drb_setup_mod_item, // Request to setup DRB at DU. + slotted_id_vector* response_flow_list, + pdu_session_id_t psi, + drb_id_t drb_id, + up_drb_context& next_drb_config, // DRB config (info is written back). + const e1ap_drb_setup_item_ng_ran& e1ap_drb_item, // Response from CU-UP. + const slotted_id_vector& + ngap_qos_flow_setup_items, // Initial request from AMF. + const srslog::basic_logger& logger); void fill_e1ap_drb_pdcp_config(e1ap_pdcp_config& e1ap_pdcp_cfg, const pdcp_config& cu_cp_pdcp_cfg); void fill_e1ap_qos_flow_param_item(e1ap_qos_flow_qos_param_item& e1ap_qos_item, @@ -84,73 +103,20 @@ bool fill_rrc_reconfig_args(rrc_reconfiguration_procedure_request& const std::optional rrc_meas_cfg, bool reestablish_srbs, bool reestablish_drbs, - bool update_keys, + std::optional ncc, byte_buffer sib1, const srslog::basic_logger& logger); -bool update_setup_list( - slotted_id_vector& ngap_response_list, - std::vector& srb_setup_mod_list, - std::vector& drb_setup_mod_list, - const slotted_id_vector& ngap_setup_list, - const slotted_id_vector& - pdu_session_resource_setup_list, - up_config_update& next_config, - up_resource_manager& up_resource_mng, - const security_indication_t& default_security_indication, - const srslog::basic_logger& logger); - -bool update_setup_list(std::vector& srb_setup_mod_list, - std::vector& drb_setup_mod_list, - const slotted_id_vector& ngap_setup_list, - const slotted_id_vector& - pdu_session_resource_setup_list, - up_config_update& next_config, - up_resource_manager& up_resource_mng, - const srslog::basic_logger& logger); - -bool update_setup_list(e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, - const std::vector& drb_setup_mod_list, - const up_config_update& next_config, - const srslog::basic_logger& logger); - -void update_failed_list( - slotted_id_vector& ngap_failed_list, - const slotted_id_vector& pdu_session_resource_failed_list, - up_config_update& next_config); - -/// \brief Processes the result of a Bearer Context Modifcation Result's PDU session modify list. -/// \param[out] ngap_response_list Reference to the final NGAP response -/// \param[out] ue_context_mod_request Reference to the next request message - a UE context modification. -/// \param[in] ngap_modify_list Const reference to the original NGAP request -/// \param[in] e1ap_pdu_session_resource_modify_list Const reference to the response of the previous subprocedure -/// \param[in]next_config Const reference to the calculated config update -/// \param[in] logger Reference to the logger. -/// \return True on success, false otherwise. -bool update_modify_list( - slotted_id_vector& ngap_response_list, - f1ap_ue_context_modification_request& ue_context_mod_request, - const slotted_id_vector& ngap_modify_list, - const slotted_id_vector& - e1ap_pdu_session_resource_modify_list, - up_config_update& next_config, - const srslog::basic_logger& logger); - -/// \brief Processes the response of a UE Context Modifcation Request. -/// \param[out] ngap_response_list Reference to the final NGAP response. -/// \param[out] ue_context_mod_request Reference to the next request message - a Bearer context modification request. -/// \param[in] ngap_modify_list Const reference to the original NGAP request -/// \param[in] ue_context_modification_response Const reference to the response of the UE context modifcation request. -/// \param[in] next_config Const reference to the calculated config update +/// \brief Processes the response of a UE Context Setup Request. +/// \param[out] bearer_ctxt_mod_request Reference to the resulting Bearer Context Modification Request response. +/// \param[out] drb_setup_mod_list Reference to the successful DRB setup list. +/// \param[out] next_config Const reference to the calculated config update. /// \param[in] logger Reference to the logger. /// \return True on success, false otherwise. -bool update_modify_list( - slotted_id_vector& ngap_response_list, - e1ap_bearer_context_modification_request& bearer_context_mod_request, - const slotted_id_vector& ngap_modify_list, - const f1ap_ue_context_modification_response& ue_context_modification_response, - const up_config_update& next_config, - const srslog::basic_logger& logger); +bool update_setup_list_with_ue_ctxt_setup_response(e1ap_bearer_context_modification_request& bearer_ctxt_mod_request, + const std::vector& drb_setup_mod_list, + const up_config_update& next_config, + const srslog::basic_logger& logger); } // namespace srs_cu_cp } // namespace srsran diff --git a/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp b/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp index b03c10e80e..9bde8f3673 100644 --- a/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp +++ b/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp @@ -139,7 +139,7 @@ void reestablishment_context_modification_routine::operator()(coro_context& setup_items, const up_context& context, const up_resource_manager_cfg& cfg, diff --git a/lib/du/du_high/adapters/du_high_adapter_factories.h b/lib/du/du_high/adapters/du_high_adapter_factories.h index 0996e81b3b..b861f7d248 100644 --- a/lib/du/du_high/adapters/du_high_adapter_factories.h +++ b/lib/du/du_high/adapters/du_high_adapter_factories.h @@ -30,8 +30,8 @@ namespace srsran { namespace srs_du { /// \brief Create a MAC instance for DU-high. In case the test mode is enabled, the MAC messages will be intercepted. -std::unique_ptr create_du_high_mac(const mac_config& mac_cfg, - const srs_du::du_test_mode_config& test_cfg); +std::unique_ptr +create_du_high_mac(const mac_config& mac_cfg, const srs_du::du_test_mode_config& test_cfg, unsigned nof_cells); } // namespace srs_du } // namespace srsran diff --git a/lib/du/du_high/du_high_impl.cpp b/lib/du/du_high/du_high_impl.cpp index 882bf72e83..a1ba1098cd 100644 --- a/lib/du/du_high/du_high_impl.cpp +++ b/lib/du/du_high/du_high_impl.cpp @@ -125,7 +125,8 @@ du_high_impl::du_high_impl(const du_high_configuration& config_) : cfg.ran.sched_cfg, cfg.sched_ue_metrics_notifier ? *cfg.sched_ue_metrics_notifier : *metrics_notifier, timers}, - cfg.test_cfg); + cfg.test_cfg, + cfg.ran.cells.size()); f1ap = create_du_high_f1ap(*cfg.f1c_client, adapters->f1_to_du_notifier, cfg.exec_mapper->du_control_executor(), @@ -178,17 +179,22 @@ void du_high_impl::start() // If test mode is enabled, create a test-mode UE by injecting a Msg3. if (cfg.test_cfg.test_ue.has_value()) { - // Push an UL-CCCH message that will trigger the creation of a UE for testing purposes. - for (unsigned ue_num = 0, nof_ues = cfg.test_cfg.test_ue->nof_ues; ue_num != nof_ues; ++ue_num) { - auto rx_buf = byte_buffer::create({0x34, 0x1e, 0x4f, 0xc0, 0x4f, 0xa6, 0x06, 0x3f, 0x00, 0x00, 0x00}); - if (not rx_buf.has_value()) { - logger.warning("Unable to allocate byte_buffer"); - continue; + for (unsigned cell_id = 0, cell_end = cfg.ran.cells.size(); cell_id != cell_end; ++cell_id) { + // Push an UL-CCCH message that will trigger the creation of a UE for testing purposes. + for (unsigned ue_num = 0, nof_ues = cfg.test_cfg.test_ue->nof_ues; ue_num != nof_ues; ++ue_num) { + auto rx_buf = byte_buffer::create({0x34, 0x1e, 0x4f, 0xc0, 0x4f, 0xa6, 0x06, 0x3f, 0x00, 0x00, 0x00}); + if (not rx_buf.has_value()) { + logger.warning("Unable to allocate byte_buffer"); + continue; + } + mac->get_pdu_handler().handle_rx_data_indication(mac_rx_data_indication{ + slot_point{0, 0}, + to_du_cell_index(cell_id), + {mac_rx_pdu{to_rnti(to_value(cfg.test_cfg.test_ue->rnti) + (cell_id * nof_ues) + ue_num), + 0, + 0, + std::move(rx_buf.value())}}}); } - mac->get_pdu_handler().handle_rx_data_indication(mac_rx_data_indication{ - slot_point{0, 0}, - to_du_cell_index(0), - {mac_rx_pdu{to_rnti(to_value(cfg.test_cfg.test_ue->rnti) + ue_num), 0, 0, std::move(rx_buf.value())}}}); } } } diff --git a/lib/du/du_high/du_manager/du_ue/CMakeLists.txt b/lib/du/du_high/du_manager/du_ue/CMakeLists.txt index 7402494b6e..538e2de5c0 100644 --- a/lib/du/du_high/du_manager/du_ue/CMakeLists.txt +++ b/lib/du/du_high/du_manager/du_ue/CMakeLists.txt @@ -19,4 +19,4 @@ # add_library(du_ue du_bearer.cpp du_ue_adapters.cpp du_ue_bearer_manager.cpp du_ue_controller_impl.cpp du_ue_manager.cpp) -target_link_libraries(du_ue srsran_du_manager_procedures srsran_f1u_du) +target_link_libraries(du_ue srsran_du_manager_procedures srsran_f1u_du srsran_gtpu) diff --git a/lib/du/du_high/du_manager/procedures/du_ue_ric_configuration_procedure.cpp b/lib/du/du_high/du_manager/procedures/du_ue_ric_configuration_procedure.cpp index eb9f4d9d2d..1a997952b3 100644 --- a/lib/du/du_high/du_manager/procedures/du_ue_ric_configuration_procedure.cpp +++ b/lib/du/du_high/du_manager/procedures/du_ue_ric_configuration_procedure.cpp @@ -91,15 +91,32 @@ async_task du_ue_ric_configuration_procedure::h rrm_policy_ratio_group dummy = {}; res_alloc_cfg.rrm_policy_group = req.rrm_policy_group.has_value() ? req.rrm_policy_group.value() : dummy; // TODO remove when RRM group support is added to scheduler. - res_alloc_cfg.pdsch_grant_size_limits = { - req.rrm_policy_group.has_value() ? (req.rrm_policy_group.value().min_prb_policy_ratio.has_value() - ? req.rrm_policy_group.value().min_prb_policy_ratio.value() - : 0) - : 0, - req.rrm_policy_group.has_value() ? (req.rrm_policy_group.value().max_prb_policy_ratio.has_value() - ? req.rrm_policy_group.value().max_prb_policy_ratio.value() - : MAX_NOF_PRBS) - : MAX_NOF_PRBS}; + int min_prb_limit = 0; + int max_prb_limit = MAX_NOF_PRBS; + if (req.rrm_policy_group.has_value()) { + unsigned int nof_prbs = get_max_Nprb(du_params.ran.cells[0].dl_carrier.carrier_bw_mhz, + du_params.ran.cells[0].scs_common, + band_helper::get_freq_range(du_params.ran.cells[0].dl_carrier.band)); + + min_prb_limit = req.rrm_policy_group.value().min_prb_policy_ratio.has_value() + ? req.rrm_policy_group.value().min_prb_policy_ratio.value() + : 0; + max_prb_limit = req.rrm_policy_group.value().max_prb_policy_ratio.has_value() + ? req.rrm_policy_group.value().max_prb_policy_ratio.value() + : MAX_NOF_PRBS; + + min_prb_limit = std::max(0, std::min(min_prb_limit, 100)); + max_prb_limit = std::max(0, std::min(max_prb_limit, 100)); + + if (max_prb_limit < min_prb_limit) { + std::swap(max_prb_limit, min_prb_limit); + } + + min_prb_limit = static_cast((1.0 * min_prb_limit / 100) * nof_prbs); + max_prb_limit = static_cast((1.0 * max_prb_limit / 100) * nof_prbs); + } + res_alloc_cfg.pdsch_grant_size_limits = {min_prb_limit, max_prb_limit}; + res_alloc_cfg.pusch_grant_size_limits = {min_prb_limit, max_prb_limit}; res_alloc_cfg.max_pdsch_harq_retxs = req.num_harq_retransmissions.has_value() ? req.num_harq_retransmissions.value() diff --git a/lib/du/du_high/du_manager/ran_resource_management/du_srs_resource_manager.cpp b/lib/du/du_high/du_manager/ran_resource_management/du_srs_resource_manager.cpp index 50b860705e..e7aab2dcde 100644 --- a/lib/du/du_high/du_manager/ran_resource_management/du_srs_resource_manager.cpp +++ b/lib/du/du_high/du_manager/ran_resource_management/du_srs_resource_manager.cpp @@ -261,8 +261,7 @@ bool du_srs_policy_max_ul_rate::alloc_resources(cell_group_config& cell_grp_cfg) cells[primary_cell_index].cell_cfg.ul_carrier.nof_ant == 2 or cells[primary_cell_index].cell_cfg.ul_carrier.nof_ant == 4, "The number of UL antenna ports is not valid"); - only_ue_srs_res.nof_ports = - static_cast(cells[primary_cell_index].cell_cfg.ul_carrier.nof_ant); + only_ue_srs_res.nof_ports = srs_config::srs_resource::nof_srs_ports::port1; only_ue_srs_res.tx_comb.size = cells[primary_cell_index].cell_cfg.srs_cfg.tx_comb; only_ue_srs_res.tx_comb.tx_comb_offset = du_res.tx_comb_offset.to_uint(); only_ue_srs_res.tx_comb.tx_comb_cyclic_shift = du_res.cs; diff --git a/lib/du/du_high/du_manager/ran_resource_management/ue_capability_manager.cpp b/lib/du/du_high/du_manager/ran_resource_management/ue_capability_manager.cpp index 43d65b78cc..aebb18dd6c 100644 --- a/lib/du/du_high/du_manager/ran_resource_management/ue_capability_manager.cpp +++ b/lib/du/du_high/du_manager/ran_resource_management/ue_capability_manager.cpp @@ -109,6 +109,28 @@ static void set_pusch_mcs_table(serving_cell_config& cell_cfg, pusch_mcs_table m } } +// Configure dedicated UE configuration to set UL-MIMO related parameters. +static void set_ul_mimo(serving_cell_config& cell_cfg, + unsigned max_rank, + unsigned nof_srs_ports, + tx_scheme_codebook_subset codebook_subset) +{ + // Skip if the UL configuration is not present. + if (SRSRAN_UNLIKELY(!cell_cfg.ul_config.has_value() || !cell_cfg.ul_config->init_ul_bwp.pusch_cfg || + !cell_cfg.ul_config->init_ul_bwp.srs_cfg)) { + return; + } + + // Prepare codebook transmission parameters. + cell_cfg.ul_config->init_ul_bwp.pusch_cfg->tx_cfg = + tx_scheme_codebook{.max_rank = max_rank, .codebook_subset = codebook_subset}; + + // Force the number of ports for all SRS resources to the maximum the UE supports. + for (auto& srs_res : cell_cfg.ul_config->init_ul_bwp.srs_cfg->srs_res_list) { + srs_res.nof_ports = static_cast(nof_srs_ports); + } +} + ue_capability_manager::ue_capability_manager(span cell_cfg_list_, srslog::basic_logger& logger_) : base_cell_cfg_list(cell_cfg_list_), logger(logger_) { @@ -132,6 +154,10 @@ void ue_capability_manager::update(du_ue_resource_config& ue_res_cfg, const byte // Enable 256QAM for PUSCH, if supported. set_pusch_mcs_table(pcell_cfg, select_pusch_mcs_table(cell_idx)); + + // Setup UL MIMO parameters. + set_ul_mimo( + pcell_cfg, select_pusch_max_rank(cell_idx), select_srs_nof_ports(cell_idx), select_tx_codebook_subset(cell_idx)); } bool ue_capability_manager::decode_ue_capability_list(const byte_buffer& ue_cap_rat_list) @@ -210,41 +236,39 @@ pusch_mcs_table ue_capability_manager::select_pusch_mcs_table(du_cell_index_t ce tx_scheme_codebook_subset ue_capability_manager::select_tx_codebook_subset(du_cell_index_t cell_idx) const { - nr_band band = base_cell_cfg_list[cell_idx].ul_carrier.band; - const auto& base_ul_cfg = base_cell_cfg_list[cell_idx].ue_ded_serv_cell_cfg.ul_config; + nr_band band = base_cell_cfg_list[cell_idx].ul_carrier.band; - // Default to non-coherent, if no PUSCH config or no UE capabilities decoded yet. - if (not base_ul_cfg.has_value() or not base_ul_cfg->init_ul_bwp.pusch_cfg.has_value() or not ue_caps.has_value()) { - return tx_scheme_codebook_subset::non_coherent; + // If UE capabilities or the band are not available, return default value. + if (not ue_caps.has_value() || ue_caps->bands.count(band) == 0) { + return ue_capability_summary::default_pusch_tx_coherence; } - // If the band capability is present, select the codebook from this band. - if (ue_caps->bands.count(band)) { - return ue_caps->bands.at(band).pusch_tx_coherence; - } + return ue_caps->bands.at(band).pusch_tx_coherence; +} - // Find the band with the most limiting transmit codebook subset across all the bands. - const auto min_tx_codebook_subset = - std::min_element(ue_caps->bands.begin(), ue_caps->bands.end(), [](const auto& lhs, const auto& rhs) { - // The most limiting codebook subset has the greater integer value. - return rhs.second.pusch_tx_coherence > lhs.second.pusch_tx_coherence; - }); +unsigned ue_capability_manager::select_srs_nof_ports(du_cell_index_t cell_idx) const +{ + nr_band band = base_cell_cfg_list[cell_idx].ul_carrier.band; - // Select the most limiting transmit subset if the band list is not emtpy. - if (min_tx_codebook_subset != ue_caps->bands.end()) { - return min_tx_codebook_subset->second.pusch_tx_coherence; + // If UE capabilities or the band are not available, return default value. + if (not ue_caps.has_value() || ue_caps->bands.count(band) == 0) { + return ue_capability_summary::default_nof_srs_tx_ports; } - // Otherwise, default to non-coherent. - return tx_scheme_codebook_subset::non_coherent; + return ue_caps->bands.at(band).nof_srs_tx_ports; } -unsigned ue_capability_manager::select_srs_nof_ports() const +unsigned ue_capability_manager::select_pusch_max_rank(du_cell_index_t cell_idx) const { - // Default to one port if the UE capabilities are not present. - if (not ue_caps.has_value()) { - return 1; + nr_band band = base_cell_cfg_list[cell_idx].ul_carrier.band; + + // Configured maximum number of layers. + unsigned pusch_max_rank = base_cell_cfg_list[cell_idx].pusch_max_nof_layers; + + // If UE capabilities or the band are not available, return default value. + if (not ue_caps.has_value() || ue_caps->bands.count(band) == 0) { + return std::min(pusch_max_rank, ue_capability_summary::default_pusch_max_rank); } - return ue_caps->nof_srs_tx_ports; + return std::min(pusch_max_rank, ue_caps->bands.at(band).pusch_max_rank); } diff --git a/lib/du/du_high/du_manager/ran_resource_management/ue_capability_manager.h b/lib/du/du_high/du_manager/ran_resource_management/ue_capability_manager.h index 0045989552..337ce0cb37 100644 --- a/lib/du/du_high/du_manager/ran_resource_management/ue_capability_manager.h +++ b/lib/du/du_high/du_manager/ran_resource_management/ue_capability_manager.h @@ -40,6 +40,16 @@ namespace srs_du { /// Flat structure summarizing the decoded ASN.1 UE capabilities. struct ue_capability_summary { + /// \defgroup default_caps Default parameters. + /// @{ + /// Default PUSCH transmit coherence. + static constexpr tx_scheme_codebook_subset default_pusch_tx_coherence = tx_scheme_codebook_subset::non_coherent; + /// Default PUSCH maximum number of layers. + static constexpr unsigned default_pusch_max_rank = 1; + /// Default SRS number of transmit ports. + static constexpr unsigned default_nof_srs_tx_ports = 1; + /// @} + /// Contains band specific parameters. struct supported_band { /// Set to true if QAM-256 is supported for PUSCH transmissions. @@ -49,14 +59,15 @@ struct ue_capability_summary { /// It is given by field \e pusch-TransCoherence in Information Element \e MIMO-ParametersPerBand. /// /// The most limiting transmit codebook subset is selected by default. - tx_scheme_codebook_subset pusch_tx_coherence = tx_scheme_codebook_subset::non_coherent; + tx_scheme_codebook_subset pusch_tx_coherence = default_pusch_tx_coherence; + /// Maximum PUSCH number of layers. + unsigned pusch_max_rank = default_pusch_max_rank; + /// Maximum number of ports that can be simultaneously used for transmiting Sounding Reference Signals. + uint8_t nof_srs_tx_ports = default_nof_srs_tx_ports; }; /// Set to true if QAM-256 MCS table are supported for PDSCH transmissions. bool pdsch_qam256_supported = false; - /// Number of Sounding Reference Signals transmit ports. It is set to the most limiting band combination of - /// transmitters. - uint8_t nof_srs_tx_ports = 1; /// Contains specific bands capabilities. std::unordered_map bands; /// Set to true if Long DRX cycle is supported. @@ -96,7 +107,9 @@ class ue_capability_manager /// Selects the PUSCH transmission codebook subset. tx_scheme_codebook_subset select_tx_codebook_subset(du_cell_index_t cell_idx) const; /// Selects the SRS transmission number of ports. - unsigned select_srs_nof_ports() const; + unsigned select_srs_nof_ports(du_cell_index_t cell_idx) const; + /// Selects the PUSCH maximum number of layers. + unsigned select_pusch_max_rank(du_cell_index_t cell_idx) const; span base_cell_cfg_list; srslog::basic_logger& logger; diff --git a/lib/du/du_high/test_mode/mac_test_mode_adapter.cpp b/lib/du/du_high/test_mode/mac_test_mode_adapter.cpp index 1b4382548d..2a24e43de8 100644 --- a/lib/du/du_high/test_mode/mac_test_mode_adapter.cpp +++ b/lib/du/du_high/test_mode/mac_test_mode_adapter.cpp @@ -73,8 +73,8 @@ size_t get_ring_size(const mac_cell_creation_request& cell_cfg) } // namespace -test_ue_info_manager::test_ue_info_manager(rnti_t rnti_start_, uint16_t nof_ues_) : - rnti_start(rnti_start_), nof_ues(nof_ues_), pending_tasks(128) +test_ue_info_manager::test_ue_info_manager(rnti_t rnti_start_, uint16_t nof_ues_, uint16_t nof_cells_) : + rnti_start(rnti_start_), nof_ues(nof_ues_), nof_cells(nof_cells_), pending_tasks(128) { } @@ -476,9 +476,10 @@ void phy_test_mode_adapter::phy_cell::on_cell_results_completion(slot_point slot // ---- mac_test_mode_adapter::mac_test_mode_adapter(const srs_du::du_test_mode_config::test_mode_ue_config& test_ue_cfg_, - mac_result_notifier& phy_notifier_) : + mac_result_notifier& phy_notifier_, + unsigned nof_cells) : test_ue(test_ue_cfg_), - ue_info_mgr(test_ue.rnti, test_ue.nof_ues), + ue_info_mgr(test_ue.rnti, test_ue.nof_ues, nof_cells), phy_notifier(std::make_unique(phy_notifier_)) { } @@ -620,14 +621,15 @@ void mac_test_mode_adapter::handle_ue_config_applied(du_ue_index_t ue_idx) } std::unique_ptr srsran::srs_du::create_du_high_mac(const mac_config& mac_cfg, - const srs_du::du_test_mode_config& test_cfg) + const srs_du::du_test_mode_config& test_cfg, + unsigned nof_cells) { if (not test_cfg.test_ue.has_value()) { return create_mac(mac_cfg); } // Create a MAC test mode adapter that wraps the real MAC. - auto mac_testmode = std::make_unique(*test_cfg.test_ue, mac_cfg.phy_notifier); + auto mac_testmode = std::make_unique(*test_cfg.test_ue, mac_cfg.phy_notifier, nof_cells); mac_testmode->connect(create_mac(mac_config{mac_cfg.ul_ccch_notifier, mac_cfg.ue_exec_mapper, mac_cfg.cell_exec_mapper, diff --git a/lib/du/du_high/test_mode/mac_test_mode_adapter.h b/lib/du/du_high/test_mode/mac_test_mode_adapter.h index 049781ba9d..e8367ced1c 100644 --- a/lib/du/du_high/test_mode/mac_test_mode_adapter.h +++ b/lib/du/du_high/test_mode/mac_test_mode_adapter.h @@ -38,7 +38,7 @@ namespace srs_du { class test_ue_info_manager { public: - test_ue_info_manager(rnti_t rnti_start_, uint16_t nof_ues_); + test_ue_info_manager(rnti_t rnti_start_, uint16_t nof_ues_, uint16_t nof_cells_); du_ue_index_t rnti_to_du_ue_idx(rnti_t rnti) const { @@ -52,7 +52,7 @@ class test_ue_info_manager bool is_test_ue(rnti_t rnti) const { - return (rnti >= rnti_start) and (rnti < to_rnti(to_value(rnti_start) + nof_ues)); + return (rnti >= rnti_start) and (rnti < to_rnti(to_value(rnti_start) + nof_ues * nof_cells)); } void add_ue(rnti_t rnti, du_ue_index_t ue_idx_, const sched_ue_config_request& sched_ue_cfg_req_); @@ -97,6 +97,7 @@ class test_ue_info_manager // Parameters received from configuration. rnti_t rnti_start; uint16_t nof_ues; + uint16_t nof_cells; // Mapping between UE RNTI and test UE information. std::unordered_map rnti_to_ue_info_lookup; @@ -207,7 +208,8 @@ class mac_test_mode_adapter final : public mac_interface, { public: explicit mac_test_mode_adapter(const srs_du::du_test_mode_config::test_mode_ue_config& test_ue_cfg, - mac_result_notifier& phy_notifier_); + mac_result_notifier& phy_notifier_, + unsigned nof_cells); ~mac_test_mode_adapter() override; void connect(std::unique_ptr mac_ptr); diff --git a/lib/e1ap/common/e1ap_asn1_converters.h b/lib/e1ap/common/e1ap_asn1_converters.h index 2add78950c..aaa29f5dd7 100644 --- a/lib/e1ap/common/e1ap_asn1_converters.h +++ b/lib/e1ap/common/e1ap_asn1_converters.h @@ -158,8 +158,8 @@ e1ap_asn1_to_integrity_algorithm(const asn1::e1ap::integrity_protection_algorith inline asn1::e1ap::snssai_s snssai_to_e1ap_asn1(srsran::s_nssai_t snssai) { asn1::e1ap::snssai_s asn1_snssai; - asn1_snssai.sst.from_number(snssai.sst); - if (snssai.sd.has_value()) { + asn1_snssai.sst.from_number(snssai.sst.value()); + if (snssai.sd.is_set()) { asn1_snssai.sd_present = true; asn1_snssai.sd.from_number(snssai.sd.value()); } @@ -173,10 +173,10 @@ inline asn1::e1ap::snssai_s snssai_to_e1ap_asn1(srsran::s_nssai_t snssai) inline srsran::s_nssai_t e1ap_asn1_to_snssai(asn1::e1ap::snssai_s asn1_snssai) { srsran::s_nssai_t snssai; - snssai.sst = asn1_snssai.sst.to_number(); + snssai.sst = slice_service_type{(uint8_t)asn1_snssai.sst.to_number()}; if (asn1_snssai.sd_present) { - snssai.sd = asn1_snssai.sd.to_number(); + snssai.sd = slice_differentiator::create(asn1_snssai.sd.to_number()).value(); } return snssai; diff --git a/lib/e1ap/common/e1ap_asn1_helpers.h b/lib/e1ap/common/e1ap_asn1_helpers.h index 52311dc9ac..2063a4f35b 100644 --- a/lib/e1ap/common/e1ap_asn1_helpers.h +++ b/lib/e1ap/common/e1ap_asn1_helpers.h @@ -46,10 +46,10 @@ inline void fill_e1ap_cu_up_e1_setup_request(cu_up_e1_setup_request& for (const auto& asn1_slice_support_item : asn1_plmn_item.slice_support_list) { slice_support_item_t slice_support; - slice_support.s_nssai.sst = asn1_slice_support_item.snssai.sst.to_number(); + slice_support.s_nssai.sst = slice_service_type{(uint8_t)asn1_slice_support_item.snssai.sst.to_number()}; if (asn1_slice_support_item.snssai.sd_present) { - slice_support.s_nssai.sd = asn1_slice_support_item.snssai.sd.to_number(); + slice_support.s_nssai.sd = slice_differentiator::create(asn1_slice_support_item.snssai.sd.to_number()).value(); } plmn.slice_support_list.push_back(slice_support); diff --git a/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.cpp b/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.cpp index a08f21dc3c..ff864deca1 100644 --- a/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.cpp +++ b/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.cpp @@ -595,18 +595,45 @@ bool e2sm_kpm_du_meas_provider_impl::get_delay_ul(const asn1::e2sm::label_info_l { bool meas_collected = false; if (last_ue_metrics.empty()) { - return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::real); + return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::no_value); } - scheduler_ue_metrics ue_metrics = last_ue_metrics[0]; + if ((label_info_list.size() > 1 or (label_info_list.size() == 1 and not label_info_list[0].meas_label.no_label_present))) { logger.debug("Metric: DRB.AirIfDelayUl supports only NO_LABEL label."); return meas_collected; } - meas_record_item_c meas_record_item; - meas_record_item.set_real().value = ue_metrics.ul_delay_ms; - items.push_back(meas_record_item); - meas_collected = true; + + if (ues.empty()) { + double mean_ul_delay_ms = + std::accumulate(last_ue_metrics.begin(), + last_ue_metrics.end(), + 0, + [](size_t sum, const scheduler_ue_metrics& metric) { return sum + metric.ul_delay_ms; }) / + last_ue_metrics.size(); + meas_record_item_c meas_record_item; + if (mean_ul_delay_ms) { + meas_record_item.set_real().value = static_cast(mean_ul_delay_ms); + } else { + meas_record_item.set_no_value(); + } + items.push_back(meas_record_item); + meas_collected = true; + } + + for (auto& ue : ues) { + gnb_cu_ue_f1ap_id_t gnb_cu_ue_f1ap_id = int_to_gnb_cu_ue_f1ap_id(ue.gnb_du_ue_id().gnb_cu_ue_f1ap_id); + uint32_t ue_idx = f1ap_ue_id_provider.get_ue_index(gnb_cu_ue_f1ap_id); + meas_record_item_c meas_record_item; + if (last_ue_metrics[ue_idx].ul_delay_ms) { + meas_record_item.set_real().value = static_cast(last_ue_metrics[ue_idx].ul_delay_ms); + } else { + meas_record_item.set_no_value(); + } + items.push_back(meas_record_item); + meas_collected = true; + } + return meas_collected; } @@ -645,7 +672,7 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_dl_mean_throughput(const asn1::e2sm { bool meas_collected = false; if (ue_aggr_rlc_metrics.empty()) { - return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::integer); + return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::real); } if ((label_info_list.size() > 1 or (label_info_list.size() == 1 and not label_info_list[0].meas_label.no_label_present))) { @@ -691,7 +718,7 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_dl_mean_throughput(const asn1::e2sm for (auto& ue : ue_throughput) { total_throughput += ue.second; } - meas_record_item.set_integer() = total_throughput; + meas_record_item.set_real().value = total_throughput; items.push_back(meas_record_item); meas_collected = true; } @@ -706,7 +733,7 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_dl_mean_throughput(const asn1::e2sm meas_collected = true; continue; } - meas_record_item.set_integer() = ue_throughput[ue_idx]; + meas_record_item.set_real().value = ue_throughput[ue_idx]; items.push_back(meas_record_item); meas_collected = true; } @@ -720,7 +747,7 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_ul_mean_throughput(const asn1::e2sm { bool meas_collected = false; if (ue_aggr_rlc_metrics.empty()) { - return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::integer); + return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::real); } if ((label_info_list.size() > 1 or (label_info_list.size() == 1 and not label_info_list[0].meas_label.no_label_present))) { @@ -746,7 +773,7 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_ul_mean_throughput(const asn1::e2sm for (auto& ue : ue_throughput) { total_throughput += ue.second; } - meas_record_item.set_integer() = total_throughput; + meas_record_item.set_real().value = total_throughput; items.push_back(meas_record_item); meas_collected = true; } @@ -761,7 +788,7 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_ul_mean_throughput(const asn1::e2sm meas_collected = true; continue; } - meas_record_item.set_integer() = ue_throughput[ue_idx]; + meas_record_item.set_real().value = ue_throughput[ue_idx]; items.push_back(meas_record_item); meas_collected = true; } @@ -775,7 +802,7 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_ul_success_rate(const asn1::e2sm::l { bool meas_collected = false; if (ue_aggr_rlc_metrics.empty()) { - return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::integer); + return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::no_value); } if ((label_info_list.size() > 1 or (label_info_list.size() == 1 and not label_info_list[0].meas_label.no_label_present))) { @@ -852,7 +879,7 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_rlc_packet_drop_rate_dl( { bool meas_collected = false; if (ue_aggr_rlc_metrics.empty()) { - return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::integer); + return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::no_value); } if ((label_info_list.size() > 1 or @@ -1050,7 +1077,7 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_dl_rlc_sdu_latency(const asn1::e2sm { bool meas_collected = false; if (ue_aggr_rlc_metrics.empty()) { - return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::real); + return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::no_value); } if ((label_info_list.size() > 1 or @@ -1076,12 +1103,16 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_dl_rlc_sdu_latency(const asn1::e2sm } } if (av_ue_sdu_latency_us) { + float av_ue_sdu_latency_ms = (av_ue_sdu_latency_us / ue_aggr_rlc_metrics.size()) / 1e3; // Unit is 0.1 ms. + av_ue_sdu_latency_ms = std::round(av_ue_sdu_latency_ms * 10.0f) / 10.0f; meas_record_item.set_real(); - meas_record_item.real().value = av_ue_sdu_latency_us / ue_aggr_rlc_metrics.size(); + meas_record_item.real().value = av_ue_sdu_latency_ms; items.push_back(meas_record_item); meas_collected = true; } else { - logger.warning("Invalid RLC SDU latency value."); + meas_record_item.set_no_value(); + items.push_back(meas_record_item); + meas_collected = true; return meas_collected; } } else { @@ -1106,12 +1137,16 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_dl_rlc_sdu_latency(const asn1::e2sm 0, [](size_t sum, const rlc_metrics& metric) { return sum + metric.tx.tx_high.num_sdus; }); if (tot_sdu_latency_us) { + float av_ue_sdu_latency_ms = (tot_sdu_latency_us / tot_num_sdus) / 1e3; // Unit is 0.1 ms. + av_ue_sdu_latency_ms = std::round(av_ue_sdu_latency_ms * 10.0f) / 10.0f; meas_record_item.set_real(); - meas_record_item.real().value = tot_sdu_latency_us / tot_num_sdus; + meas_record_item.real().value = av_ue_sdu_latency_ms; items.push_back(meas_record_item); meas_collected = true; } else { - logger.warning("Invalid RLC SDU latency value."); + meas_record_item.set_no_value(); + items.push_back(meas_record_item); + meas_collected = true; } } } @@ -1125,7 +1160,7 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_ul_rlc_sdu_latency(const asn1::e2sm { bool meas_collected = false; if (ue_aggr_rlc_metrics.empty()) { - return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::real); + return handle_no_meas_data_available(ues, items, asn1::e2sm::meas_record_item_c::types::options::no_value); } if ((label_info_list.size() > 1 or @@ -1152,11 +1187,13 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_ul_rlc_sdu_latency(const asn1::e2sm } if (av_ue_sdu_latency_us) { meas_record_item.set_real(); - meas_record_item.real().value = (float)av_ue_sdu_latency_us / ue_aggr_rlc_metrics.size(); + meas_record_item.real().value = (av_ue_sdu_latency_us / ue_aggr_rlc_metrics.size()) / 1e3; // Unit is ms. items.push_back(meas_record_item); meas_collected = true; } else { - logger.warning("Invalid RLC SDU latency value."); + meas_record_item.set_no_value(); + items.push_back(meas_record_item); + meas_collected = true; return meas_collected; } } else { @@ -1182,11 +1219,13 @@ bool e2sm_kpm_du_meas_provider_impl::get_drb_ul_rlc_sdu_latency(const asn1::e2sm [](size_t sum, const rlc_metrics& metric) { return sum + metric.rx.num_sdus; }); if (tot_sdu_latency) { meas_record_item.set_real(); - meas_record_item.real().value = tot_sdu_latency / tot_num_sdus; + meas_record_item.real().value = (tot_sdu_latency / tot_num_sdus) / 1e3; // Unit is ms. items.push_back(meas_record_item); meas_collected = true; } else { - logger.warning("Invalid RLC SDU latency value."); + meas_record_item.set_no_value(); + items.push_back(meas_record_item); + meas_collected = true; } } } diff --git a/lib/e2/e2sm/e2sm_rc/e2sm_rc_control_action_du_executor.cpp b/lib/e2/e2sm/e2sm_rc/e2sm_rc_control_action_du_executor.cpp index 5041eb0fc0..eb1acfdee2 100644 --- a/lib/e2/e2sm/e2sm_rc/e2sm_rc_control_action_du_executor.cpp +++ b/lib/e2/e2sm/e2sm_rc/e2sm_rc_control_action_du_executor.cpp @@ -120,37 +120,45 @@ void e2sm_rc_control_action_2_6_du_executor::parse_action_ran_parameter_value( if (action_params[ran_param_id] == "PLMN Identity") { srs_du::control_config_params cur_control_params = {}; cur_control_params.rrm_policy_group.emplace(); - cur_control_params.rrm_policy_group.value().pol_member.plmn_id = - plmn_identity::parse(ran_param.ran_p_choice_elem_false().ran_param_value.value_oct_s().to_string()).value(); + if (ran_param.ran_p_choice_elem_false().ran_param_value.value_oct_s().size() != 3) { + logger.error("E2SM-RC Slice-level PRB quota Control Request: PLMN (param_id={}) encoded not correctly.", + ran_param_id); + return; + } + std::array plmn_bytes; + std::copy(ran_param.ran_p_choice_elem_false().ran_param_value.value_oct_s().begin(), + ran_param.ran_p_choice_elem_false().ran_param_value.value_oct_s().end(), + plmn_bytes.begin()); + cur_control_params.rrm_policy_group.value().pol_member.plmn_id.from_bytes(plmn_bytes); ctrl_cfg.param_list.push_back(cur_control_params); } else if (action_params[ran_param_id] == "SST") { if (ctrl_cfg.param_list.size()) { - if (ctrl_cfg.param_list.back().rrm_policy_group.value().pol_member.s_nssai.sst) { + if (ctrl_cfg.param_list.back().rrm_policy_group.value().pol_member.s_nssai.sst.value() != 0) { srs_du::control_config_params cur_control_params = {}; cur_control_params.rrm_policy_group.emplace(); cur_control_params = ctrl_cfg.param_list.back(); cur_control_params.rrm_policy_group.value().pol_member.s_nssai.sst = - ran_param.ran_p_choice_elem_false().ran_param_value.value_oct_s().to_number(); + slice_service_type{(uint8_t)ran_param.ran_p_choice_elem_false().ran_param_value.value_oct_s().to_number()}; ctrl_cfg.param_list.push_back(cur_control_params); } else { ctrl_cfg.param_list.back().rrm_policy_group.value().pol_member.s_nssai.sst = - ran_param.ran_p_choice_elem_false().ran_param_value.value_oct_s().to_number(); + slice_service_type{(uint8_t)ran_param.ran_p_choice_elem_false().ran_param_value.value_oct_s().to_number()}; } } } else if (action_params[ran_param_id] == "SD") { if (ctrl_cfg.param_list.size()) { - if (ctrl_cfg.param_list.back().rrm_policy_group.value().pol_member.s_nssai.sd.has_value()) { + if (ctrl_cfg.param_list.back().rrm_policy_group.value().pol_member.s_nssai.sd.is_set()) { srs_du::control_config_params cur_control_params = {}; cur_control_params.rrm_policy_group.emplace(); cur_control_params = ctrl_cfg.param_list.back(); - cur_control_params.rrm_policy_group.value().pol_member.s_nssai.sd.emplace(); cur_control_params.rrm_policy_group.value().pol_member.s_nssai.sd = - ran_param.ran_p_choice_elem_false().ran_param_value.value_oct_s().to_number(); + slice_differentiator::create(ran_param.ran_p_choice_elem_false().ran_param_value.value_oct_s().to_number()) + .value(); ctrl_cfg.param_list.push_back(cur_control_params); } else { - ctrl_cfg.param_list.back().rrm_policy_group.value().pol_member.s_nssai.sd.emplace(); ctrl_cfg.param_list.back().rrm_policy_group.value().pol_member.s_nssai.sd = - ran_param.ran_p_choice_elem_false().ran_param_value.value_oct_s().to_number(); + slice_differentiator::create(ran_param.ran_p_choice_elem_false().ran_param_value.value_oct_s().to_number()) + .value(); } } } else if (action_params[ran_param_id] == "Min PRB Policy Ratio") { @@ -269,7 +277,7 @@ e2sm_ric_control_response e2sm_rc_control_action_2_6_du_executor::convert_to_e2s srs_du::control_config_params req = du_config_req_.param_list[0]; if (req.rrm_policy_group.has_value()) { e2sm_rc_ctrl_outcome_format1_item_s min_prb_outcome; - min_prb_outcome.ran_param_id = 10; + min_prb_outcome.ran_param_id = 11; if (req.rrm_policy_group.value().min_prb_policy_ratio.has_value()) { min_prb_outcome.ran_param_value.set_value_int() = req.rrm_policy_group.value().min_prb_policy_ratio.value(); ctrl_outcome.ran_p_list.push_back(min_prb_outcome); @@ -278,7 +286,7 @@ e2sm_ric_control_response e2sm_rc_control_action_2_6_du_executor::convert_to_e2s if (req.rrm_policy_group.has_value()) { e2sm_rc_ctrl_outcome_format1_item_s max_prb_outcome; - max_prb_outcome.ran_param_id = 11; + max_prb_outcome.ran_param_id = 12; if (req.rrm_policy_group.value().max_prb_policy_ratio.has_value()) { max_prb_outcome.ran_param_value.set_value_int() = req.rrm_policy_group.value().max_prb_policy_ratio.value(); ctrl_outcome.ran_p_list.push_back(max_prb_outcome); diff --git a/lib/f1ap/asn1_helpers.cpp b/lib/f1ap/asn1_helpers.cpp index e28e7322e6..58115f5125 100644 --- a/lib/f1ap/asn1_helpers.cpp +++ b/lib/f1ap/asn1_helpers.cpp @@ -226,9 +226,9 @@ static f1ap_drb_info drb_info_from_f1ap_asn1(const asn1::f1ap::qos_info_c& asn1_ non_dyn_5qi_descriptor& nondyn_5qi = out.drb_qos.qos_desc.get_nondyn_5qi(); nondyn_5qi.five_qi = uint_to_five_qi(asn1_non_dyn_5qi.five_qi); out.drb_qos.alloc_retention_prio.prio_level_arp = asn1_drb_info.drb_qos.ngra_nalloc_retention_prio.prio_level; - out.s_nssai.sst = asn1_drb_info.snssai.sst.to_number(); + out.s_nssai.sst = slice_service_type{(uint8_t)asn1_drb_info.snssai.sst.to_number()}; if (asn1_drb_info.snssai.sd_present) { - out.s_nssai.sd = asn1_drb_info.snssai.sd.to_number(); + out.s_nssai.sd = slice_differentiator::create(asn1_drb_info.snssai.sd.to_number()).value(); } // TODO: Do not populate gbr_flow_info for non-GBR flows. if (asn1_drb_info.drb_qos.gbr_qos_flow_info_present) { @@ -364,8 +364,8 @@ static qos_info_c qos_info_to_f1ap_asn1(const f1ap_drb_info& drb_info) } // s nssai - asn1_drb_info.snssai.sst.from_number(drb_info.s_nssai.sst); - if (drb_info.s_nssai.sd.has_value()) { + asn1_drb_info.snssai.sst.from_number(drb_info.s_nssai.sst.value()); + if (drb_info.s_nssai.sd.is_set()) { asn1_drb_info.snssai.sd_present = true; asn1_drb_info.snssai.sd.from_number(drb_info.s_nssai.sd.value()); } diff --git a/lib/f1ap/cu_cp/f1ap_cu_impl.cpp b/lib/f1ap/cu_cp/f1ap_cu_impl.cpp index e5dc168e7b..c949e1f0c5 100644 --- a/lib/f1ap/cu_cp/f1ap_cu_impl.cpp +++ b/lib/f1ap/cu_cp/f1ap_cu_impl.cpp @@ -120,7 +120,10 @@ bool f1ap_cu_impl::handle_ue_id_update(ue_index_t ue_index, ue_index_t old_ue_in } // Mark that an old gNB-DU UE F1AP ID needs to be sent to the DU in the next DL RRC Message Transfer. - ue_ctxt_list[ue_index].pending_old_ue_id = ue_ctxt_list[old_ue_index].ue_ids.du_ue_f1ap_id; + srsran_assert(ue_ctxt_list[old_ue_index].ue_ids.du_ue_f1ap_id && + ue_ctxt_list[old_ue_index].ue_ids.du_ue_f1ap_id != gnb_du_ue_f1ap_id_t::invalid, + "GNB-DU-UE-F1AP-ID should be valid"); + ue_ctxt_list[ue_index].pending_old_ue_id = *ue_ctxt_list[old_ue_index].ue_ids.du_ue_f1ap_id; return true; } diff --git a/lib/f1ap/cu_cp/procedures/ue_context_modification_procedure.cpp b/lib/f1ap/cu_cp/procedures/ue_context_modification_procedure.cpp index e7f192c292..fc6aad9847 100644 --- a/lib/f1ap/cu_cp/procedures/ue_context_modification_procedure.cpp +++ b/lib/f1ap/cu_cp/procedures/ue_context_modification_procedure.cpp @@ -89,7 +89,9 @@ void ue_context_modification_procedure::send_ue_context_modification_request() fill_asn1_ue_context_modification_request(ctx_mod, request); - ctx_mod->gnb_du_ue_f1ap_id = gnb_du_ue_f1ap_id_to_uint(ue_ctxt.ue_ids.du_ue_f1ap_id); + srsran_sanity_check(ue_ctxt.ue_ids.du_ue_f1ap_id && ue_ctxt.ue_ids.du_ue_f1ap_id != gnb_du_ue_f1ap_id_t::invalid, + "Invalid gNB-DU-UE-F1AP-Id"); + ctx_mod->gnb_du_ue_f1ap_id = gnb_du_ue_f1ap_id_to_uint(*ue_ctxt.ue_ids.du_ue_f1ap_id); ctx_mod->gnb_cu_ue_f1ap_id = gnb_cu_ue_f1ap_id_to_uint(ue_ctxt.ue_ids.cu_ue_f1ap_id); // send UE context modification request message diff --git a/lib/f1ap/cu_cp/procedures/ue_context_release_procedure.cpp b/lib/f1ap/cu_cp/procedures/ue_context_release_procedure.cpp index 203cb66267..b7a0c8f24f 100644 --- a/lib/f1ap/cu_cp/procedures/ue_context_release_procedure.cpp +++ b/lib/f1ap/cu_cp/procedures/ue_context_release_procedure.cpp @@ -37,7 +37,7 @@ ue_context_release_procedure::ue_context_release_procedure(const f1ap_configurat f1ap_cfg(f1ap_cfg_), ue_ctxt(ue_ctxt_), f1ap_notifier(f1ap_notif_), logger(srslog::fetch_basic_logger("CU-CP-F1")) { command->gnb_cu_ue_f1ap_id = gnb_cu_ue_f1ap_id_to_uint(ue_ctxt.ue_ids.cu_ue_f1ap_id); - command->gnb_du_ue_f1ap_id = gnb_du_ue_f1ap_id_to_uint(ue_ctxt.ue_ids.du_ue_f1ap_id); + command->gnb_du_ue_f1ap_id = gnb_du_ue_f1ap_id_to_uint(*ue_ctxt.ue_ids.du_ue_f1ap_id); command->cause = cause_to_asn1(cmd_.cause); if (!cmd_.rrc_release_pdu.empty()) { command->rrc_container_present = true; @@ -86,7 +86,7 @@ ue_index_t ue_context_release_procedure::create_ue_context_release_complete() { if (transaction_sink.successful()) { gnb_du_ue_f1ap_id_t du_ue_id = int_to_gnb_du_ue_f1ap_id(transaction_sink.response()->gnb_du_ue_f1ap_id); - if (du_ue_id != ue_ctxt.ue_ids.du_ue_f1ap_id) { + if (!ue_ctxt.ue_ids.du_ue_f1ap_id || du_ue_id != *ue_ctxt.ue_ids.du_ue_f1ap_id) { logger.error("{}: Procedure failed. Cause: gNB-DU-UE-F1AP-ID mismatch.", f1ap_ue_log_prefix{ue_ctxt.ue_ids, name()}); return ue_index_t::invalid; diff --git a/lib/f1ap/cu_cp/procedures/ue_context_setup_procedure.cpp b/lib/f1ap/cu_cp/procedures/ue_context_setup_procedure.cpp index 3d8efed595..a5a7c31cd7 100644 --- a/lib/f1ap/cu_cp/procedures/ue_context_setup_procedure.cpp +++ b/lib/f1ap/cu_cp/procedures/ue_context_setup_procedure.cpp @@ -224,9 +224,10 @@ static void fill_asn1_ue_context_setup_request(asn1::f1ap::ue_context_setup_requ { asn1_request->gnb_cu_ue_f1ap_id = gnb_cu_ue_f1ap_id_to_uint(ue_ids.cu_ue_f1ap_id); - asn1_request->gnb_du_ue_f1ap_id_present = ue_ids.du_ue_f1ap_id != gnb_du_ue_f1ap_id_t::invalid; + asn1_request->gnb_du_ue_f1ap_id_present = + ue_ids.du_ue_f1ap_id && ue_ids.du_ue_f1ap_id != gnb_du_ue_f1ap_id_t::invalid; if (asn1_request->gnb_du_ue_f1ap_id_present) { - asn1_request->gnb_du_ue_f1ap_id = gnb_du_ue_f1ap_id_to_uint(ue_ids.du_ue_f1ap_id); + asn1_request->gnb_du_ue_f1ap_id = gnb_du_ue_f1ap_id_to_uint(*ue_ids.du_ue_f1ap_id); } asn1_request->sp_cell_id = cgi_to_asn1(request.sp_cell_id); diff --git a/lib/f1ap/cu_cp/ue_context/f1ap_cu_ue_context.cpp b/lib/f1ap/cu_cp/ue_context/f1ap_cu_ue_context.cpp index eeb4b16c15..6cbb245459 100644 --- a/lib/f1ap/cu_cp/ue_context/f1ap_cu_ue_context.cpp +++ b/lib/f1ap/cu_cp/ue_context/f1ap_cu_ue_context.cpp @@ -29,11 +29,12 @@ using namespace srs_cu_cp; void f1ap_ue_context::handle_dl_rrc_message(const f1ap_dl_rrc_message& msg, f1ap_message_notifier& msg_notifier) { - srsran_sanity_check(ue_ids.du_ue_f1ap_id != gnb_du_ue_f1ap_id_t::invalid, "Invalid gNB-DU-UE-F1AP-Id"); + srsran_sanity_check(ue_ids.du_ue_f1ap_id && ue_ids.du_ue_f1ap_id != gnb_du_ue_f1ap_id_t::invalid, + "Invalid gNB-DU-UE-F1AP-Id"); asn1::f1ap::dl_rrc_msg_transfer_s dl_rrc_msg = {}; dl_rrc_msg->gnb_cu_ue_f1ap_id = gnb_cu_ue_f1ap_id_to_uint(ue_ids.cu_ue_f1ap_id); - dl_rrc_msg->gnb_du_ue_f1ap_id = gnb_du_ue_f1ap_id_to_uint(ue_ids.du_ue_f1ap_id); + dl_rrc_msg->gnb_du_ue_f1ap_id = gnb_du_ue_f1ap_id_to_uint(*ue_ids.du_ue_f1ap_id); dl_rrc_msg->srb_id = static_cast(srb_id_to_uint(msg.srb_id)); dl_rrc_msg->rrc_container = msg.rrc_container.copy(); diff --git a/lib/f1ap/cu_cp/ue_context/f1ap_cu_ue_context.h b/lib/f1ap/cu_cp/ue_context/f1ap_cu_ue_context.h index 862fac823c..b2921930c2 100644 --- a/lib/f1ap/cu_cp/ue_context/f1ap_cu_ue_context.h +++ b/lib/f1ap/cu_cp/ue_context/f1ap_cu_ue_context.h @@ -98,7 +98,7 @@ class f1ap_ue_context_list { auto it = std::find_if( ues.begin(), ues.end(), [du_ue_id](const std::pair& u) { - return u.second.ue_ids.du_ue_f1ap_id == du_ue_id; + return u.second.ue_ids.du_ue_f1ap_id && u.second.ue_ids.du_ue_f1ap_id == du_ue_id; }); return it != ues.end() ? &it->second : nullptr; } diff --git a/lib/f1ap/cu_cp/ue_context/f1ap_ue_ids.h b/lib/f1ap/cu_cp/ue_context/f1ap_ue_ids.h index aeca00aa83..b82ce447f0 100644 --- a/lib/f1ap/cu_cp/ue_context/f1ap_ue_ids.h +++ b/lib/f1ap/cu_cp/ue_context/f1ap_ue_ids.h @@ -30,9 +30,9 @@ namespace srs_cu_cp { /// Identifiers that associate a UE in the F1AP-CU. struct f1ap_ue_ids { - const ue_index_t ue_index = ue_index_t::invalid; - const gnb_cu_ue_f1ap_id_t cu_ue_f1ap_id = gnb_cu_ue_f1ap_id_t::invalid; - gnb_du_ue_f1ap_id_t du_ue_f1ap_id = gnb_du_ue_f1ap_id_t::invalid; + ue_index_t ue_index = ue_index_t::invalid; + gnb_cu_ue_f1ap_id_t cu_ue_f1ap_id = gnb_cu_ue_f1ap_id_t::invalid; + std::optional du_ue_f1ap_id; }; } // namespace srs_cu_cp diff --git a/lib/f1ap/cu_cp/ue_context/f1ap_ue_logger.h b/lib/f1ap/cu_cp/ue_context/f1ap_ue_logger.h index 4ecf1032da..4fe1b36752 100644 --- a/lib/f1ap/cu_cp/ue_context/f1ap_ue_logger.h +++ b/lib/f1ap/cu_cp/ue_context/f1ap_ue_logger.h @@ -32,12 +32,16 @@ namespace srs_cu_cp { struct f1ap_ue_log_prefix : public srsran::f1ap_common_log_prefix { using srsran::f1ap_common_log_prefix::f1ap_common_log_prefix; - f1ap_ue_log_prefix(ue_index_t ue_index_, gnb_cu_ue_f1ap_id_t cu_ue_id_, gnb_du_ue_f1ap_id_t du_ue_id_) : - srsran::f1ap_common_log_prefix(du_ue_id_, cu_ue_id_), ue_index(ue_index_) + f1ap_ue_log_prefix(ue_index_t ue_index_, + gnb_cu_ue_f1ap_id_t cu_ue_id_, + std::optional du_ue_id_) : + srsran::f1ap_common_log_prefix(du_ue_id_.value_or(gnb_du_ue_f1ap_id_t::invalid), cu_ue_id_), ue_index(ue_index_) { } f1ap_ue_log_prefix(const f1ap_ue_ids& context_, const char* proc_name_ = nullptr) : - srsran::f1ap_common_log_prefix(context_.du_ue_f1ap_id, context_.cu_ue_f1ap_id, proc_name_), + srsran::f1ap_common_log_prefix(context_.du_ue_f1ap_id.value_or(gnb_du_ue_f1ap_id_t::invalid), + context_.cu_ue_f1ap_id, + proc_name_), ue_index(context_.ue_index) { } diff --git a/lib/f1ap/du/procedures/f1ap_du_setup_procedure.cpp b/lib/f1ap/du/procedures/f1ap_du_setup_procedure.cpp index 8793d38174..3c6c8f6ef9 100644 --- a/lib/f1ap/du/procedures/f1ap_du_setup_procedure.cpp +++ b/lib/f1ap/du/procedures/f1ap_du_setup_procedure.cpp @@ -118,10 +118,10 @@ void f1ap_du_setup_procedure::send_f1_setup_request() f1ap_cell.served_cell_info.served_plmns[0].ie_exts.tai_slice_support_list_present = not cell_cfg.slices.empty(); for (const s_nssai_t& s_nssai : cell_cfg.slices) { slice_support_item_s slice{}; - slice.snssai.sst.from_number(s_nssai.sst); - slice.snssai.sd_present = s_nssai.sd.has_value(); + slice.snssai.sst.from_number(s_nssai.sst.value()); + slice.snssai.sd_present = s_nssai.sd.is_set(); if (slice.snssai.sd_present) { - slice.snssai.sd.from_number(*s_nssai.sd); + slice.snssai.sd.from_number(s_nssai.sd.value()); } f1ap_cell.served_cell_info.served_plmns[0].ie_exts.tai_slice_support_list.push_back(slice); } diff --git a/lib/f1u/CMakeLists.txt b/lib/f1u/CMakeLists.txt index f0a204c914..95135677a8 100644 --- a/lib/f1u/CMakeLists.txt +++ b/lib/f1u/CMakeLists.txt @@ -22,12 +22,12 @@ add_library(srsran_f1u_cu_up cu_up/f1u_bearer_impl.cpp cu_up/f1u_bearer_factory. add_library(srsran_f1u_du du/f1u_bearer_impl.cpp du/f1u_bearer_factory.cpp) # Local connector -add_library(srsgnb_app_f1u_connector local_connector/f1u_local_connector.cpp) -target_link_libraries(srsgnb_app_f1u_connector srsran_f1u_cu_up) +add_library(srsran_f1u_connector local_connector/f1u_local_connector.cpp) +target_link_libraries(srsran_f1u_connector srsran_f1u_cu_up) #Split connector -add_library(srsgnb_app_f1u_cu_up_split_connector cu_up/split_connector/f1u_split_connector_factory.cpp cu_up/split_connector/f1u_split_connector.cpp ) -target_link_libraries(srsgnb_app_f1u_cu_up_split_connector srsran_f1u_cu_up) +add_library(srsran_f1u_cu_up_split_connector cu_up/split_connector/f1u_split_connector_factory.cpp cu_up/split_connector/f1u_split_connector.cpp ) +target_link_libraries(srsran_f1u_cu_up_split_connector srsran_f1u_cu_up) -add_library(srsgnb_app_f1u_du_split_connector du/split_connector/f1u_split_connector.cpp du/split_connector/f1u_split_connector_factory.cpp) -target_link_libraries(srsgnb_app_f1u_du_split_connector srsran_f1u_du) +add_library(srsran_f1u_du_split_connector du/split_connector/f1u_split_connector.cpp du/split_connector/f1u_split_connector_factory.cpp) +target_link_libraries(srsran_f1u_du_split_connector srsran_f1u_du) diff --git a/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp b/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp index 1b86020720..f587bb8284 100644 --- a/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp +++ b/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp @@ -469,9 +469,10 @@ static expected translate_ul_tti_pdus_to_phy_pdus(const fapi::ul_tt case fapi::ul_pdu_type::SRS: { uplink_processor::srs_pdu& ul_pdu = pdus.srs.emplace_back(); convert_srs_fapi_to_phy(ul_pdu, pdu.srs_pdu, msg.sfn, msg.slot); - if (!ul_pdu_validator.is_valid(ul_pdu.config)) { - logger.warning("Upper PHY flagged a SRS PDU as having an invalid configuration. Skipping UL_TTI.request"); - + error_type srs_validation = ul_pdu_validator.is_valid(ul_pdu.config); + if (!srs_validation.has_value()) { + logger.warning("Skipping UL_TTI.request: SRS PDU flagged as invalid with the following error\n {}", + srs_validation.error()); return make_unexpected(default_error_t{}); } break; diff --git a/lib/gtpu/gtpu_tunnel_ngu_rx_impl.h b/lib/gtpu/gtpu_tunnel_ngu_rx_impl.h index 315bffb954..994d6cf4dd 100644 --- a/lib/gtpu/gtpu_tunnel_ngu_rx_impl.h +++ b/lib/gtpu/gtpu_tunnel_ngu_rx_impl.h @@ -22,12 +22,12 @@ #pragma once -#include "../support/sdu_window_impl.h" #include "gtpu_tunnel_base_rx.h" #include "srsran/gtpu/gtpu_config.h" #include "srsran/gtpu/gtpu_tunnel_ngu_rx.h" #include "srsran/psup/psup_packing.h" #include "srsran/ran/cu_types.h" +#include "srsran/support/sdu_window.h" #include "srsran/support/timers.h" namespace srsran { @@ -65,7 +65,7 @@ class gtpu_tunnel_ngu_rx_impl : public gtpu_tunnel_base_rx psup_packer(logger.get_basic_logger()), lower_dn(rx_lower_), config(cfg), - rx_window(std::make_unique>(logger)), + rx_window(logger, gtpu_rx_window_size), ue_dl_timer_factory(ue_dl_timer_factory_) { if (config.t_reordering.count() != 0) { @@ -172,12 +172,12 @@ class gtpu_tunnel_ngu_rx_impl : public gtpu_tunnel_base_rx } // Check if PDU has been received - if (rx_window->has_sn(sn)) { + if (rx_window.has_sn(sn)) { logger.log_warning("Duplicate PDU dropped. sn={} pdu_len={}", sn, pdu_len); return; } - gtpu_rx_sdu_info& rx_sdu_info = rx_window->add_sn(sn); + gtpu_rx_sdu_info& rx_sdu_info = rx_window.add_sn(sn); rx_sdu_info.sdu = std::move(rx_sdu); rx_sdu_info.qos_flow_id = pdu_session_info.qos_flow_id; rx_sdu_info.sn = sn; @@ -221,10 +221,10 @@ class gtpu_tunnel_ngu_rx_impl : public gtpu_tunnel_base_rx void deliver_all_consecutive_sdus() { - while (st.rx_deliv != st.rx_next && rx_window->has_sn(st.rx_deliv)) { - gtpu_rx_sdu_info& sdu_info = (*rx_window)[st.rx_deliv]; + while (st.rx_deliv != st.rx_next && rx_window.has_sn(st.rx_deliv)) { + gtpu_rx_sdu_info& sdu_info = rx_window[st.rx_deliv]; deliver_sdu(sdu_info); - rx_window->remove_sn(st.rx_deliv); + rx_window.remove_sn(st.rx_deliv); // Update RX_DELIV st.rx_deliv = st.rx_deliv + 1; @@ -244,10 +244,10 @@ class gtpu_tunnel_ngu_rx_impl : public gtpu_tunnel_base_rx } while (st.rx_deliv != st.rx_reord) { - if (rx_window->has_sn(st.rx_deliv)) { - gtpu_rx_sdu_info& sdu_info = (*rx_window)[st.rx_deliv]; + if (rx_window.has_sn(st.rx_deliv)) { + gtpu_rx_sdu_info& sdu_info = rx_window[st.rx_deliv]; deliver_sdu(sdu_info); - rx_window->remove_sn(st.rx_deliv); + rx_window.remove_sn(st.rx_deliv); } // Update RX_DELIV @@ -279,7 +279,7 @@ class gtpu_tunnel_ngu_rx_impl : public gtpu_tunnel_base_rx gtpu_rx_state st = {}; /// Rx window - std::unique_ptr> rx_window; + sdu_window rx_window; /// Rx reordering timer unique_timer reordering_timer; diff --git a/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp b/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp index c79e4ea053..3635750130 100644 --- a/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp +++ b/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp @@ -323,7 +323,7 @@ shared_transport_block dl_sch_pdu_assembler::assemble_newtx_pdu(rnti_t auto shared_buffer = harq_buffers.allocate_dl_harq_buffer(ue_idx, h_id); if (not shared_buffer or shared_buffer->get_buffer().size() < tb_size_bytes) { - logger.error( + logger.warning( "DL ue={} rnti={} h_id={}: Failed to assemble MAC PDU. Cause: No HARQ buffers available", ue_idx, rnti, h_id); return make_shared_zero_buffer(tb_size_bytes); } @@ -472,10 +472,10 @@ dl_sch_pdu_assembler::assemble_retx_pdu(rnti_t rnti, harq_id_t h_id, unsigned tb auto shared_buffer = harq_buffers.allocate_dl_harq_buffer(ue_idx, h_id); if (not shared_buffer or shared_buffer->get_buffer().size() < tbs_bytes) { - logger.error("DL ue={} rnti={} h_id={}: Failed to assemble MAC PDU. Cause: No HARQ buffers available", - ue_mng.get_ue_index(rnti), - rnti, - h_id); + logger.warning("DL ue={} rnti={} h_id={}: Failed to assemble MAC PDU. Cause: No HARQ buffers available", + ue_mng.get_ue_index(rnti), + rnti, + h_id); return make_shared_zero_buffer(tbs_bytes); } diff --git a/lib/mac/mac_dl/mac_cell_processor.cpp b/lib/mac/mac_dl/mac_cell_processor.cpp index fe3265c0f3..fbb77d9c75 100644 --- a/lib/mac/mac_dl/mac_cell_processor.cpp +++ b/lib/mac/mac_dl/mac_cell_processor.cpp @@ -25,6 +25,7 @@ #include "srsran/mac/mac_cell_result.h" #include "srsran/ran/pdsch/pdsch_constants.h" #include "srsran/support/async/execute_on_blocking.h" +#include "srsran/support/rtsan.h" using namespace srsran; @@ -220,7 +221,7 @@ async_task mac_cell_processor::remove_bearers(du_ue_index_t ue_index, span }); } -void mac_cell_processor::handle_slot_indication_impl(slot_point sl_tx) +void mac_cell_processor::handle_slot_indication_impl(slot_point sl_tx) SRSRAN_RTSAN_NONBLOCKING { // * Start of Critical Path * // diff --git a/lib/ngap/ngap_asn1_converters.h b/lib/ngap/ngap_asn1_converters.h index c6c49bb2ad..4e703ae8eb 100644 --- a/lib/ngap/ngap_asn1_converters.h +++ b/lib/ngap/ngap_asn1_converters.h @@ -492,9 +492,9 @@ inline void asn1_to_handov_type(ngap_handov_type& handov_type, const asn1::ngap: inline s_nssai_t ngap_asn1_to_s_nssai(const asn1::ngap::s_nssai_s& asn1_s_nssai) { s_nssai_t s_nssai; - s_nssai.sst = asn1_s_nssai.sst.to_number(); + s_nssai.sst = slice_service_type{(uint8_t)asn1_s_nssai.sst.to_number()}; if (asn1_s_nssai.sd_present) { - s_nssai.sd = asn1_s_nssai.sd.to_number(); + s_nssai.sd = slice_differentiator::create(asn1_s_nssai.sd.to_number()).value(); } return s_nssai; @@ -503,9 +503,9 @@ inline s_nssai_t ngap_asn1_to_s_nssai(const asn1::ngap::s_nssai_s& asn1_s_nssai) inline asn1::ngap::s_nssai_s s_nssai_to_asn1(const s_nssai_t& s_nssai) { asn1::ngap::s_nssai_s asn1_s_nssai; - asn1_s_nssai.sst.from_number(s_nssai.sst); + asn1_s_nssai.sst.from_number(s_nssai.sst.value()); - if (s_nssai.sd.has_value()) { + if (s_nssai.sd.is_set()) { asn1_s_nssai.sd_present = true; asn1_s_nssai.sd.from_number(s_nssai.sd.value()); } @@ -526,8 +526,8 @@ inline bool asn1_to_security_context(security::security_context& sec_c const asn1::ngap::ue_security_cap_s& asn1_sec_cap, const asn1::ngap::security_context_s& asn1_sec_ctxt) { - // TODO: Handle next_hop_chaining_count copy_asn1_key(sec_ctxt.k, asn1_sec_ctxt.next_hop_nh); + sec_ctxt.ncc = asn1_sec_ctxt.next_hop_chaining_count; fill_supported_algorithms(sec_ctxt.supported_int_algos, asn1_sec_cap.nr_integrity_protection_algorithms); fill_supported_algorithms(sec_ctxt.supported_enc_algos, asn1_sec_cap.nr_encryption_algorithms); srslog::fetch_basic_logger("NGAP").debug(asn1_sec_ctxt.next_hop_nh.data(), 32, "K_gnb"); diff --git a/lib/ngap/ngap_asn1_helpers.h b/lib/ngap/ngap_asn1_helpers.h index 61e080a5d5..7bc1d4d392 100644 --- a/lib/ngap/ngap_asn1_helpers.h +++ b/lib/ngap/ngap_asn1_helpers.h @@ -123,9 +123,10 @@ inline void fill_ngap_ng_setup_result(ngap_ng_setup_result& result, const asn1:: for (const auto& asn1_slice_support_item : asn1_plmn_support_item.slice_support_list) { slice_support_item_t slice_support_item = {}; - slice_support_item.s_nssai.sst = asn1_slice_support_item.s_nssai.sst.to_number(); + slice_support_item.s_nssai.sst = slice_service_type{(uint8_t)asn1_slice_support_item.s_nssai.sst.to_number()}; if (asn1_slice_support_item.s_nssai.sd_present) { - slice_support_item.s_nssai.sd = asn1_slice_support_item.s_nssai.sd.to_number(); + slice_support_item.s_nssai.sd = + slice_differentiator::create(asn1_slice_support_item.s_nssai.sd.to_number()).value(); } plmn_support_item.slice_support_list.push_back(slice_support_item); } @@ -256,9 +257,9 @@ inline bool fill_cu_cp_pdu_session_resource_setup_item_base(cu_cp_pdu_session_re // s-NSSAI if (asn1_session_item.s_nssai.sd_present) { - setup_item.s_nssai.sd = asn1_session_item.s_nssai.sd.to_number(); + setup_item.s_nssai.sd = slice_differentiator::create(asn1_session_item.s_nssai.sd.to_number()).value(); } - setup_item.s_nssai.sst = asn1_session_item.s_nssai.sst.to_number(); + setup_item.s_nssai.sst = slice_service_type{(uint8_t)asn1_session_item.s_nssai.sst.to_number()}; // pDUSessionResourceSetupRequestTransfer asn1::ngap::pdu_session_res_setup_request_transfer_s asn1_setup_req_transfer; diff --git a/lib/ngap/ngap_impl.cpp b/lib/ngap/ngap_impl.cpp index 49f7d606bb..e79f5dcc7f 100644 --- a/lib/ngap/ngap_impl.cpp +++ b/lib/ngap/ngap_impl.cpp @@ -59,10 +59,10 @@ ngap_impl::ngap_impl(const ngap_configuration& ngap_cfg_, ev_mng(timer_factory{timers, ctrl_exec}), conn_handler(n2_gateway, *this, cu_cp_notifier, ctrl_exec) { - context.gnb_id = ngap_cfg_.gnb_id; - context.ran_node_name = ngap_cfg_.ran_node_name; - context.supported_tas = ngap_cfg_.supported_tas; - context.pdu_session_setup_timeout = ngap_cfg_.pdu_session_setup_timeout; + context.gnb_id = ngap_cfg_.gnb_id; + context.ran_node_name = ngap_cfg_.ran_node_name; + context.supported_tas = ngap_cfg_.supported_tas; + context.request_pdu_session_timeout = ngap_cfg_.request_pdu_session_timeout; } // Note: For fwd declaration of member types, dtor cannot be trivial. @@ -194,13 +194,13 @@ void ngap_impl::handle_initial_ue_message(const cu_cp_initial_ue_message& msg) fill_asn1_initial_ue_message(init_ue_msg, msg, context); // Start PDU session setup timer - ue_ctxt.pdu_session_setup_timer.set(context.pdu_session_setup_timeout, [this, msg](timer_id_t /*tid*/) { - on_pdu_session_setup_timer_expired(msg.ue_index); + ue_ctxt.request_pdu_session_timer.set(context.request_pdu_session_timeout, [this, msg](timer_id_t /*tid*/) { + on_request_pdu_session_timer_expired(msg.ue_index); }); - ue_ctxt.pdu_session_setup_timer.run(); + ue_ctxt.request_pdu_session_timer.run(); ue_ctxt.logger.log_debug("Starting PDU session creation timer (timeout={}ms)...", - ue_ctxt.pdu_session_setup_timer.duration().count()); + ue_ctxt.request_pdu_session_timer.duration().count()); // Forward message to AMF tx_pdu_notifier->on_new_message(ngap_msg); @@ -436,7 +436,7 @@ void ngap_impl::handle_initial_context_setup_request(const asn1::ngap::init_cont // If InitialContextSetupRequest contains PDU Session Setup list, stop pdu session setup timer if (request->pdu_session_res_setup_list_cxt_req_present) { - ue_ctxt.pdu_session_setup_timer.stop(); + ue_ctxt.request_pdu_session_timer.stop(); } // Update AMF ID and use the one from this Context Setup as per TS 38.413 v16.2 page 38 @@ -502,7 +502,7 @@ void ngap_impl::handle_pdu_session_resource_setup_request(const asn1::ngap::pdu_ ue_ctxt.ue_ids.amf_ue_id); // Stop PDU session setup timer - ue_ctxt.pdu_session_setup_timer.stop(); + ue_ctxt.request_pdu_session_timer.stop(); if (!ue->is_security_enabled()) { ue_ctxt.logger.log_warning("Dropping PduSessionResourceSetupRequest. Security context does not exist"); @@ -930,7 +930,7 @@ async_task ngap_impl::handle_ue_context_release_request(const cu_cp_ue_con ngap_ue_context& ue_ctxt = ue_ctxt_list[msg.ue_index]; // Stop PDU session setup timer - ue_ctxt.pdu_session_setup_timer.stop(); + ue_ctxt.request_pdu_session_timer.stop(); if (ue_ctxt.ue_ids.amf_ue_id == amf_ue_id_t::invalid) { ue_ctxt.logger.log_debug("Ignoring UeContextReleaseRequest. UE does not have an AMF UE ID"); @@ -1065,7 +1065,7 @@ void ngap_impl::schedule_error_indication(ue_index_t ue_index, ngap_cause_t caus })); } -void ngap_impl::on_pdu_session_setup_timer_expired(ue_index_t ue_index) +void ngap_impl::on_request_pdu_session_timer_expired(ue_index_t ue_index) { if (ue_ctxt_list.contains(ue_index)) { ngap_ue_context& ue_ctxt = ue_ctxt_list[ue_index]; @@ -1079,8 +1079,8 @@ void ngap_impl::on_pdu_session_setup_timer_expired(ue_index_t ue_index) if (ue_ctxt.ue_ids.amf_ue_id == amf_ue_id_t::invalid) { // AMF never responded to InitialUEMessage, so we only remove the UE from the DU - ue_ctxt.logger.log_warning("PDU session setup timer expired after {}ms. Releasing UE from DU", - ue_ctxt.pdu_session_setup_timer.duration().count()); + ue_ctxt.logger.log_info("UE did not request a PDU session after {}ms. Releasing UE from DU", + ue_ctxt.request_pdu_session_timer.duration().count()); ue->schedule_async_task(launch_async([this, ue_index](coro_context>& ctx) { CORO_BEGIN(ctx); @@ -1089,8 +1089,8 @@ void ngap_impl::on_pdu_session_setup_timer_expired(ue_index_t ue_index) CORO_RETURN(); })); } else { - ue_ctxt.logger.log_warning("PDU session setup timer expired after {}ms. Requesting UE release", - ue_ctxt.pdu_session_setup_timer.duration().count()); + ue_ctxt.logger.log_info("UE did not request a PDU session after {}ms. Requesting UE release", + ue_ctxt.request_pdu_session_timer.duration().count()); // Request UE release ue->schedule_async_task(launch_async([this, ue_index](coro_context>& ctx) { diff --git a/lib/ngap/ngap_impl.h b/lib/ngap/ngap_impl.h index 9c7c6a5092..1945b646c5 100644 --- a/lib/ngap/ngap_impl.h +++ b/lib/ngap/ngap_impl.h @@ -171,8 +171,8 @@ class ngap_impl final : public ngap_interface /// \param[in] amf_ue_id The AMF UE ID. void schedule_error_indication(ue_index_t ue_index, ngap_cause_t cause, std::optional amf_ue_id = {}); - /// \brief Callback for the PDU Session Setup Timer expiration. Triggers the release of the UE. - void on_pdu_session_setup_timer_expired(ue_index_t ue_index); + /// \brief Callback for the PDU Session Request Timer expiration. Triggers the release of the UE. + void on_request_pdu_session_timer_expired(ue_index_t ue_index); /// \brief Log NGAP RX PDU. void log_rx_pdu(const ngap_message& msg); diff --git a/lib/ngap/ue_context/ngap_ue_context.h b/lib/ngap/ue_context/ngap_ue_context.h index 090b74233b..b5a0deedc7 100644 --- a/lib/ngap/ue_context/ngap_ue_context.h +++ b/lib/ngap/ue_context/ngap_ue_context.h @@ -42,7 +42,7 @@ struct ngap_ue_context { ngap_cu_cp_ue_notifier* ue = nullptr; guami_t serving_guami; uint64_t aggregate_maximum_bit_rate_dl = 0; - unique_timer pdu_session_setup_timer = {}; + unique_timer request_pdu_session_timer = {}; bool release_requested = false; bool release_scheduled = false; byte_buffer last_pdu_session_resource_modify_request; // To check if a received modify request is a duplicate @@ -55,7 +55,7 @@ struct ngap_ue_context { task_executor& task_exec_) : ue_ids({ue_index_, ran_ue_id_}), ue(&ue_notifier_), logger("NGAP", {ue_index_, ran_ue_id_}) { - pdu_session_setup_timer = timers_.create_unique_timer(task_exec_); + request_pdu_session_timer = timers_.create_unique_timer(task_exec_); } [[nodiscard]] ngap_cu_cp_ue_notifier* get_cu_cp_ue() const { return ue; } diff --git a/lib/pdcp/pdcp_entity_rx.cpp b/lib/pdcp/pdcp_entity_rx.cpp index 99966719d2..65cd74cd71 100644 --- a/lib/pdcp/pdcp_entity_rx.cpp +++ b/lib/pdcp/pdcp_entity_rx.cpp @@ -22,7 +22,6 @@ #include "pdcp_entity_rx.h" #include "../security/security_engine_impl.h" -#include "../support/sdu_window_impl.h" #include "srsran/instrumentation/traces/up_traces.h" #include "srsran/support/bit_encoding.h" #include "srsran/support/format/fmt_optional.h" @@ -41,7 +40,7 @@ pdcp_entity_rx::pdcp_entity_rx(uint32_t ue_index, pdcp_entity_tx_rx_base(rb_id_, cfg_.rb_type, cfg_.rlc_mode, cfg_.sn_size), logger("PDCP", {ue_index, rb_id_, "UL"}), cfg(cfg_), - rx_window(create_rx_window(cfg.sn_size)), + rx_window(logger, pdcp_window_size(pdcp_sn_size_to_uint(cfg.sn_size))), upper_dn(upper_dn_), upper_cn(upper_cn_), ue_ul_timer_factory(ue_ul_timer_factory_), @@ -102,7 +101,7 @@ void pdcp_entity_rx::handle_pdu(byte_buffer_chain buf) // Log PDU logger.log_debug(buf.begin(), buf.end(), "RX PDU. pdu_len={}", buf.length()); // Sanity check - if (buf.length() == 0) { + if (buf.empty()) { metrics.add_dropped_pdus(1); logger.log_error("Dropping empty PDU."); return; @@ -263,19 +262,18 @@ void pdcp_entity_rx::handle_data_pdu(byte_buffer pdu) } // Check if PDU has been received - if (rx_window->has_sn(rcvd_count)) { - const pdcp_rx_sdu_info& sdu_info = (*rx_window)[rcvd_count]; + if (rx_window.has_sn(rcvd_count)) { + const pdcp_rx_sdu_info& sdu_info = rx_window[rcvd_count]; if (sdu_info.count == rcvd_count) { logger.log_debug("Duplicate PDU dropped. count={}", rcvd_count); return; // PDU already present, drop. - } else { - logger.log_error("Removing old PDU with count={} for new PDU with count={}", sdu_info.count, rcvd_count); - rx_window->remove_sn(rcvd_count); } + logger.log_error("Removing old PDU with count={} for new PDU with count={}", sdu_info.count, rcvd_count); + rx_window.remove_sn(rcvd_count); } // Store PDU in Rx window - pdcp_rx_sdu_info& sdu_info = rx_window->add_sn(rcvd_count); + pdcp_rx_sdu_info& sdu_info = rx_window.add_sn(rcvd_count); sdu_info.sdu = std::move(pdu); sdu_info.count = rcvd_count; sdu_info.time_of_arrival = time_start; @@ -343,15 +341,15 @@ void pdcp_entity_rx::handle_control_pdu(byte_buffer_chain pdu) // Update RX_DELIV after submitting to higher layers void pdcp_entity_rx::deliver_all_consecutive_counts() { - while (st.rx_deliv != st.rx_next && rx_window->has_sn(st.rx_deliv)) { - pdcp_rx_sdu_info& sdu_info = (*rx_window)[st.rx_deliv]; + while (st.rx_deliv != st.rx_next && rx_window.has_sn(st.rx_deliv)) { + pdcp_rx_sdu_info& sdu_info = rx_window[st.rx_deliv]; logger.log_info("RX SDU. count={}", st.rx_deliv); // Pass PDCP SDU to the upper layers metrics.add_sdus(1, sdu_info.sdu.length()); record_reordering_dealy(sdu_info.time_of_arrival); upper_dn.on_new_sdu(std::move(sdu_info.sdu)); - rx_window->remove_sn(st.rx_deliv); + rx_window.remove_sn(st.rx_deliv); // Update RX_DELIV st.rx_deliv = st.rx_deliv + 1; @@ -364,15 +362,15 @@ void pdcp_entity_rx::deliver_all_consecutive_counts() void pdcp_entity_rx::deliver_all_sdus() { for (uint32_t count = st.rx_deliv; count < st.rx_next; count++) { - if (rx_window->has_sn(count)) { - pdcp_rx_sdu_info& sdu_info = (*rx_window)[count]; + if (rx_window.has_sn(count)) { + pdcp_rx_sdu_info& sdu_info = rx_window[count]; logger.log_info("RX SDU. count={}", count); // Pass PDCP SDU to the upper layers metrics.add_sdus(1, sdu_info.sdu.length()); record_reordering_dealy(sdu_info.time_of_arrival); upper_dn.on_new_sdu(std::move(sdu_info.sdu)); - rx_window->remove_sn(count); + rx_window.remove_sn(count); } } } @@ -381,8 +379,8 @@ void pdcp_entity_rx::deliver_all_sdus() void pdcp_entity_rx::discard_all_sdus() { while (st.rx_deliv != st.rx_next) { - if (rx_window->has_sn(st.rx_deliv)) { - rx_window->remove_sn(st.rx_deliv); + if (rx_window.has_sn(st.rx_deliv)) { + rx_window.remove_sn(st.rx_deliv); logger.log_debug("Discarded RX SDU. count={}", st.rx_next); } @@ -416,33 +414,13 @@ byte_buffer pdcp_entity_rx::compile_status_report() for (uint32_t i = bitmap_begin; i < bitmap_end; i++) { // Bit == 0: PDCP SDU with COUNT = (FMC + bit position) modulo 2^32 is missing. // Bit == 1: PDCP SDU with COUNT = (FMC + bit position) modulo 2^32 is correctly received. - unsigned bit = rx_window->has_sn(i) ? 1 : 0; + unsigned bit = rx_window.has_sn(i) ? 1 : 0; enc.pack(bit, 1); } return buf; } -std::unique_ptr> pdcp_entity_rx::create_rx_window(pdcp_sn_size sn_size_) -{ - std::unique_ptr> rx_window_; - switch (sn_size_) { - case pdcp_sn_size::size12bits: - rx_window_ = std::make_unique>(logger); - break; - case pdcp_sn_size::size18bits: - rx_window_ = std::make_unique>(logger); - break; - default: - srsran_assertion_failure("Cannot create rx_window for unsupported sn_size={}.", pdcp_sn_size_to_uint(sn_size_)); - } - return rx_window_; -} - /* * Deciphering and Integrity Protection Helpers */ @@ -566,15 +544,15 @@ void pdcp_entity_rx::handle_t_reordering_expire() metrics.add_t_reordering_timeouts(1); // Deliver all PDCP SDU(s) with associated COUNT value(s) < RX_REORD while (st.rx_deliv != st.rx_reord) { - if (rx_window->has_sn(st.rx_deliv)) { - pdcp_rx_sdu_info& sdu_info = (*rx_window)[st.rx_deliv]; + if (rx_window.has_sn(st.rx_deliv)) { + pdcp_rx_sdu_info& sdu_info = rx_window[st.rx_deliv]; logger.log_info("RX SDU. count={}", st.rx_deliv); // Pass PDCP SDU to the upper layers metrics.add_sdus(1, sdu_info.sdu.length()); record_reordering_dealy(sdu_info.time_of_arrival); upper_dn.on_new_sdu(std::move(sdu_info.sdu)); - rx_window->remove_sn(st.rx_deliv); + rx_window.remove_sn(st.rx_deliv); } // Update RX_DELIV @@ -646,4 +624,4 @@ void pdcp_entity_rx::record_reordering_dealy(std::chrono::system_clock::time_poi std::chrono::microseconds time_taken = std::chrono::duration_cast(std::chrono::system_clock::now() - time_of_arrival); metrics.add_reordering_delay_us((uint32_t)time_taken.count()); -} \ No newline at end of file +} diff --git a/lib/pdcp/pdcp_entity_rx.h b/lib/pdcp/pdcp_entity_rx.h index 8e699665c1..9b07093eeb 100644 --- a/lib/pdcp/pdcp_entity_rx.h +++ b/lib/pdcp/pdcp_entity_rx.h @@ -53,8 +53,8 @@ struct pdcp_rx_state { }; struct pdcp_rx_sdu_info { - byte_buffer sdu = {}; - uint32_t count = {}; + byte_buffer sdu; + uint32_t count = 0; std::chrono::system_clock::time_point time_of_arrival; }; @@ -133,7 +133,7 @@ class pdcp_entity_rx final : public pdcp_entity_tx_rx_base, pdcp_rx_state st = {}; /// Rx window - std::unique_ptr> rx_window; + sdu_window rx_window; /// Rx reordering timer unique_timer reordering_timer; @@ -175,11 +175,6 @@ class pdcp_entity_rx final : public pdcp_entity_tx_rx_base, pdcp_metrics_aggregator& metrics_agg; unique_timer metrics_timer; - /// Creates the rx_window according to sn_size - /// \param sn_size Size of the sequence number (SN) - /// \return unique pointer to rx_window instance - std::unique_ptr> create_rx_window(pdcp_sn_size sn_size_); - void log_state(srslog::basic_levels level) { logger.log(level, "RX entity state. {}", st); } }; diff --git a/lib/pdcp/pdcp_entity_tx.cpp b/lib/pdcp/pdcp_entity_tx.cpp index 1de1e3d464..7ede604c19 100644 --- a/lib/pdcp/pdcp_entity_tx.cpp +++ b/lib/pdcp/pdcp_entity_tx.cpp @@ -22,7 +22,6 @@ #include "pdcp_entity_tx.h" #include "../security/security_engine_impl.h" -#include "../support/sdu_window_impl.h" #include "srsran/instrumentation/traces/up_traces.h" #include "srsran/support/bit_encoding.h" #include "srsran/support/srsran_assert.h" diff --git a/lib/pdcp/pdcp_tx_window.cpp b/lib/pdcp/pdcp_tx_window.cpp index 86672ad7b7..a109b746cb 100644 --- a/lib/pdcp/pdcp_tx_window.cpp +++ b/lib/pdcp/pdcp_tx_window.cpp @@ -20,12 +20,10 @@ * */ -#include - -#include "../support/sdu_window_impl.h" #include "pdcp_tx_window.h" #include "srsran/pdcp/pdcp_config.h" #include "srsran/support/srsran_assert.h" +#include using namespace srsran; @@ -33,41 +31,27 @@ pdcp_tx_window::pdcp_tx_window(pdcp_rb_type rb_type_, pdcp_rlc_mode rlc_mode_, pdcp_sn_size sn_size_, pdcp_bearer_logger logger_) : - rb_type(rb_type_), rlc_mode(rlc_mode_), sn_size(sn_size_), logger(std::move(logger_)) + logger(std::move(logger_)), + tx_window(logger, pdcp_window_size(pdcp_sn_size_to_uint(sn_size_))), + rb_type(rb_type_), + rlc_mode(rlc_mode_), + sn_size(sn_size_) { - create_tx_window(); -} -void pdcp_tx_window::create_tx_window() -{ - switch (sn_size) { - case pdcp_sn_size::size12bits: - tx_window = std::make_unique>(logger); - break; - case pdcp_sn_size::size18bits: - tx_window = std::make_unique>(logger); - break; - default: - srsran_assertion_failure("Cannot create tx_window for unsupported sn_size={}.", pdcp_sn_size_to_uint(sn_size)); - } } -bool pdcp_tx_window::has_sn(uint32_t count) +bool pdcp_tx_window::has_sn(uint32_t count) const { - return tx_window->has_sn(count); + return tx_window.has_sn(count); } pdcp_tx_sdu_info& pdcp_tx_window::operator[](uint32_t count) { - return (*tx_window)[count]; + return tx_window[count]; } void pdcp_tx_window::add_sdu(uint32_t count, byte_buffer sdu, unique_timer discard_timer) { - pdcp_tx_sdu_info& sdu_info = tx_window->add_sn(count); + pdcp_tx_sdu_info& sdu_info = tx_window.add_sn(count); sdu_info.count = count; sdu_info.discard_timer = std::move(discard_timer); sdu_info.sdu_length = sdu.length(); @@ -81,10 +65,10 @@ void pdcp_tx_window::add_sdu(uint32_t count, byte_buffer sdu, unique_timer disca void pdcp_tx_window::remove_sdu(uint32_t count) { - if (tx_window->has_sn(count)) { - sdu_bytes -= (*tx_window)[count].sdu_length; + if (tx_window.has_sn(count)) { + sdu_bytes -= tx_window[count].sdu_length; nof_sdus--; - tx_window->remove_sn(count); + tx_window.remove_sn(count); } } @@ -92,7 +76,7 @@ void pdcp_tx_window::clear() { sdu_bytes = 0; nof_sdus = 0; - tx_window->clear(); + tx_window.clear(); } uint32_t pdcp_tx_window::get_sdu_bytes() const diff --git a/lib/pdcp/pdcp_tx_window.h b/lib/pdcp/pdcp_tx_window.h index 992b4ea8c2..96775c8c5f 100644 --- a/lib/pdcp/pdcp_tx_window.h +++ b/lib/pdcp/pdcp_tx_window.h @@ -46,7 +46,7 @@ class pdcp_tx_window public: pdcp_tx_window(pdcp_rb_type rb_type_, pdcp_rlc_mode rlc_mode_, pdcp_sn_size sn_size_, pdcp_bearer_logger logger_); - bool has_sn(uint32_t count); + bool has_sn(uint32_t count) const; /// \brief Remove SDU from TX window /// This method removes an SDU from the TX window. It will keep track of the PDU bytes in the window @@ -69,21 +69,22 @@ class pdcp_tx_window uint32_t get_pdu_bytes(security::integrity_enabled integrity) const; private: + pdcp_bearer_logger logger; + /// \brief Tx window. /// This container is used to store discard timers of transmitted SDUs and, only for AM, a copy of the SDU for data /// recovery procedure. Upon expiry of a discard timer, the PDCP Tx entity instructs the lower layers to discard the /// associated PDCP PDU. See section 5.2.1 and 7.3 of TS 38.323. - std::unique_ptr> tx_window; + sdu_window tx_window; /// Creates the tx_window according to sn_size /// \param sn_size Size of the sequence number (SN) /// \return unique pointer to tx_window instance void create_tx_window(); - pdcp_rb_type rb_type; - pdcp_rlc_mode rlc_mode; - pdcp_sn_size sn_size; - pdcp_bearer_logger logger; + pdcp_rb_type rb_type; + pdcp_rlc_mode rlc_mode; + pdcp_sn_size sn_size; uint32_t sdu_bytes = 0; uint32_t nof_sdus = 0; diff --git a/lib/phy/generic_functions/dft_processor_fftw_impl.h b/lib/phy/generic_functions/dft_processor_fftw_impl.h index 89d66879e5..4db7a3928b 100644 --- a/lib/phy/generic_functions/dft_processor_fftw_impl.h +++ b/lib/phy/generic_functions/dft_processor_fftw_impl.h @@ -23,7 +23,6 @@ #pragma once #include "srsran/phy/generic_functions/dft_processor.h" -#include "srsran/srsvec/aligned_vec.h" #include #include #include @@ -101,9 +100,9 @@ class dft_processor_fftw_impl : public dft_processor /// Stores the DFT direction. direction dir; /// DFT input buffer ownership. - srsvec::aligned_vec input; + std::vector input; /// DFT output buffer ownership. - srsvec::aligned_vec output; + std::vector output; /// FFTW actual plan. fftwf_plan plan; diff --git a/lib/phy/generic_functions/dft_processor_generic_impl.h b/lib/phy/generic_functions/dft_processor_generic_impl.h index 2c9433b801..8262a0c23a 100644 --- a/lib/phy/generic_functions/dft_processor_generic_impl.h +++ b/lib/phy/generic_functions/dft_processor_generic_impl.h @@ -23,7 +23,6 @@ #pragma once #include "srsran/phy/generic_functions/dft_processor.h" -#include "srsran/srsvec/aligned_vec.h" namespace srsran { @@ -43,9 +42,9 @@ class dft_processor_generic_impl : public dft_processor /// Stores the DFT direction. direction dir; /// DFT input buffer ownership. - srsvec::aligned_vec input; + std::vector input; /// DFT output buffer ownership. - srsvec::aligned_vec output; + std::vector output; /// Generic FFT. std::unique_ptr generic_dft; diff --git a/lib/phy/lower/modulation/ofdm_demodulator_impl.h b/lib/phy/lower/modulation/ofdm_demodulator_impl.h index 0753d6f730..4586da69c8 100644 --- a/lib/phy/lower/modulation/ofdm_demodulator_impl.h +++ b/lib/phy/lower/modulation/ofdm_demodulator_impl.h @@ -25,7 +25,6 @@ #include "phase_compensation_lut.h" #include "srsran/phy/generic_functions/dft_processor.h" #include "srsran/phy/lower/modulation/ofdm_demodulator.h" -#include "srsran/srsvec/aligned_vec.h" #include namespace srsran { @@ -59,9 +58,9 @@ class ofdm_symbol_demodulator_impl : public ofdm_symbol_demodulator /// Phase compensation table. phase_compensation_lut phase_compensation_table; /// Internal buffer aimed at storing the phase compensated DFT outputs. - srsvec::aligned_vec compensated_output; + std::vector compensated_output; /// DFT window offset phase compensation. - srsvec::aligned_vec window_phase_compensation; + std::vector window_phase_compensation; public: /// \brief Constructs an OFDM symbol demodulator. diff --git a/lib/phy/lower/modulation/ofdm_modulator_impl.h b/lib/phy/lower/modulation/ofdm_modulator_impl.h index bbbca3b1da..d4dbab69bf 100644 --- a/lib/phy/lower/modulation/ofdm_modulator_impl.h +++ b/lib/phy/lower/modulation/ofdm_modulator_impl.h @@ -26,7 +26,6 @@ #include "srsran/phy/generic_functions/dft_processor.h" #include "srsran/phy/lower/modulation/ofdm_modulator.h" #include "srsran/ran/cyclic_prefix.h" -#include "srsran/srsvec/aligned_vec.h" namespace srsran { diff --git a/lib/phy/lower/processors/uplink/prach/prach_processor_worker.h b/lib/phy/lower/processors/uplink/prach/prach_processor_worker.h index a2c2f3bafa..9b384db22e 100644 --- a/lib/phy/lower/processors/uplink/prach/prach_processor_worker.h +++ b/lib/phy/lower/processors/uplink/prach/prach_processor_worker.h @@ -33,7 +33,6 @@ #include "srsran/ran/phy_time_unit.h" #include "srsran/ran/prach/prach_constants.h" #include "srsran/srslog/srslog.h" -#include "srsran/srsvec/aligned_vec.h" #include "srsran/support/executors/task_executor.h" namespace srsran { diff --git a/lib/phy/upper/channel_coding/channel_coding_factories.cpp b/lib/phy/upper/channel_coding/channel_coding_factories.cpp index 3664e6ef8c..6f11757be0 100644 --- a/lib/phy/upper/channel_coding/channel_coding_factories.cpp +++ b/lib/phy/upper/channel_coding/channel_coding_factories.cpp @@ -27,7 +27,8 @@ #include "ldpc/ldpc_encoder_generic.h" #include "ldpc/ldpc_rate_dematcher_impl.h" #include "ldpc/ldpc_rate_matcher_impl.h" -#include "ldpc/ldpc_segmenter_impl.h" +#include "ldpc/ldpc_segmenter_rx_impl.h" +#include "ldpc/ldpc_segmenter_tx_impl.h" #include "polar/polar_allocator_impl.h" #include "polar/polar_code_impl.h" #include "polar/polar_deallocator_impl.h" @@ -223,19 +224,19 @@ class ldpc_segmenter_tx_factory_sw : public ldpc_segmenter_tx_factory std::unique_ptr create() override { - ldpc_segmenter_impl::sch_crc sch_crc = { + ldpc_segmenter_tx_impl::sch_crc sch_crc = { crc_factory->create(crc_generator_poly::CRC16), crc_factory->create(crc_generator_poly::CRC24A), crc_factory->create(crc_generator_poly::CRC24B), }; - return ldpc_segmenter_impl::create_ldpc_segmenter_impl_tx(sch_crc); + return std::make_unique(sch_crc); } }; class ldpc_segmenter_rx_factory_sw : public ldpc_segmenter_rx_factory { public: - std::unique_ptr create() override { return ldpc_segmenter_impl::create_ldpc_segmenter_impl_rx(); } + std::unique_ptr create() override { return std::make_unique(); } }; class polar_factory_sw : public polar_factory diff --git a/lib/phy/upper/channel_coding/ldpc/CMakeLists.txt b/lib/phy/upper/channel_coding/ldpc/CMakeLists.txt index 8448cd5153..d20ff49643 100644 --- a/lib/phy/upper/channel_coding/ldpc/CMakeLists.txt +++ b/lib/phy/upper/channel_coding/ldpc/CMakeLists.txt @@ -27,7 +27,8 @@ set(ldpc_sources ldpc_decoder_generic.cpp ldpc_rate_matcher_impl.cpp ldpc_rate_dematcher_impl.cpp - ldpc_segmenter_impl.cpp + ldpc_segmenter_tx_impl.cpp + ldpc_segmenter_rx_impl.cpp ) if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") diff --git a/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_helpers.h b/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_helpers.h new file mode 100644 index 0000000000..c75c40bb4b --- /dev/null +++ b/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_helpers.h @@ -0,0 +1,120 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/// \file +/// \brief Helper functions for LDPC segmenter implementations. + +#pragma once + +#include "srsran/phy/upper/channel_coding/ldpc/ldpc.h" +#include "srsran/phy/upper/codeblock_metadata.h" + +namespace srsran { + +struct segment_parameters { + /// \name Attributes relative to TS38.212 Section 5.2.2. + ///@{ + /// Final length of a segment (corresponds to \f$K\f$). + units::bits segment_length{0}; + /// Number of bits in the transport block (corresponds to \f$B\f$). + units::bits nof_tb_bits_in{0}; + /// Augmented number of bits in the transport block, including new CRCs (corresponds to \f$B'\f$). + units::bits nof_tb_bits_out{0}; + /// Number of segments resulting from the transport block (corresponds to \f$C\f$). + unsigned nof_segments = 0; + ///@} + /// \name Attributes relative to TS38.212 Section 5.4.2.1. + ///@{ + /// Number of symbols per transmission layer (corresponds to \f$G / (N_L Q_m)\f$). + unsigned nof_symbols_per_layer = 0; + /// \brief Number of segments of short rate-matched length (corresponds to \f$C - \bigr(\bigl(G / (N_L Q_m)\bigr) + /// \bmod C\bigr)\f$). + unsigned nof_short_segments = 0; + ///@} + /// Base graph used for encoding/decoding the current transport block. + ldpc_base_graph_type base_graph = ldpc_base_graph_type::BG1; + /// Lifting size used for encoding/decoding the current transport block. + unsigned lifting_size = 0; + /// Total codeword length. + units::bits cw_length; + /// Number of filler bits. + units::bits nof_filler_bits; + /// Number of segment-specific CRC bits. + units::bits nof_crc_bits; + // Number of information bits that is assigned to a segment. + units::bits cb_info_bits; + // Number of information bits that is assigned to the last segment. + units::bits cb_info_bits_last; + // Number of zero-pad bits. + units::bits zero_pad; + /// Number of TB-specific CRC bits. + units::bits nof_tb_crc_bits; + /// Array of codeblock starting offset within the codeword. + std::array cw_offset; + /// Array of segment starting offset within the transport block. + std::array tb_offset; + /// Array of codeblock metadata structure. + std::array cb_metadata; +}; + +/// Computes the length of the rate-matched codeblock corresponding to each segment, as per TS38.212 +/// Section 5.4.2.1. +inline unsigned +compute_rm_length(const segment_parameters& seg_param, modulation_scheme mod, unsigned nof_layers, unsigned i_seg) +{ + unsigned tmp = 0; + if (i_seg < seg_param.nof_short_segments) { + // For unsigned, division then floor is the same as integer division. + tmp = seg_param.nof_symbols_per_layer / seg_param.nof_segments; + } else { + tmp = divide_ceil(seg_param.nof_symbols_per_layer, seg_param.nof_segments); + } + return tmp * nof_layers * get_bits_per_symbol(mod); +} + +/// Generates a codeblock metadata structure for the current segment configuration. +inline codeblock_metadata +generate_cb_metadata(const segment_parameters& seg_param, const segmenter_config& cfg, unsigned i_seg) +{ + codeblock_metadata tmp_description; + + tmp_description.tb_common.base_graph = seg_param.base_graph; + tmp_description.tb_common.lifting_size = static_cast(seg_param.lifting_size); + tmp_description.tb_common.rv = cfg.rv; + tmp_description.tb_common.mod = cfg.mod; + tmp_description.tb_common.Nref = cfg.Nref; + tmp_description.tb_common.cw_length = seg_param.cw_length.value(); + + unsigned rm_length = compute_rm_length(seg_param, cfg.mod, cfg.nof_layers, i_seg); + + tmp_description.cb_specific.full_length = + ldpc::compute_full_codeblock_size(seg_param.base_graph, seg_param.segment_length).value(); + tmp_description.cb_specific.nof_filler_bits = seg_param.nof_filler_bits.value(); + tmp_description.cb_specific.rm_length = rm_length; + tmp_description.cb_specific.cw_offset = seg_param.cw_offset[i_seg]; + tmp_description.cb_specific.nof_crc_bits = + (seg_param.nof_segments == 1) ? seg_param.nof_tb_crc_bits.value() : seg_param.nof_crc_bits.value(); + + return tmp_description; +} + +} // namespace srsran diff --git a/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_impl.cpp b/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_impl.cpp deleted file mode 100644 index 8baa213b12..0000000000 --- a/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_impl.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "ldpc_segmenter_impl.h" -#include "srsran/phy/upper/channel_coding/ldpc/ldpc.h" -#include "srsran/phy/upper/codeblock_metadata.h" -#include "srsran/srsvec/bit.h" -#include "srsran/srsvec/copy.h" -#include "srsran/support/math/math_utils.h" -#include "srsran/support/srsran_assert.h" - -using namespace srsran; -using namespace srsran::ldpc; - -/// Length of the CRC checksum added to the segments. -static constexpr units::bits SEG_CRC_LENGTH{24}; -/// Maximum accepted transport block size. -/// Note: This value has to be multiple of 8. -static constexpr units::bits MAX_TBS{1277992}; -static_assert(MAX_TBS.is_byte_exact(), "Value is not a multiple of 8"); - -std::unique_ptr ldpc_segmenter_impl::create_ldpc_segmenter_impl_tx(ldpc_segmenter_impl::sch_crc& c) -{ - srsran_assert(c.crc16, "Invalid CRC16 calculator."); - srsran_assert(c.crc24A, "Invalid CRC24A calculator."); - srsran_assert(c.crc24B, "Invalid CRC24B calculator."); - srsran_assert(c.crc16->get_generator_poly() == crc_generator_poly::CRC16, "Not a CRC generator of type CRC16."); - srsran_assert(c.crc24A->get_generator_poly() == crc_generator_poly::CRC24A, "Not a CRC generator of type CRC24A."); - srsran_assert(c.crc24B->get_generator_poly() == crc_generator_poly::CRC24B, "Not a CRC generator of type CRC24B."); - - return std::unique_ptr(new ldpc_segmenter_impl(std::move(c))); -} - -std::unique_ptr ldpc_segmenter_impl::create_ldpc_segmenter_impl_rx() -{ - return std::unique_ptr(new ldpc_segmenter_impl()); -} - -unsigned ldpc_segmenter_impl::compute_rm_length(unsigned i_seg, modulation_scheme mod, unsigned nof_layers) const -{ - unsigned tmp = 0; - if (i_seg < nof_short_segments) { - // For unsigned, division then floor is the same as integer division. - tmp = nof_symbols_per_layer / nof_segments; - } else { - tmp = divide_ceil(nof_symbols_per_layer, nof_segments); - } - return tmp * nof_layers * get_bits_per_symbol(mod); -} - -static void check_inputs_tx(const static_vector& segments, - span transport_block, - const segmenter_config& cfg) -{ - using namespace units::literals; - srsran_assert(segments.empty(), "Argument segments should be empty."); - srsran_assert(!transport_block.empty(), "Argument transport_block should not be empty."); - srsran_assert(units::bytes(transport_block.size()).to_bits() + 24_bits <= MAX_TBS, - "Transport block too long. The admissible size, including CRC, is {}.", - MAX_TBS.truncate_to_bytes()); - - srsran_assert((cfg.rv >= 0) && (cfg.rv <= 3), "Invalid redundancy version."); - - srsran_assert((cfg.nof_layers >= 1) && (cfg.nof_layers <= 4), "Invalid number of layers."); - - srsran_assert(cfg.nof_ch_symbols % (cfg.nof_layers) == 0, - "The number of channel symbols should be a multiple of the product between the number of layers."); -} - -// For the Tx-chain segmenter. -void ldpc_segmenter_impl::segment(static_vector& described_segments, - span transport_block, - const segmenter_config& cfg) -{ - check_inputs_tx(described_segments, transport_block, cfg); - - using namespace units::literals; - - base_graph = cfg.base_graph; - // Each transport_block entry is a byte, and TBS can always be expressed as an integer number of bytes (see, e.g., - // TS38.214 Section 5.1.3.2). - units::bits nof_tb_bits_tmp = units::bytes(transport_block.size()).to_bits(); - - constexpr units::bits MAX_BITS_CRC16{3824}; - crc_calculator& tb_crc = (nof_tb_bits_tmp <= MAX_BITS_CRC16) ? *crc_set.crc16 : *crc_set.crc24A; - units::bits nof_tb_crc_bits = compute_tb_crc_size(nof_tb_bits_tmp); - - nof_tb_bits_in = nof_tb_bits_tmp + nof_tb_crc_bits; - - nof_segments = ldpc::compute_nof_codeblocks(nof_tb_bits_tmp, base_graph); - nof_tb_bits_out = nof_tb_bits_in; - if (nof_segments > 1) { - nof_tb_bits_out += units::bits(nof_segments * SEG_CRC_LENGTH.value()); - } - lifting_size = compute_lifting_size(nof_tb_bits_tmp, base_graph, nof_segments); - segment_length = compute_codeblock_size(base_graph, lifting_size); - - units::bits nof_crc_bits = (nof_segments > 1) ? SEG_CRC_LENGTH : 0_bits; - - // Compute the number of information bits that is assigned to a segment. - units::bits cb_info_bits = units::bits(divide_ceil(nof_tb_bits_out.value(), nof_segments)) - nof_crc_bits; - - // Zero-padding if necessary. - units::bits zero_pad = units::bits((cb_info_bits + nof_crc_bits).value() * nof_segments) - nof_tb_bits_out; - - // Append TB CRC. - crc_calculator_checksum_t tb_checksum = tb_crc.calculate_byte(transport_block); - - // Number of channel symbols assigned to a transmission layer. - nof_symbols_per_layer = cfg.nof_ch_symbols / cfg.nof_layers; - // Number of segments that will have a short rate-matched length. In TS38.212 Section 5.4.2.1, these correspond to - // codeblocks whose length E_r is computed by rounding down - floor. For the remaining codewords, the length is - // rounded up. - nof_short_segments = nof_segments - (nof_symbols_per_layer % nof_segments); - - // Codeword length (after concatenation of codeblocks). - units::bits cw_length(cfg.nof_ch_symbols * get_bits_per_symbol(cfg.mod)); - - // Number of filler bits in this segment. - units::bits nof_filler_bits = segment_length - cb_info_bits - nof_crc_bits; - - unsigned cw_offset = 0; - unsigned tb_offset = 0; - for (unsigned i_segment = 0; i_segment != nof_segments; ++i_segment) { - bool last_cb = (i_segment == nof_segments - 1); - - // Generate metadata for this segment. - codeblock_metadata cb_metadata = - generate_cb_metadata({i_segment, cw_length, cw_offset, nof_filler_bits, nof_crc_bits, nof_tb_crc_bits}, cfg); - - // Prepare segment data. - described_segments.emplace_back(cb_metadata, segment_length); - described_segment& segment = described_segments.back(); - - // Segment bit buffer. - bit_buffer& cb_bit_buffer = segment.get_data(); - - // Number of information bits to get from the transport block. Remove transport block CRC bits if it is the last CB. - units::bits nof_used_bits = cb_info_bits; - if (last_cb) { - nof_used_bits -= nof_tb_crc_bits + zero_pad; - } - - // Copy information bits from the transport block. - bit_buffer cb_info_bit_buffer = cb_bit_buffer.first(nof_used_bits.value()); - srsvec::copy_offset(cb_info_bit_buffer, transport_block, tb_offset); - tb_offset += nof_used_bits.value(); - - // If it is the last CB, append the transport block CRC and padding bits. - if (last_cb) { - // For each byte of the transport block CRC... - for (unsigned i_checksum_byte = 0, i_checksum_byte_end = nof_tb_crc_bits.truncate_to_bytes().value(); - i_checksum_byte != i_checksum_byte_end; - ++i_checksum_byte) { - // Extract byte from the CRC. - unsigned tb_crc_byte = (tb_checksum >> (nof_tb_crc_bits.value() - (i_checksum_byte + 1) * 8)) & 0xffUL; - // Insert the byte at the end of the bit buffer. - cb_bit_buffer.insert(tb_crc_byte, nof_used_bits.value(), 8); - // Increment the number of bits. - nof_used_bits += 8_bits; - } - tb_offset += nof_tb_crc_bits.value(); - - // Insert zero padding bits. - for (units::bits nof_used_bits_end = nof_used_bits + zero_pad; nof_used_bits != nof_used_bits_end;) { - // Calculate the number of zeros to pad, no more than a byte at a time. - units::bits nof_zeros = std::min(8_bits, nof_used_bits_end - nof_used_bits); - // Insert the zeros at the end of the bit buffer. - cb_bit_buffer.insert(0UL, nof_used_bits.value(), nof_zeros.value()); - // Increment the number of bits. - nof_used_bits += nof_zeros; - } - } - - // Append codeblock CRC if required. - if (nof_crc_bits != 0_bits) { - crc_calculator_checksum_t cb_checksum = crc_set.crc24B->calculate(cb_bit_buffer.first(nof_used_bits.value())); - - // For each byte of the transport block CRC... - for (unsigned i_checksum_byte = 0, i_checksum_byte_end = nof_crc_bits.truncate_to_bytes().value(); - i_checksum_byte != i_checksum_byte_end; - ++i_checksum_byte) { - // Extract byte from the CRC. - unsigned cb_crc_byte = (cb_checksum >> (nof_crc_bits.value() - (i_checksum_byte + 1) * 8)) & 0xffUL; - // Insert the byte at the end of the bit buffer. - cb_bit_buffer.insert(cb_crc_byte, nof_used_bits.value(), 8); - // Increment the number of bits. - nof_used_bits += 8_bits; - } - } - - // Append filler bits as zeros. - while (nof_used_bits != segment_length) { - // Calculate the number of zeros to pad, no more than a byte at a time. - units::bits nof_zeros = std::min(8_bits, segment_length - nof_used_bits); - // Insert the zeros at the end of the bit buffer. - cb_bit_buffer.insert(0UL, nof_used_bits.value(), nof_zeros.value()); - // Increment the number of bits. - nof_used_bits += nof_zeros; - } - - // Advance offsets. - cw_offset += cb_metadata.cb_specific.rm_length; - } - - // After segmenting no bits should be left in the buffer. - srsran_assert(nof_tb_bits_in.value() == tb_offset, - "Transport block offset ({}) must be equal to the transport block size including CRC ({}).", - tb_offset, - nof_tb_bits_in); - // After accumulating all codeblock rate-matched lengths, cw_offset should be the same as cw_length. - srsran_assert(cw_length.value() == cw_offset, - "Codeblock offset ({}) must be equal to the codeword size ({}).", - cw_offset, - cw_length.value()); -} - -static void check_inputs_rx(span codeword_llrs, const segmenter_config& cfg) -{ - srsran_assert(!codeword_llrs.empty(), "Argument transport_block should not be empty."); - srsran_assert(codeword_llrs.size() == cfg.nof_ch_symbols * get_bits_per_symbol(cfg.mod), - "Wrong number of LLRs {} (expected {}).", - codeword_llrs.size(), - cfg.nof_ch_symbols * get_bits_per_symbol(cfg.mod)); - - srsran_assert((cfg.rv >= 0) && (cfg.rv <= 3), "Invalid redundancy version."); - - srsran_assert((cfg.nof_layers >= 1) && (cfg.nof_layers <= 4), "Invalid number of layers."); - - srsran_assert(cfg.nof_ch_symbols % (cfg.nof_layers) == 0, - "The number of channel symbols should be a multiple of the product between the number of layers."); -} - -// For the Rx-chain segmenter. -void ldpc_segmenter_impl::segment(static_vector& described_codeblocks, - span codeword_llrs, - unsigned tbs, - const segmenter_config& cfg) -{ - check_inputs_rx(codeword_llrs, cfg); - - using namespace units::literals; - - base_graph = cfg.base_graph; - - units::bits tbs_bits(tbs); - units::bits nof_tb_crc_bits = compute_tb_crc_size(tbs_bits); - - nof_tb_bits_in = tbs_bits + nof_tb_crc_bits; - - nof_segments = ldpc::compute_nof_codeblocks(tbs_bits, base_graph); - nof_tb_bits_out = nof_tb_bits_in; - if (nof_segments > 1) { - nof_tb_bits_out += units::bits(nof_segments * SEG_CRC_LENGTH.value()); - } - lifting_size = compute_lifting_size(tbs_bits, base_graph, nof_segments); - segment_length = compute_codeblock_size(base_graph, lifting_size); - - units::bits nof_crc_bits = (nof_segments > 1) ? SEG_CRC_LENGTH : 0_bits; - - // Compute the maximum number of information bits that can be assigned to a segment. - units::bits max_info_bits = units::bits(divide_ceil(nof_tb_bits_out.value(), nof_segments)) - nof_crc_bits; - - // Number of channel symbols assigned to a transmission layer. - nof_symbols_per_layer = cfg.nof_ch_symbols / cfg.nof_layers; - // Number of segments that will have a short rate-matched length. In TS38.212 Section 5.4.2.1, these correspond to - // codeblocks whose length E_r is computed by rounding down - floor. For the remaining codewords, the length is - // rounded up. - nof_short_segments = nof_segments - (nof_symbols_per_layer % nof_segments); - - // Codeword length (after concatenation of codeblocks). - units::bits cw_length(codeword_llrs.size()); - - unsigned cw_offset = 0; - for (unsigned i_segment = 0; i_segment != nof_segments; ++i_segment) { - units::bits nof_filler_bits = segment_length - (max_info_bits + nof_crc_bits); - - codeblock_metadata tmp_description = - generate_cb_metadata({i_segment, cw_length, cw_offset, nof_filler_bits, nof_crc_bits, nof_tb_crc_bits}, cfg); - - unsigned rm_length = tmp_description.cb_specific.rm_length; - described_codeblocks.push_back({codeword_llrs.subspan(cw_offset, rm_length), tmp_description}); - cw_offset += rm_length; - } - // After accumulating all codeblock rate-matched lengths, cw_offset should be the same as cw_length. - srsran_assert(cw_length.value() == cw_offset, "Cw offset must be equal to the cw length"); -} - -codeblock_metadata ldpc_segmenter_impl::generate_cb_metadata(const segment_internal& seg_extra, - const segmenter_config& cfg) const -{ - codeblock_metadata tmp_description; - - tmp_description.tb_common.base_graph = base_graph; - tmp_description.tb_common.lifting_size = static_cast(lifting_size); - tmp_description.tb_common.rv = cfg.rv; - tmp_description.tb_common.mod = cfg.mod; - tmp_description.tb_common.Nref = cfg.Nref; - tmp_description.tb_common.cw_length = seg_extra.cw_length.value(); - - unsigned rm_length = compute_rm_length(seg_extra.i_segment, cfg.mod, cfg.nof_layers); - - tmp_description.cb_specific.full_length = compute_full_codeblock_size(base_graph, segment_length).value(); - tmp_description.cb_specific.nof_filler_bits = seg_extra.nof_filler_bits.value(); - tmp_description.cb_specific.rm_length = rm_length; - tmp_description.cb_specific.cw_offset = seg_extra.cw_offset; - // nof_crc_bits == 0 indicates that we are using the TB CRC with length 16. - tmp_description.cb_specific.nof_crc_bits = - (nof_segments == 1) ? seg_extra.nof_tb_crc_bits.value() : seg_extra.nof_crc_bits.value(); - - return tmp_description; -} diff --git a/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_impl.h b/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_impl.h deleted file mode 100644 index 48bc7adb69..0000000000 --- a/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_impl.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -/// \file -/// \brief LDPC codeblock segmentation declaration. - -#pragma once - -#include "ldpc_graph_impl.h" -#include "srsran/phy/upper/channel_coding/crc_calculator.h" -#include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_rx.h" -#include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx.h" - -namespace srsran { - -/// \brief Generic implementation of LDPC segmentation. -/// -/// Implements both ldpc_segmenter_tx and ldpc_segmenter_rx. For this reason, the constructor has been hidden behind the -/// static wrapper methods create_ldpc_segmenter_impl_tx() and create_ldpc_segmenter_impl_rx(). -class ldpc_segmenter_impl : public ldpc_segmenter_tx, public ldpc_segmenter_rx -{ -public: - /// CRC calculators used in shared channels. - struct sch_crc { - /// For short TB checksums. - std::unique_ptr crc16; - /// For long TB checksums. - std::unique_ptr crc24A; - /// For segment-specific checksums. - std::unique_ptr crc24B; - }; - - /// \brief Wraps the constructor of the Tx version of the LDPC segmenter. - static std::unique_ptr create_ldpc_segmenter_impl_tx(sch_crc&); - - /// \brief Wraps the constructor of the Rx version of the LDPC segmenter. - /// \remark The receive-chain version of the segmenter does not need CRC calculators. - static std::unique_ptr create_ldpc_segmenter_impl_rx(); - - // Tx-chain segmentation. - // See interface for the documentation. - void segment(static_vector& described_segments, - span transport_block, - const segmenter_config& cfg) override; - - // Rx-chain segmentation. - // See interface for the documentation. - void segment(static_vector& described_codeblocks, - span codeword_llrs, - unsigned tbs, - const segmenter_config& cfg) override; - -private: - /// Default constructor. - ldpc_segmenter_impl() = default; - - /// \brief Creates an LDPC segmentation object that aggregates a crc_calculator. - /// - /// \param[in] c The CRC calculators to aggregate. The generation polynomials must math their respective types. - explicit ldpc_segmenter_impl(sch_crc c) : crc_set({std::move(c.crc16), std::move(c.crc24A), std::move(c.crc24B)}) {} - - /// Computes the length of the rate-matched codeblock corresponding to each segment, as per TS38.212 - /// Section 5.4.2.1. - unsigned compute_rm_length(unsigned i_seg, modulation_scheme mod, unsigned nof_layers) const; - - /// Internally computed segment metadata. - struct segment_internal { - /// Segment index. - unsigned i_segment; - /// Total codeword length. - units::bits cw_length; - /// Codeblock starting index within the codeword. - unsigned cw_offset; - /// Number of filler bits. - units::bits nof_filler_bits; - /// Number of segment-specific CRC bits. - units::bits nof_crc_bits; - /// Number of TB-specific CRC bits. - units::bits nof_tb_crc_bits; - }; - - /// Generates a codeblock metadata structure for the current segment configuration. - codeblock_metadata generate_cb_metadata(const segment_internal& seg_extra, const segmenter_config& cfg) const; - - /// Base graph used for encoding/decoding the current transport block. - ldpc_base_graph_type base_graph = ldpc_base_graph_type::BG1; - /// Lifting size used for encoding/decoding the current transport block. - unsigned lifting_size = 0; - - /// \name Attributes relative to TS38.212 Section 5.2.2. - ///@{ - - /// Final length of a segment (corresponds to \f$K\f$). - units::bits segment_length{0}; - /// Number of bits in the transport block (corresponds to \f$B\f$). - units::bits nof_tb_bits_in{0}; - /// Augmented number of bits in the transport block, including new CRCs (corresponds to \f$B'\f$). - units::bits nof_tb_bits_out{0}; - /// Number of segments resulting from the transport block (corresponds to \f$C\f$). - unsigned nof_segments = 0; - ///@} - - /// \name Attributes relative to TS38.212 Section 5.4.2.1. - ///@{ - - /// Number of symbols per transmission layer (corresponds to \f$G / (N_L Q_m)\f$). - unsigned nof_symbols_per_layer = 0; - /// \brief Number of segments of short rate-matched length (corresponds to \f$C - \bigr(\bigl(G / (N_L Q_m)\bigr) - /// \bmod C\bigr)\f$). - unsigned nof_short_segments = 0; - ///@} - - /// CRC calculators for transport-block and segment-specific checksums. - sch_crc crc_set; -}; - -} // namespace srsran diff --git a/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_rx_impl.cpp b/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_rx_impl.cpp new file mode 100644 index 0000000000..fc6515429b --- /dev/null +++ b/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_rx_impl.cpp @@ -0,0 +1,106 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "ldpc_segmenter_rx_impl.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc.h" +#include "srsran/phy/upper/codeblock_metadata.h" +#include "srsran/srsvec/bit.h" +#include "srsran/srsvec/copy.h" +#include "srsran/support/math/math_utils.h" +#include "srsran/support/srsran_assert.h" + +using namespace srsran; +using namespace srsran::ldpc; + +/// Length of the CRC checksum added to the segments. +static constexpr units::bits SEG_CRC_LENGTH{24}; + +static void check_inputs_rx(span codeword_llrs, const segmenter_config& cfg) +{ + srsran_assert(!codeword_llrs.empty(), "Argument transport_block should not be empty."); + srsran_assert(codeword_llrs.size() == cfg.nof_ch_symbols * get_bits_per_symbol(cfg.mod), + "Wrong number of LLRs {} (expected {}).", + codeword_llrs.size(), + cfg.nof_ch_symbols * get_bits_per_symbol(cfg.mod)); + + srsran_assert((cfg.rv >= 0) && (cfg.rv <= 3), "Invalid redundancy version."); + + srsran_assert((cfg.nof_layers >= 1) && (cfg.nof_layers <= 4), "Invalid number of layers."); + + srsran_assert(cfg.nof_ch_symbols % (cfg.nof_layers) == 0, + "The number of channel symbols should be a multiple of the product between the number of layers."); +} + +void ldpc_segmenter_rx_impl::segment(static_vector& described_codeblocks, + span codeword_llrs, + unsigned tbs, + const segmenter_config& cfg) +{ + check_inputs_rx(codeword_llrs, cfg); + + using namespace units::literals; + + params.base_graph = cfg.base_graph; + + units::bits tbs_bits(tbs); + params.nof_tb_crc_bits = compute_tb_crc_size(tbs_bits); + + params.nof_tb_bits_in = tbs_bits + params.nof_tb_crc_bits; + + params.nof_segments = ldpc::compute_nof_codeblocks(tbs_bits, params.base_graph); + params.nof_tb_bits_out = params.nof_tb_bits_in; + if (params.nof_segments > 1) { + params.nof_tb_bits_out += units::bits(params.nof_segments * SEG_CRC_LENGTH.value()); + } + params.lifting_size = compute_lifting_size(tbs_bits, params.base_graph, params.nof_segments); + params.segment_length = compute_codeblock_size(params.base_graph, params.lifting_size); + + params.nof_crc_bits = (params.nof_segments > 1) ? SEG_CRC_LENGTH : 0_bits; + + // Compute the maximum number of information bits that can be assigned to a segment. + units::bits max_info_bits = + units::bits(divide_ceil(params.nof_tb_bits_out.value(), params.nof_segments)) - params.nof_crc_bits; + + // Number of channel symbols assigned to a transmission layer. + params.nof_symbols_per_layer = cfg.nof_ch_symbols / cfg.nof_layers; + // Number of segments that will have a short rate-matched length. In TS38.212 Section 5.4.2.1, these correspond to + // codeblocks whose length E_r is computed by rounding down - floor. For the remaining codewords, the length is + // rounded up. + params.nof_short_segments = params.nof_segments - (params.nof_symbols_per_layer % params.nof_segments); + + // Codeword length (after concatenation of codeblocks). + params.cw_length = static_cast(codeword_llrs.size()); + + unsigned cw_offset = 0; + for (unsigned i_segment = 0; i_segment != params.nof_segments; ++i_segment) { + params.cw_offset[i_segment] = cw_offset; + params.nof_filler_bits = params.segment_length - (max_info_bits + params.nof_crc_bits); + + codeblock_metadata tmp_description = generate_cb_metadata(params, cfg, i_segment); + + unsigned rm_length = tmp_description.cb_specific.rm_length; + described_codeblocks.push_back({codeword_llrs.subspan(cw_offset, rm_length), tmp_description}); + cw_offset += rm_length; + } + // After accumulating all codeblock rate-matched lengths, cw_offset should be the same as cw_length. + srsran_assert(params.cw_length.value() == cw_offset, "Cw offset must be equal to the cw length"); +} diff --git a/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_rx_impl.h b/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_rx_impl.h new file mode 100644 index 0000000000..7222a5ed82 --- /dev/null +++ b/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_rx_impl.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/// \file +/// \brief LDPC codeblock segmentation declaration. + +#pragma once + +#include "ldpc_graph_impl.h" +#include "ldpc_segmenter_helpers.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_rx.h" + +namespace srsran { + +/// \brief Generic implementation of Rx-chain LDPC segmentation. +class ldpc_segmenter_rx_impl : public ldpc_segmenter_rx +{ +public: + /// Default constructor. + ldpc_segmenter_rx_impl() = default; + + // See interface for the documentation. + void segment(static_vector& described_codeblocks, + span codeword_llrs, + unsigned tbs, + const segmenter_config& cfg) override; + +private: + /// Segmentation parameters. + segment_parameters params; +}; + +} // namespace srsran diff --git a/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx_impl.cpp b/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx_impl.cpp new file mode 100644 index 0000000000..8f54441728 --- /dev/null +++ b/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx_impl.cpp @@ -0,0 +1,254 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "ldpc_segmenter_tx_impl.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc.h" +#include "srsran/phy/upper/codeblock_metadata.h" +#include "srsran/srsvec/bit.h" +#include "srsran/srsvec/copy.h" +#include "srsran/support/math/math_utils.h" +#include "srsran/support/srsran_assert.h" + +using namespace srsran; +using namespace srsran::ldpc; + +/// Length of the CRC checksum added to the segments. +static constexpr units::bits SEG_CRC_LENGTH{24}; +/// Maximum accepted transport block size. +/// Note: This value has to be multiple of 8. +static constexpr units::bits MAX_TBS{1277992}; +static_assert(MAX_TBS.is_byte_exact(), "Value is not a multiple of 8"); + +static void check_inputs_tx(span transport_block, const segmenter_config& cfg) +{ + using namespace units::literals; + srsran_assert(!transport_block.empty(), "Argument transport_block should not be empty."); + srsran_assert(units::bytes(transport_block.size()).to_bits() + 24_bits <= MAX_TBS, + "Transport block too long. The admissible size, including CRC, is {}.", + MAX_TBS.truncate_to_bytes()); + + srsran_assert((cfg.rv >= 0) && (cfg.rv <= 3), "Invalid redundancy version."); + + srsran_assert((cfg.nof_layers >= 1) && (cfg.nof_layers <= 4), "Invalid number of layers."); + + srsran_assert(cfg.nof_ch_symbols % (cfg.nof_layers) == 0, + "The number of channel symbols should be a multiple of the product between the number of layers."); +} + +// For the Tx-chain segmenter without using an intermediate buffer. +ldpc_segmenter_buffer& ldpc_segmenter_tx_impl::new_transmission(span transport_block, + const segmenter_config& cfg) +{ + check_inputs_tx(transport_block, cfg); + + using namespace units::literals; + + params.base_graph = cfg.base_graph; + // Each transport_block entry is a byte, and TBS can always be expressed as an integer number of bytes (see, e.g., + // TS38.214 Section 5.1.3.2). + units::bits nof_tb_bits_tmp = units::bytes(transport_block.size()).to_bits(); + + params.nof_tb_crc_bits = compute_tb_crc_size(nof_tb_bits_tmp); + + params.nof_tb_bits_in = nof_tb_bits_tmp + params.nof_tb_crc_bits; + + params.nof_segments = ldpc::compute_nof_codeblocks(nof_tb_bits_tmp, params.base_graph); + params.nof_tb_bits_out = params.nof_tb_bits_in; + if (params.nof_segments > 1) { + params.nof_tb_bits_out += units::bits(params.nof_segments * SEG_CRC_LENGTH.value()); + } + params.lifting_size = compute_lifting_size(nof_tb_bits_tmp, params.base_graph, params.nof_segments); + params.segment_length = compute_codeblock_size(params.base_graph, params.lifting_size); + + params.nof_crc_bits = (params.nof_segments > 1) ? SEG_CRC_LENGTH : 0_bits; + + // Compute the number of information bits that is assigned to a segment. + params.cb_info_bits = + units::bits(divide_ceil(params.nof_tb_bits_out.value(), params.nof_segments)) - params.nof_crc_bits; + + // Zero-padding if necessary. + params.zero_pad = + units::bits((params.cb_info_bits + params.nof_crc_bits).value() * params.nof_segments) - params.nof_tb_bits_out; + + // Number of channel symbols assigned to a transmission layer. + params.nof_symbols_per_layer = cfg.nof_ch_symbols / cfg.nof_layers; + // Number of segments that will have a short rate-matched length. In TS38.212 Section 5.4.2.1, these correspond to + // codeblocks whose length E_r is computed by rounding down - floor. For the remaining codewords, the length is + // rounded up. + params.nof_short_segments = params.nof_segments - (params.nof_symbols_per_layer % params.nof_segments); + + // Codeword length (after concatenation of codeblocks). + params.cw_length = static_cast(cfg.nof_ch_symbols * get_bits_per_symbol(cfg.mod)); + + // Number of filler bits in this segment. + params.nof_filler_bits = params.segment_length - params.cb_info_bits - params.nof_crc_bits; + + unsigned cw_offset = 0; + unsigned tb_offset = 0; + for (unsigned i_segment = 0; i_segment != params.nof_segments; ++i_segment) { + bool last_cb = (i_segment == params.nof_segments - 1); + + // Save codeblock offsets. + params.cw_offset[i_segment] = cw_offset; + params.tb_offset[i_segment] = tb_offset; + + // Generate metadata for this segment. + params.cb_metadata[i_segment] = generate_cb_metadata(params, cfg, i_segment); + + // Number of information bits to get from the transport block. Remove transport block CRC bits if it is the last CB. + units::bits nof_used_bits = params.cb_info_bits; + if (last_cb) { + nof_used_bits -= params.nof_tb_crc_bits + params.zero_pad; + params.cb_info_bits_last = nof_used_bits; + tb_offset += params.nof_tb_crc_bits.value(); + } + + // Advance codeblock offsets. + tb_offset += nof_used_bits.value(); + cw_offset += this->get_rm_length(i_segment); + } + + // After segmenting no bits should be left in the buffer. + srsran_assert(params.nof_tb_bits_in.value() == tb_offset, + "Transport block offset ({}) must be equal to the transport block size including CRC ({}).", + tb_offset, + params.nof_tb_bits_in); + // After accumulating all codeblock rate-matched lengths, cw_offset should be the same as cw_length. + srsran_assert(params.cw_length.value() == cw_offset, + "Codeblock offset ({}) must be equal to the codeword size ({}).", + cw_offset, + params.cw_length.value()); + + return *this; +} + +void ldpc_segmenter_tx_impl::read_codeblock(bit_buffer codeblock, + span transport_block, + unsigned cb_index) const +{ + srsran_assert(codeblock.size() == params.segment_length.value(), + "Invalid codeblock size (i.e., {}), expected {}.", + codeblock.size(), + params.segment_length.value()); + srsran_assert(cb_index < params.nof_segments, + "Codeblock index ({}) must be lower than the number of segments ({}).", + cb_index, + params.nof_segments); + + using namespace units::literals; + + units::bits nof_used_bits = 0_bits; + unsigned nof_segment_bits = this->get_cb_info_bits(cb_index).value(); + + // Copy codeblock data. + { + bit_buffer message = codeblock.first(nof_segment_bits); + srsvec::copy_offset(message, transport_block, params.tb_offset[cb_index]); + nof_used_bits += units::bits(nof_segment_bits); + } + + // Append transport block CRC if applicable. + if ((params.tb_offset[cb_index] + nof_segment_bits) == units::bytes(transport_block.size()).to_bits().value()) { + // Append TB CRC. + constexpr units::bits MAX_BITS_CRC16{3824}; + crc_calculator& tb_crc = + (units::bytes(transport_block.size()).to_bits() <= MAX_BITS_CRC16) ? *crc_set.crc16 : *crc_set.crc24A; + crc_calculator_checksum_t tb_checksum = tb_crc.calculate_byte(transport_block); + + for (unsigned i_checksum_byte = 0, i_checksum_byte_end = params.nof_tb_crc_bits.truncate_to_bytes().value(); + i_checksum_byte != i_checksum_byte_end; + ++i_checksum_byte) { + // Extract byte from the CRC. + unsigned tb_crc_byte = (tb_checksum >> (params.nof_tb_crc_bits.value() - (i_checksum_byte + 1) * 8)) & 0xffUL; + // Insert the byte at the end of the bit buffer. + codeblock.insert(tb_crc_byte, nof_used_bits.value(), 8); + // Increment the number of bits. + nof_used_bits += 8_bits; + } + + // Insert zero padding bits. + for (units::bits nof_used_bits_end = nof_used_bits + params.zero_pad; nof_used_bits != nof_used_bits_end;) { + // Calculate the number of zeros to pad, no more than a byte at a time. + units::bits nof_zeros = std::min(8_bits, nof_used_bits_end - nof_used_bits); + // Insert the zeros at the end of the bit buffer. + codeblock.insert(0UL, nof_used_bits.value(), nof_zeros.value()); + // Increment the number of bits. + nof_used_bits += nof_zeros; + } + } + + // Append codeblock CRC if applicable. + if (params.nof_crc_bits != 0_bits) { + crc_calculator_checksum_t cb_checksum = crc_set.crc24B->calculate(codeblock.first(nof_used_bits.value())); + for (unsigned i_checksum_byte = 0, i_checksum_byte_end = 3; i_checksum_byte != i_checksum_byte_end; + ++i_checksum_byte) { + // Extract byte from the CRC. + unsigned cb_crc_byte = (cb_checksum >> (24 - (i_checksum_byte + 1) * 8)) & 0xffUL; + // Insert the byte at the end of the bit buffer. + codeblock.insert(cb_crc_byte, nof_used_bits.value(), 8); + // Increment the number of bits. + nof_used_bits += 8_bits; + } + } + + // Append filler bits as zeros. + while (nof_used_bits != params.segment_length) { + // Calculate the number of zeros to pad, no more than a byte at a time. + units::bits nof_zeros = std::min(8_bits, units::bits(params.segment_length) - nof_used_bits); + // Insert the zeros at the end of the bit buffer. + codeblock.insert(0UL, nof_used_bits.value(), nof_zeros.value()); + // Increment the number of bits. + nof_used_bits += nof_zeros; + } +} + +codeblock_metadata ldpc_segmenter_tx_impl::get_cb_metadata(unsigned cb_index) const +{ + srsran_assert(cb_index < params.nof_segments, + "Codeblock index ({}) must be lower than the number of segments ({}).", + cb_index, + params.nof_segments); + + return params.cb_metadata[cb_index]; +} + +units::bits ldpc_segmenter_tx_impl::get_cb_info_bits(unsigned cb_index) const +{ + srsran_assert(cb_index < params.nof_segments, + "Codeblock index ({}) must be lower than the number of segments ({}).", + cb_index, + params.nof_segments); + + bool last_cb = (cb_index == params.nof_segments - 1); + + return last_cb ? params.cb_info_bits_last : params.cb_info_bits; +} + +unsigned ldpc_segmenter_tx_impl::get_rm_length(unsigned cb_index) const +{ + srsran_assert(cb_index < params.nof_segments, + "Codeblock index ({}) must be lower than the number of segments ({}).", + cb_index, + params.nof_segments); + + return params.cb_metadata[cb_index].cb_specific.rm_length; +} diff --git a/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx_impl.h b/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx_impl.h new file mode 100644 index 0000000000..5149f34ac0 --- /dev/null +++ b/lib/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx_impl.h @@ -0,0 +1,109 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/// \file +/// \brief Tx-chain bufferless LDPC codeblock segmentation. + +#pragma once + +#include "ldpc_graph_impl.h" +#include "ldpc_segmenter_helpers.h" +#include "srsran/phy/upper/channel_coding/crc_calculator.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_buffer.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx.h" + +namespace srsran { + +/// \brief Bufferless implementation of Tx-chain LDPC segmentation. +class ldpc_segmenter_tx_impl : public ldpc_segmenter_tx, private ldpc_segmenter_buffer +{ +public: + /// CRC calculators used in shared channels. + struct sch_crc { + /// For short TB checksums. + std::unique_ptr crc16; + /// For long TB checksums. + std::unique_ptr crc24A; + /// For segment-specific checksums. + std::unique_ptr crc24B; + }; + + /// \brief Initializes the internal Tx-chain LDPC segmenter blocks. + /// \param[in] crc Unique pointers to CRC calculators. + ldpc_segmenter_tx_impl(sch_crc& c) : crc_set({std::move(c.crc16), std::move(c.crc24A), std::move(c.crc24B)}) + { + srsran_assert(crc_set.crc16, "Invalid CRC16 calculator."); + srsran_assert(crc_set.crc24A, "Invalid CRC24A calculator."); + srsran_assert(crc_set.crc24B, "Invalid CRC24B calculator."); + srsran_assert(crc_set.crc16->get_generator_poly() == crc_generator_poly::CRC16, + "Not a CRC generator of type CRC16."); + srsran_assert(crc_set.crc24A->get_generator_poly() == crc_generator_poly::CRC24A, + "Not a CRC generator of type CRC24A."); + srsran_assert(crc_set.crc24B->get_generator_poly() == crc_generator_poly::CRC24B, + "Not a CRC generator of type CRC24B."); + } + + // See interface for the documentation. + ldpc_segmenter_buffer& new_transmission(span transport_block, const segmenter_config& cfg) override; + +private: + /// Segmentation parameters. + segment_parameters params; + + /// CRC calculators for transport-block and segment-specific checksums. + sch_crc crc_set; + + // See ldpc_segmenter_buffer interface for documentation. + void read_codeblock(bit_buffer codeblock, span transport_block, unsigned cb_index) const override; + + // See ldpc_segmenter_buffer interface for documentation. + codeblock_metadata get_cb_metadata(unsigned cb_index) const override; + + // See ldpc_segmenter_buffer interface for documentation. + unsigned get_nof_codeblocks() const override { return params.nof_segments; }; + + // See ldpc_segmenter_buffer interface for documentation. + units::bits get_cb_info_bits(unsigned cb_index) const override; + + // See ldpc_segmenter_buffer interface for documentation. + units::bits get_segment_length() const override { return params.segment_length; }; + + // See ldpc_segmenter_buffer interface for documentation. + units::bits get_cw_length() const override { return params.cw_length; }; + + // See ldpc_segmenter_buffer interface for documentation. + units::bits get_zero_pad() const override { return params.zero_pad; }; + + // See ldpc_segmenter_buffer interface for documentation. + units::bits get_tb_crc_bits() const override { return params.nof_tb_crc_bits; }; + + // See ldpc_segmenter_buffer interface for documentation. + units::bits get_nof_filler_bits() const override { return params.nof_filler_bits; }; + + // See ldpc_segmenter_buffer interface for documentation. + unsigned get_nof_short_segments() const override { return params.nof_short_segments; }; + + // See ldpc_segmenter_buffer interface for documentation. + unsigned get_rm_length(unsigned cb_index) const override; +}; + +} // namespace srsran diff --git a/lib/phy/upper/channel_coding/polar/polar_decoder_impl.cpp b/lib/phy/upper/channel_coding/polar/polar_decoder_impl.cpp index df1f821558..0531d0bb45 100644 --- a/lib/phy/upper/channel_coding/polar/polar_decoder_impl.cpp +++ b/lib/phy/upper/channel_coding/polar/polar_decoder_impl.cpp @@ -93,8 +93,8 @@ void polar_decoder_impl::tmp_node_s::compute(std::vector& node_type, uint8_t code_size_log_1 = code_size_log - 1; uint16_t code_half_size = (1U << code_size_log_1); - srsvec::zero(i_even.first(code_half_size)); - srsvec::zero(i_odd.first(code_half_size)); + srsvec::zero(span(i_even).first(code_half_size)); + srsvec::zero(span(i_odd).first(code_half_size)); for (uint16_t i = 0; i != code_half_size; ++i) { i_even[i] = 2 * i; i_odd[i] = 2 * i + 1; @@ -157,12 +157,12 @@ polar_decoder_impl::polar_decoder_impl(std::unique_ptr enc_, uint llr_alloc.resize(llr_all_stages); // Assign a valid view for the first stage llr0 and an invalid view for llr1. - llr0[0] = llr_alloc.first(1); + llr0[0] = span(llr_alloc).first(1); llr1[0] = span(); // Assign a valid view for the rest of stages llr0 and llr1. for (uint8_t s = 1; s != n_llr_all_stages; ++s) { - llr0[s] = llr_alloc.subspan(param.code_stage_size[s] - 1, param.code_stage_size[s]); + llr0[s] = span(llr_alloc).subspan(param.code_stage_size[s] - 1, param.code_stage_size[s]); llr1[s] = llr0[s].last(param.code_stage_size[s - 1]); } @@ -189,7 +189,7 @@ void polar_decoder_impl::init(span data_decoded, srsvec::zero(data_decoded.first(code_size)); // Initialize est_bit vector to all zeros. - srsvec::zero(est_bit.first(code_size)); + srsvec::zero(span(est_bit).first(code_size)); // Initializes LLR buffer for the last stage/level with the input LLRs values. for (uint16_t i = 0; i != code_half_size; ++i) { @@ -199,7 +199,7 @@ void polar_decoder_impl::init(span data_decoded, // Initializes the state of the decoding tree: start from the only one node at the last stage + 1. state.stage = code_size_log + 1; - srsvec::zero(state.active_node_per_stage.first(code_size_log + 1)); + srsvec::zero(span(state.active_node_per_stage).first(code_size_log + 1)); state.flag_finished = false; // Computes the node types for the decoding tree. @@ -233,7 +233,7 @@ void polar_decoder_impl::rate_1_node(span message) uint16_t code_size = param.code_stage_size[param.code_size_log]; uint16_t code_stage_size = param.code_stage_size[stage]; - span codeword = est_bit.subspan(bit_pos, code_stage_size); + span codeword = span(est_bit).subspan(bit_pos, code_stage_size); srsran_assert(llr0[stage].size() == code_stage_size, "Invalid size ({} != {})", llr0[stage].size(), code_stage_size); vec_hard_bit(llr0[stage], codeword); diff --git a/lib/phy/upper/channel_coding/polar/polar_decoder_impl.h b/lib/phy/upper/channel_coding/polar/polar_decoder_impl.h index 5f623c18b1..4ab6a53d44 100644 --- a/lib/phy/upper/channel_coding/polar/polar_decoder_impl.h +++ b/lib/phy/upper/channel_coding/polar/polar_decoder_impl.h @@ -27,7 +27,6 @@ #include "srsran/phy/upper/channel_coding/polar/polar_decoder.h" #include "srsran/phy/upper/channel_coding/polar/polar_encoder.h" -#include "srsran/srsvec/aligned_vec.h" namespace srsran { @@ -52,7 +51,7 @@ class polar_decoder_impl : public polar_decoder /// \f$log_2\f$ of the code size. uint8_t code_size_log; /// Number of bits of the encoder input/output vector, for all stages. - srsvec::aligned_vec code_stage_size; + std::vector code_stage_size; /// Stores the type of all nodes in stage 0. std::vector node_type_alloc; /// Stores the type of all nodes, stage by stage (e.g., node_type[0] is the same as \c node_type_alloc). @@ -66,21 +65,21 @@ class polar_decoder_impl : public polar_decoder /// True if the last bit is decoded. False otherwise. bool flag_finished; /// Indicates the active node at each stage of the algorithm at a given moment. - srsvec::aligned_vec active_node_per_stage; + std::vector active_node_per_stage; }; /// Structure with pointers needed to obtain the node_type. struct tmp_node_s { /// \brief Denotes whether a node is of type [RATE_0](#polar_decoder_impl::node_rate) (value 0) or of another /// type (value 1). - srsvec::aligned_vec is_not_rate_0; + std::vector is_not_rate_0; /// \brief Denotes whether a node is of type [RATE_1](#polar_decoder_impl::node_rate) (value 1) or of another type /// (value 0). span is_rate_1; /// List of even-valued node indices. - srsvec::aligned_vec i_even; + std::vector i_even; /// List of odd-valued node indices. - srsvec::aligned_vec i_odd; + std::vector i_odd; /// \brief Allocates memory resources for the computation of the node_type. /// \param[in] nMax \f$log_2\f$ of the maximum number of bits in the codeword. @@ -94,13 +93,13 @@ class polar_decoder_impl : public polar_decoder }; /// LLR values for stage 0 (i.e., received LLRs). - srsvec::aligned_vec llr_alloc; + std::vector llr_alloc; /// Pointers to the upper half of the LLR values for all stages. std::vector> llr0; /// Pointers to the lower half of LLR values for all stages. std::vector> llr1; /// Temporary estimated bits. - srsvec::aligned_vec est_bit; + std::vector est_bit; /// Decoder inner parameters. params_s param; /// Decoder state. diff --git a/lib/phy/upper/channel_coding/polar/polar_encoder_impl.h b/lib/phy/upper/channel_coding/polar/polar_encoder_impl.h index 68db5e7ba4..0018ba26d6 100644 --- a/lib/phy/upper/channel_coding/polar/polar_encoder_impl.h +++ b/lib/phy/upper/channel_coding/polar/polar_encoder_impl.h @@ -25,7 +25,6 @@ #pragma once #include "srsran/phy/upper/channel_coding/polar/polar_encoder.h" -#include "srsran/srsvec/aligned_vec.h" namespace srsran { diff --git a/lib/phy/upper/channel_modulation/evm_calculator_generic_impl.cpp b/lib/phy/upper/channel_modulation/evm_calculator_generic_impl.cpp index fe3b1eb7dd..569080b1a9 100644 --- a/lib/phy/upper/channel_modulation/evm_calculator_generic_impl.cpp +++ b/lib/phy/upper/channel_modulation/evm_calculator_generic_impl.cpp @@ -55,7 +55,7 @@ float evm_calculator_generic_impl::calculate(span so hard_decision(hard_bits, soft_bits.first(block_nof_bits)); // Modulate. - span modulated = temp_modulated.first(block_nof_symbols); + span modulated = span(temp_modulated).first(block_nof_symbols); modulator->modulate(modulated, hard_bits, modulation); // Calculate EVM. diff --git a/lib/phy/upper/channel_modulation/evm_calculator_generic_impl.h b/lib/phy/upper/channel_modulation/evm_calculator_generic_impl.h index 6dec42157b..bd8ac7af1f 100644 --- a/lib/phy/upper/channel_modulation/evm_calculator_generic_impl.h +++ b/lib/phy/upper/channel_modulation/evm_calculator_generic_impl.h @@ -24,7 +24,6 @@ #include "srsran/phy/upper/channel_modulation/evm_calculator.h" #include "srsran/phy/upper/channel_modulation/modulation_mapper.h" -#include "srsran/srsvec/aligned_vec.h" namespace srsran { @@ -54,7 +53,7 @@ class evm_calculator_generic_impl : public evm_calculator /// Data after hard-decision. dynamic_bit_buffer temp_hard_bits; /// Modulated data. - srsvec::aligned_vec temp_modulated; + std::vector temp_modulated; }; } // namespace srsran diff --git a/lib/phy/upper/channel_processors/CMakeLists.txt b/lib/phy/upper/channel_processors/CMakeLists.txt index 936b31fb2e..eb7bbf6275 100644 --- a/lib/phy/upper/channel_processors/CMakeLists.txt +++ b/lib/phy/upper/channel_processors/CMakeLists.txt @@ -40,6 +40,7 @@ target_link_libraries(srsran_pdcch_processor srsran_ran) add_library(srsran_prach_detector STATIC prach_detector_generic_impl.cpp + prach_detector_generic_thresholds.cpp prach_generator_impl.cpp prach_detector_phy_validator_impl.cpp) target_link_libraries(srsran_prach_detector srsran_ran srsvec) diff --git a/lib/phy/upper/channel_processors/pdsch/factories.cpp b/lib/phy/upper/channel_processors/pdsch/factories.cpp index 37e2f3d0a1..151523b9ce 100644 --- a/lib/phy/upper/channel_processors/pdsch/factories.cpp +++ b/lib/phy/upper/channel_processors/pdsch/factories.cpp @@ -160,7 +160,7 @@ class pdsch_processor_factory_sw : public pdsch_processor_factory class pdsch_processor_concurrent_factory_sw : public pdsch_processor_factory { public: - pdsch_processor_concurrent_factory_sw(std::shared_ptr crc_factory, + pdsch_processor_concurrent_factory_sw(std::shared_ptr segmenter_factory_, std::shared_ptr encoder_factory, std::shared_ptr rate_matcher_factory, std::shared_ptr prg_factory_, @@ -170,9 +170,12 @@ class pdsch_processor_concurrent_factory_sw : public pdsch_processor_factory std::shared_ptr ptrs_factory, task_executor& executor_, unsigned nof_concurrent_threads) : - prg_factory(std::move(prg_factory_)), rg_mapper_factory(std::move(rg_mapper_factory_)), executor(executor_) + segmenter_factory(std::move(segmenter_factory_)), + prg_factory(std::move(prg_factory_)), + rg_mapper_factory(std::move(rg_mapper_factory_)), + executor(executor_) { - srsran_assert(crc_factory, "Invalid CRC calculator factory."); + srsran_assert(segmenter_factory, "Invalid segmenter factory."); srsran_assert(encoder_factory, "Invalid encoder factory."); srsran_assert(rate_matcher_factory, "Invalid rate matcher factory."); srsran_assert(prg_factory, "Invalid PRG factory."); @@ -186,10 +189,7 @@ class pdsch_processor_concurrent_factory_sw : public pdsch_processor_factory std::vector> cb_processors; for (unsigned i_encoder = 0; i_encoder != nof_concurrent_threads; ++i_encoder) { cb_processors.emplace_back( - std::make_unique(crc_factory->create(crc_generator_poly::CRC24A), - crc_factory->create(crc_generator_poly::CRC24B), - crc_factory->create(crc_generator_poly::CRC16), - encoder_factory->create(), + std::make_unique(encoder_factory->create(), rate_matcher_factory->create(), prg_factory->create(), modulator_factory->create_modulation_mapper())); @@ -222,7 +222,8 @@ class pdsch_processor_concurrent_factory_sw : public pdsch_processor_factory std::unique_ptr create() override { - return std::make_unique(cb_processor_pool, + return std::make_unique(segmenter_factory->create(), + cb_processor_pool, prg_factory->create(), rg_mapper_factory->create(), dmrs_generator_pool, @@ -236,6 +237,7 @@ class pdsch_processor_concurrent_factory_sw : public pdsch_processor_factory } private: + std::shared_ptr segmenter_factory; std::shared_ptr prg_factory; std::shared_ptr rg_mapper_factory; task_executor& executor; @@ -427,9 +429,9 @@ srsran::create_pdsch_processor_factory_sw(std::shared_ptr } std::shared_ptr -srsran::create_pdsch_concurrent_processor_factory_sw(std::shared_ptr crc_factory, - std::shared_ptr ldpc_enc_factory, - std::shared_ptr ldpc_rm_factory, +srsran::create_pdsch_concurrent_processor_factory_sw(std::shared_ptr ldpc_segmenter_factory, + std::shared_ptr ldpc_enc_factory, + std::shared_ptr ldpc_rm_factory, std::shared_ptr prg_factory, std::shared_ptr rg_mapper_factory, std::shared_ptr modulator_factory, @@ -438,7 +440,7 @@ srsran::create_pdsch_concurrent_processor_factory_sw(std::shared_ptr(std::move(crc_factory), + return std::make_shared(std::move(ldpc_segmenter_factory), std::move(ldpc_enc_factory), std::move(ldpc_rm_factory), std::move(prg_factory), @@ -488,4 +490,4 @@ std::unique_ptr pdsch_processor_factory::create(srslog::basic_l bool enable_logging_broadcast) { return std::make_unique(logger, enable_logging_broadcast, create()); -} \ No newline at end of file +} diff --git a/lib/phy/upper/channel_processors/pdsch/pdsch_codeblock_processor.cpp b/lib/phy/upper/channel_processors/pdsch/pdsch_codeblock_processor.cpp index 3b2beb73a5..47481f36fe 100644 --- a/lib/phy/upper/channel_processors/pdsch/pdsch_codeblock_processor.cpp +++ b/lib/phy/upper/channel_processors/pdsch/pdsch_codeblock_processor.cpp @@ -24,8 +24,9 @@ using namespace srsran; -pdsch_codeblock_processor::result pdsch_codeblock_processor::process(span data, - const configuration& config) +pdsch_codeblock_processor::result pdsch_codeblock_processor::process(span data, + const ldpc_segmenter_buffer& segment_buffer, + const configuration& config) { using namespace units::literals; @@ -36,7 +37,6 @@ pdsch_codeblock_processor::result pdsch_codeblock_processor::process(spanadvance(config.metadata.cb_specific.cw_offset); // Prepare codeblock data. - units::bits nof_used_bits = 0_bits; cb_data.resize(config.cb_size.value()); // Calculate transport block size. @@ -51,7 +51,7 @@ pdsch_codeblock_processor::result pdsch_codeblock_processor::process(span= 1, "Number of bits per resource element must be greater than or equal to 1."); - // Copy codeblock data. - { - bit_buffer message = cb_data.first(config.cb_info_size.value()); - srsvec::copy_offset(message, data, config.tb_offset.value()); - nof_used_bits += units::bits(config.cb_info_size); - } - - // Append transport block CRC if applicable. - if ((config.tb_offset + config.cb_info_size) == tbs) { - constexpr units::bits MAX_BITS_CRC16{3824}; - crc_calculator& tb_crc = (tbs <= MAX_BITS_CRC16) ? *crc16 : *crc24a; - units::bits nof_tb_crc_bits = units::bits(get_crc_size(tb_crc.get_generator_poly())); - - crc_calculator_checksum_t tb_checksum = tb_crc.calculate_byte(data); - for (unsigned i_checksum_byte = 0, i_checksum_byte_end = nof_tb_crc_bits.truncate_to_bytes().value(); - i_checksum_byte != i_checksum_byte_end; - ++i_checksum_byte) { - // Extract byte from the CRC. - unsigned tb_crc_byte = (tb_checksum >> (nof_tb_crc_bits.value() - (i_checksum_byte + 1) * 8)) & 0xffUL; - // Insert the byte at the end of the bit buffer. - cb_data.insert(tb_crc_byte, nof_used_bits.value(), 8); - // Increment the number of bits. - nof_used_bits += 8_bits; - } - - // Insert zero padding bits. - for (units::bits nof_used_bits_end = nof_used_bits + config.zero_pad; nof_used_bits != nof_used_bits_end;) { - // Calculate the number of zeros to pad, no more than a byte at a time. - units::bits nof_zeros = std::min(8_bits, nof_used_bits_end - nof_used_bits); - // Insert the zeros at the end of the bit buffer. - cb_data.insert(0UL, nof_used_bits.value(), nof_zeros.value()); - // Increment the number of bits. - nof_used_bits += nof_zeros; - } - } - - // Append codeblock CRC if applicable. - if (config.has_cb_crc) { - crc_calculator& cb_crc = *crc24b; - - crc_calculator_checksum_t cb_checksum = cb_crc.calculate(cb_data.first(nof_used_bits.value())); - for (unsigned i_checksum_byte = 0, i_checksum_byte_end = 3; i_checksum_byte != i_checksum_byte_end; - ++i_checksum_byte) { - // Extract byte from the CRC. - unsigned cb_crc_byte = (cb_checksum >> (24 - (i_checksum_byte + 1) * 8)) & 0xffUL; - // Insert the byte at the end of the bit buffer. - cb_data.insert(cb_crc_byte, nof_used_bits.value(), 8); - // Increment the number of bits. - nof_used_bits += 8_bits; - } - } - - // Append filler bits as zeros. - while (nof_used_bits != config.cb_size) { - // Calculate the number of zeros to pad, no more than a byte at a time. - units::bits nof_zeros = std::min(8_bits, units::bits(config.cb_size) - nof_used_bits); - // Insert the zeros at the end of the bit buffer. - cb_data.insert(0UL, nof_used_bits.value(), nof_zeros.value()); - // Increment the number of bits. - nof_used_bits += nof_zeros; - } + // Copy codeblock data, including TB and/or CB CRC if applicable, as well as filler and zero padding bits. + segment_buffer.read_codeblock(cb_data, data, config.cb_index); // Encode the segment into a codeblock. const ldpc_encoder_buffer& rm_buffer = encoder->encode(cb_data, config.metadata.tb_common); diff --git a/lib/phy/upper/channel_processors/pdsch/pdsch_codeblock_processor.h b/lib/phy/upper/channel_processors/pdsch/pdsch_codeblock_processor.h index 254e17aac7..651be224d8 100644 --- a/lib/phy/upper/channel_processors/pdsch/pdsch_codeblock_processor.h +++ b/lib/phy/upper/channel_processors/pdsch/pdsch_codeblock_processor.h @@ -25,6 +25,7 @@ #include "srsran/phy/upper/channel_coding/crc_calculator.h" #include "srsran/phy/upper/channel_coding/ldpc/ldpc_encoder.h" #include "srsran/phy/upper/channel_coding/ldpc/ldpc_rate_matcher.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_buffer.h" #include "srsran/phy/upper/channel_modulation/modulation_mapper.h" #include "srsran/phy/upper/sequence_generators/pseudo_random_generator.h" #include "srsran/ran/pdsch/pdsch_constants.h" @@ -52,6 +53,8 @@ class pdsch_codeblock_processor public: /// Collects the parameters necessary for processing a PDSCH codeblock. struct configuration { + /// Index of the codeblock within the transport block. + unsigned cb_index; /// Position of the codeblock relative to the first bit of the transport block. units::bits tb_offset; /// Codeblock number of information bits. @@ -77,27 +80,15 @@ class pdsch_codeblock_processor }; /// Builds a codeblock processor with the required dependencies. - pdsch_codeblock_processor(std::unique_ptr crc24a_, - std::unique_ptr crc24b_, - std::unique_ptr crc16_, - std::unique_ptr encoder_, + pdsch_codeblock_processor(std::unique_ptr encoder_, std::unique_ptr rate_matcher_, std::unique_ptr scrambler_, std::unique_ptr modulator_) : - crc24a(std::move(crc24a_)), - crc24b(std::move(crc24b_)), - crc16(std::move(crc16_)), encoder(std::move(encoder_)), rate_matcher(std::move(rate_matcher_)), scrambler(std::move(scrambler_)), modulator(std::move(modulator_)) { - srsran_assert(crc24a, "Invalid CRC calculator."); - srsran_assert(crc24b, "Invalid CRC calculator."); - srsran_assert(crc16, "Invalid CRC calculator."); - srsran_assert(crc24a->get_generator_poly() == crc_generator_poly::CRC24A, "Invalid CRC calculator."); - srsran_assert(crc24b->get_generator_poly() == crc_generator_poly::CRC24B, "Invalid CRC calculator."); - srsran_assert(crc16->get_generator_poly() == crc_generator_poly::CRC16, "Invalid CRC calculator."); srsran_assert(encoder, "Invalid LDPC encoder."); srsran_assert(rate_matcher, "Invalid LDPC rate matcher."); srsran_assert(scrambler, "Invalid scrambler."); @@ -112,7 +103,7 @@ class pdsch_codeblock_processor /// \param[in] data Original transport block data without CRC. /// \param[in] config Required parameters for processing a codeblock. /// \return A struct with the results. - result process(span data, const configuration& config); + result process(span data, const ldpc_segmenter_buffer& segment_buffer, const configuration& config); /// Gets the QAM modulation scaling, as per TS38.211 Section 5.1. float get_scaling(modulation_scheme modulation) @@ -122,12 +113,6 @@ class pdsch_codeblock_processor } private: - /// Pointer to CRC24A. - std::unique_ptr crc24a; - /// Pointer to CRC24B. - std::unique_ptr crc24b; - /// Pointer to CRC16. - std::unique_ptr crc16; /// Pointer to an LDPC encoder. std::unique_ptr encoder; /// Pointer to an LDPC rate matcher. diff --git a/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_hw_impl.cpp b/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_hw_impl.cpp index 490f16128e..ccb9a9ed73 100644 --- a/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_hw_impl.cpp +++ b/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_hw_impl.cpp @@ -28,9 +28,6 @@ using namespace srsran; using namespace srsran::ldpc; -/// Length of the CRC checksum added to the segments. -static constexpr units::bits SEG_CRC_LENGTH{24}; - void pdsch_encoder_hw_impl::encode(span codeword, span transport_block, const configuration& config) @@ -62,14 +59,15 @@ void pdsch_encoder_hw_impl::encode(span codeword, segmenter_cfg.nof_layers = config.nof_layers; segmenter_cfg.nof_ch_symbols = config.nof_ch_symbols; - if (cb_mode) { - // Clear the buffer. - d_segments.clear(); + // Initialize the segmenter. + segment_buffer = &segmenter->new_transmission(transport_block, segmenter_cfg); - // Segmentation (it includes CRC attachment for the entire transport block and each individual segment). - segmenter->segment(d_segments, transport_block, segmenter_cfg); + units::bits cb_size = segment_buffer->get_segment_length(); + if (cb_mode) { + // Prepare codeblock data. + cb_data.resize(cb_size.value()); } - set_hw_enc_tb_configuration(hw_cfg, transport_block, d_segments, segmenter_cfg); + set_hw_enc_tb_configuration(hw_cfg, transport_block, segmenter_cfg); if (cb_mode) { nof_ops = hw_cfg.nof_segments; } @@ -87,16 +85,17 @@ void pdsch_encoder_hw_impl::encode(span codeword, // Get the data to be encoded. span data; if (cb_mode) { - // Retrieve the CB. - described_segment& descr_seg = d_segments[cb_id]; + // Copy codeblock data, including TB and/or CB CRC if applicable, as well as filler and zero padding bits. + segment_buffer->read_codeblock(cb_data, transport_block, cb_id); // Set the encoding parameters of the CB as required by the hardware-accelerated PDSCH encoder. - set_hw_enc_cb_configuration(hw_cfg, descr_seg.get_metadata(), cb_id); + set_hw_enc_cb_configuration(hw_cfg, segment_buffer->get_cb_metadata(cb_id), cb_id); // Retrieve the current CB to be encoded. - unsigned segment_length = static_cast(descr_seg.get_data().size()) - hw_cfg.nof_filler_bits; + unsigned segment_length = + static_cast(segment_buffer->get_segment_length().value()) - hw_cfg.nof_filler_bits; unsigned segment_length_bytes = static_cast(ceil(static_cast(segment_length) / 8.0)); - data = descr_seg.get_data().get_buffer().first(segment_length_bytes); + data = cb_data.get_buffer().first(segment_length_bytes); } else { // Set the encoding parameters of the CB as required by the hardware-accelerated PDSCH encoder. set_hw_enc_cb_configuration(hw_cfg, {}, cb_id); @@ -136,7 +135,7 @@ void pdsch_encoder_hw_impl::encode(span codeword, unsigned rm_length = 0; span codeblock; if (cb_mode) { - rm_length = d_segments[cb_id].get_metadata().cb_specific.rm_length; + rm_length = segment_buffer->get_rm_length(cb_id); srsran_assert(offset + rm_length <= codeword.size(), "Wrong codeword length."); codeblock = span(codeword).subspan(offset, rm_length); @@ -182,37 +181,9 @@ void pdsch_encoder_hw_impl::encode(span codeword, encoder->free_queue(); } -/// \brief Computes the length of the rate-matched codeblock corresponding to each segment, -/// as per TS38.212 Section 5.4.2.1. -/// \param[in] i_seg Index of the segment. -/// \param[in] nof_segments Number of segments in the transport block. -/// \param[in] nof_short_segments Number of segments that will have a short rate-matched length. -/// \param[in] nof_symbols_per_layer Number of channel symbols assigned to a transmission layer. -/// \param[in] modulation_scheme Modulation scheme. -/// \param[in] nof_layers Number of transmission layers the transport block is mapped onto. -/// \return The length of the rate-matched codeblock corresponding to the indicated segment. -static unsigned compute_rm_length(unsigned i_seg, - unsigned nof_segments, - unsigned nof_short_segments, - unsigned nof_symbols_per_layer, - modulation_scheme mod, - unsigned nof_layers) -{ - unsigned tmp = 0; - if (i_seg < nof_short_segments) { - // For unsigned, division then floor is the same as integer division. - tmp = nof_symbols_per_layer / nof_segments; - } else { - tmp = divide_ceil(nof_symbols_per_layer, nof_segments); - } - return tmp * nof_layers * get_bits_per_symbol(mod); -} - -void pdsch_encoder_hw_impl::set_hw_enc_tb_configuration( - hal::hw_pdsch_encoder_configuration& hw_cfg, - span transport_block, - const static_vector& d_segs, - const segmenter_config& cfg) +void pdsch_encoder_hw_impl::set_hw_enc_tb_configuration(hal::hw_pdsch_encoder_configuration& hw_cfg, + span transport_block, + const segmenter_config& cfg) { using namespace units::literals; @@ -228,34 +199,19 @@ void pdsch_encoder_hw_impl::set_hw_enc_tb_configuration( // Each transport_block entry is a byte, and TBS can always be expressed as an integer number of bytes (see, e.g., // TS38.214 Section 5.1.3.2). units::bits nof_tb_bits = units::bytes(transport_block.size()).to_bits(); - units::bits nof_tb_crc_bits = ldpc::compute_tb_crc_size(nof_tb_bits); + units::bits nof_tb_crc_bits = segment_buffer->get_tb_crc_bits(); hw_cfg.nof_tb_bits = static_cast(nof_tb_bits.value()); hw_cfg.nof_tb_crc_bits = static_cast(nof_tb_crc_bits.value()); // Number of segments. - unsigned nof_segments = 0; - if (cb_mode) { - nof_segments = static_cast(d_segments.size()); - } else { - nof_segments = ldpc::compute_nof_codeblocks(nof_tb_bits, cfg.base_graph); - } - hw_cfg.nof_segments = nof_segments; - - // TB size in bits, accounting for CRC bits. - units::bits nof_tb_bits_out = nof_tb_bits + nof_tb_crc_bits; - uint8_t crc_byte_0 = 0; - uint8_t crc_byte_1 = 0; - uint8_t crc_byte_2 = 0; - if (nof_segments > 1) { - // Internally the CB CRC bits are also accounted for. - nof_tb_bits_out += units::bits(nof_segments * SEG_CRC_LENGTH.value()); - } + unsigned nof_segments = segment_buffer->get_nof_codeblocks(); + hw_cfg.nof_segments = nof_segments; // Compute the TB CRC bits (needed for both FPGA and bbdev). - crc_calculator_checksum_t tb_crc = compute_tb_crc(transport_block, hw_cfg.nof_tb_crc_bits); - crc_byte_0 = (static_cast(tb_crc) >> 16) & 0xff; - crc_byte_1 = (static_cast(tb_crc) >> 8) & 0xff; - crc_byte_2 = static_cast(tb_crc) & 0xff; + crc_calculator_checksum_t tb_crc = compute_tb_crc(transport_block, hw_cfg.nof_tb_crc_bits); + uint8_t crc_byte_0 = (static_cast(tb_crc) >> 16) & 0xff; + uint8_t crc_byte_1 = (static_cast(tb_crc) >> 8) & 0xff; + uint8_t crc_byte_2 = static_cast(tb_crc) & 0xff; if (hw_cfg.nof_tb_crc_bits > 16) { hw_cfg.tb_crc = {crc_byte_0, crc_byte_1, crc_byte_2}; } else { @@ -263,47 +219,29 @@ void pdsch_encoder_hw_impl::set_hw_enc_tb_configuration( hw_cfg.tb_crc = {crc_byte_1, crc_byte_2}; } - // Number of channel symbols assigned to a transmission layer. - unsigned nof_symbols_per_layer = cfg.nof_ch_symbols / cfg.nof_layers; // Number of segments that will have a short rate-matched length. In TS38.212 Section 5.4.2.1, these correspond to // codeblocks whose length E_r is computed by rounding down - floor. For the remaining codewords, the length is // rounded up. - unsigned nof_short_segments = nof_segments - (nof_symbols_per_layer % nof_segments); + unsigned nof_short_segments = segment_buffer->get_nof_short_segments(); hw_cfg.nof_short_segments = nof_short_segments; // Codeword length for short rate-matched segments in bits (Ea). The FPGA will insert the CB CRC, thus the CB size // needs to be adjusted. unsigned cw_length_a = 0; - codeblock_metadata common_cfg; - if (cb_mode) { - common_cfg = d_segments[0].get_metadata(); - cw_length_a = common_cfg.cb_specific.rm_length; - } else { - cw_length_a = - compute_rm_length(0, nof_segments, nof_short_segments, nof_symbols_per_layer, cfg.mod, cfg.nof_layers); - } - hw_cfg.cw_length_a = cw_length_a; + codeblock_metadata common_cfg = segment_buffer->get_cb_metadata(0); + cw_length_a = segment_buffer->get_rm_length(0); + hw_cfg.cw_length_a = cw_length_a; // Codeword length for long rate-matched segments in bits (Eb). The FPGA will insert the CB CRC, thus the CB size // needs to be adjusted. unsigned cw_length_b = cw_length_a; if (nof_segments > nof_short_segments) { - if (cb_mode) { - cw_length_b = d_segments[nof_short_segments].get_metadata().cb_specific.rm_length; - } else { - cw_length_b = compute_rm_length( - nof_short_segments, nof_segments, nof_short_segments, nof_symbols_per_layer, cfg.mod, cfg.nof_layers); - } + cw_length_b = segment_buffer->get_rm_length(nof_short_segments); } hw_cfg.cw_length_b = cw_length_b; // LDPC lifting size. - unsigned lifting_size = 0; - if (cb_mode) { - lifting_size = common_cfg.tb_common.lifting_size; - } else { - lifting_size = ldpc::compute_lifting_size(nof_tb_bits, cfg.base_graph, nof_segments); - } - hw_cfg.lifting_size = lifting_size; + unsigned lifting_size = common_cfg.tb_common.lifting_size; + hw_cfg.lifting_size = lifting_size; // Base graph index and length of the circular buffer in bits, as described in TS38.212 Section 5.4.2.1. unsigned N = 0; @@ -316,17 +254,13 @@ void pdsch_encoder_hw_impl::set_hw_enc_tb_configuration( hw_cfg.Ncb = N; hw_cfg.Nref = cfg.Nref; - // Number of CRC bits per segment. - units::bits nof_crc_bits = (nof_segments > 1) ? SEG_CRC_LENGTH : 0_bits; - // Number of information bits that is assigned to a segment. - units::bits cb_info_bits = units::bits(divide_ceil(nof_tb_bits_out.value(), nof_segments)) - nof_crc_bits; + units::bits cb_info_bits = segment_buffer->get_cb_info_bits(0); hw_cfg.nof_segment_bits = static_cast(cb_info_bits.value()); if (!cb_mode) { // Number of filler bits. - units::bits segment_length = ldpc::compute_codeblock_size(cfg.base_graph, lifting_size); - units::bits nof_filler_bits = segment_length - cb_info_bits - nof_crc_bits; + units::bits nof_filler_bits = segment_buffer->get_nof_filler_bits(); hw_cfg.nof_filler_bits = static_cast(nof_filler_bits.value()); } } diff --git a/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_hw_impl.h b/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_hw_impl.h index 5e5ecd6ee4..ebc00dfb64 100644 --- a/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_hw_impl.h +++ b/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_hw_impl.h @@ -28,6 +28,7 @@ #include "srsran/hal/hw_accelerator.h" #include "srsran/hal/phy/upper/channel_processors/hw_accelerator_pdsch_enc.h" #include "srsran/phy/upper/channel_coding/crc_calculator.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_buffer.h" #include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx.h" #include "srsran/phy/upper/channel_processors/pdsch/pdsch_encoder.h" #include "srsran/ran/pdsch/pdsch_constants.h" @@ -98,8 +99,13 @@ class pdsch_encoder_hw_impl : public pdsch_encoder /// Pointer to a Hardware-accelerated PDSCH encoder. std::unique_ptr encoder; - /// Buffer for storing data segments obtained after transport block segmentation. - static_vector d_segments = {}; + /// Pointer to an LDPC segmenter output buffer interface. + const ldpc_segmenter_buffer* segment_buffer; + + /// \brief Temporary codeblock message. + /// + /// It contains codeblock information bits, codeblock CRC (if applicable) and filler bits. + static_bit_buffer cb_data; /// Buffer for storing temporary encoded and packed codeblock. static_vector codeblock_packed; @@ -109,10 +115,9 @@ class pdsch_encoder_hw_impl : public pdsch_encoder /// \param[in] transport_block The transport block to encode (packed, one byte per entry). /// \param[in] d_segs Buffer storing the data segments comprising the transport block. /// \param[in] cfg PDSCH configuration parameters. - void set_hw_enc_tb_configuration(hal::hw_pdsch_encoder_configuration& hw_cfg, - span transport_block, - const static_vector& d_segs, - const segmenter_config& cfg); + void set_hw_enc_tb_configuration(hal::hw_pdsch_encoder_configuration& hw_cfg, + span transport_block, + const segmenter_config& cfg); /// \brief Computes the segmentation parameters required by the hardware-accelerated PDSCH encoder function. /// \param[out] hw_cfg Hardware-accelerated PDSCH encoder configuration parameters. diff --git a/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_impl.cpp b/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_impl.cpp index 95e3c9e321..c31a3a68b8 100644 --- a/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_impl.cpp +++ b/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_impl.cpp @@ -29,9 +29,6 @@ void pdsch_encoder_impl::encode(span codeword, span transport_block, const configuration& config) { - // Clear the buffer. - d_segments.clear(); - segmenter_config segmenter_cfg; segmenter_cfg.base_graph = config.base_graph; segmenter_cfg.rv = config.rv; @@ -40,25 +37,32 @@ void pdsch_encoder_impl::encode(span codeword, segmenter_cfg.nof_layers = config.nof_layers; segmenter_cfg.nof_ch_symbols = config.nof_ch_symbols; - // Segmentation (it includes CRC attachment for the entire transport block and each individual segment). - segmenter->segment(d_segments, transport_block, segmenter_cfg); + // Initialize the segmenter. + const ldpc_segmenter_buffer& segment_buffer = segmenter->new_transmission(transport_block, segmenter_cfg); + + // Prepare codeblock data. + units::bits cb_size = segment_buffer.get_segment_length(); + cb_data.resize(cb_size.value()); unsigned offset = 0; - for (unsigned i_cb = 0, i_cb_end = d_segments.size(); i_cb != i_cb_end; ++i_cb) { - // Select segment description. - const described_segment& descr_seg = d_segments[i_cb]; + for (unsigned i_cb = 0, i_cb_end = segment_buffer.get_nof_codeblocks(); i_cb != i_cb_end; ++i_cb) { + // Retrieve segment description. + const codeblock_metadata cb_metadata = segment_buffer.get_cb_metadata(i_cb); + + // Copy codeblock data, including TB and/or CB CRC if applicable, as well as filler and zero padding bits. + segment_buffer.read_codeblock(cb_data, transport_block, i_cb); // Encode the segment into a codeblock. - const ldpc_encoder_buffer& rm_buffer = encoder->encode(descr_seg.get_data(), descr_seg.get_metadata().tb_common); + const ldpc_encoder_buffer& rm_buffer = encoder->encode(cb_data, cb_metadata.tb_common); // Select the correct chunk of the output codeword. - unsigned rm_length = descr_seg.get_metadata().cb_specific.rm_length; + unsigned rm_length = segment_buffer.get_rm_length(i_cb); srsran_assert(offset + rm_length <= codeword.size(), "Wrong codeword length."); span codeblock = span(codeword).subspan(offset, rm_length); // Rate match the codeblock. codeblock_packed.resize(rm_length); - rate_matcher->rate_match(codeblock_packed, rm_buffer, descr_seg.get_metadata()); + rate_matcher->rate_match(codeblock_packed, rm_buffer, cb_metadata); // Unpack code block. srsvec::bit_unpack(codeblock, codeblock_packed); diff --git a/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_impl.h b/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_impl.h index 44391b2282..d59e76d75f 100644 --- a/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_impl.h +++ b/lib/phy/upper/channel_processors/pdsch/pdsch_encoder_impl.h @@ -27,6 +27,7 @@ #include "srsran/phy/upper/channel_coding/ldpc/ldpc_encoder.h" #include "srsran/phy/upper/channel_coding/ldpc/ldpc_rate_matcher.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_buffer.h" #include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx.h" #include "srsran/phy/upper/channel_processors/pdsch/pdsch_encoder.h" #include "srsran/phy/upper/codeblock_metadata.h" @@ -63,8 +64,10 @@ class pdsch_encoder_impl : public pdsch_encoder /// Pointer to an LDPC rate matcher. std::unique_ptr rate_matcher; - /// Buffer for storing data segments obtained after transport block segmentation. - static_vector d_segments; + /// \brief Temporary codeblock message. + /// + /// It contains codeblock information bits, codeblock CRC (if applicable) and filler bits. + static_bit_buffer cb_data; /// \brief Maximum codeblock length. /// /// This is the maximum length of an encoded codeblock, achievable with base graph 1 (rate 1/3). diff --git a/lib/phy/upper/channel_processors/pdsch/pdsch_processor_concurrent_impl.cpp b/lib/phy/upper/channel_processors/pdsch/pdsch_processor_concurrent_impl.cpp index b60f699651..c85accbbc7 100644 --- a/lib/phy/upper/channel_processors/pdsch/pdsch_processor_concurrent_impl.cpp +++ b/lib/phy/upper/channel_processors/pdsch/pdsch_processor_concurrent_impl.cpp @@ -142,52 +142,39 @@ void pdsch_processor_concurrent_impl::save_inputs(resource_grid_writer& grid // Calculate number of codeblocks. nof_cb = ldpc::compute_nof_codeblocks(tbs, config.ldpc_base_graph); - // Number of segments that will have a short rate-matched length. In TS38.212 Section 5.4.2.1, these correspond to - // codeblocks whose length E_r is computed by rounding down - floor. For the remaining codewords, the length is - // rounded up. - unsigned nof_short_segments = nof_cb - (nof_re_pdsch % nof_cb); - - // Compute number of CRC bits for the transport block. - units::bits nof_tb_crc_bits = ldpc::compute_tb_crc_size(tbs); + modulation_scheme modulation = config.codewords.front().modulation; + units::bits bits_per_symbol(get_bits_per_symbol(modulation)); - // Compute number of CRC bits for each codeblock. - units::bits nof_cb_crc_bits = (nof_cb > 1) ? 24_bits : 0_bits; + // Calculate rate match buffer size. + units::bits Nref = ldpc::compute_N_ref(config.tbs_lbrm, nof_cb); - // Calculate the total number of bits including transport block and codeblock CRC. - units::bits nof_tb_bits_out = tbs + nof_tb_crc_bits + units::bits(nof_cb_crc_bits * nof_cb); + // Initialize the segmenter. + segmenter_config segmenter_cfg; + segmenter_cfg.base_graph = config.ldpc_base_graph; + segmenter_cfg.rv = config.codewords.front().rv; + segmenter_cfg.mod = modulation; + segmenter_cfg.Nref = Nref.value(); + segmenter_cfg.nof_layers = nof_layers; + segmenter_cfg.nof_ch_symbols = nof_ch_symbols; - // Compute the number of information bits that is assigned to a codeblock. - cb_info_bits = units::bits(divide_ceil(nof_tb_bits_out.value(), nof_cb)) - nof_cb_crc_bits; + // Initialize the segmenter. + segment_buffer = &segmenter->new_transmission(data.get_buffer(), segmenter_cfg); - unsigned lifting_size = ldpc::compute_lifting_size(tbs, config.ldpc_base_graph, nof_cb); - segment_length = ldpc::compute_codeblock_size(config.ldpc_base_graph, lifting_size); + // Retrieve the segment length. + segment_length = segment_buffer->get_segment_length(); - modulation_scheme modulation = config.codewords.front().modulation; - units::bits bits_per_symbol(get_bits_per_symbol(modulation)); + // Number of segments that will have a short rate-matched length. In TS38.212 Section 5.4.2.1, these correspond to + // codeblocks whose length E_r is computed by rounding down - floor. For the remaining codewords, the length is + // rounded up. + unsigned nof_short_segments = segment_buffer->get_nof_short_segments(); - units::bits full_codeblock_size = ldpc::compute_full_codeblock_size(config.ldpc_base_graph, segment_length); - units::bits cw_length = units::bits(nof_re_pdsch * nof_layers * bits_per_symbol); - zero_pad = (cb_info_bits + nof_cb_crc_bits) * nof_cb - nof_tb_bits_out; + // Compute the number of information bits that is assigned to a codeblock. + cb_info_bits = segment_buffer->get_cb_info_bits(0); - // Calculate rate match buffer size. - units::bits Nref = ldpc::compute_N_ref(config.tbs_lbrm, nof_cb); + units::bits cw_length = segment_buffer->get_cw_length(); + zero_pad = segment_buffer->get_zero_pad(); - // Prepare codeblock metadata. - cb_metadata.tb_common.base_graph = config.ldpc_base_graph; - cb_metadata.tb_common.lifting_size = static_cast(lifting_size); - cb_metadata.tb_common.rv = config.codewords.front().rv; - cb_metadata.tb_common.mod = modulation; - cb_metadata.tb_common.Nref = Nref.value(); - cb_metadata.tb_common.cw_length = cw_length.value(); - cb_metadata.cb_specific.full_length = full_codeblock_size.value(); - cb_metadata.cb_specific.rm_length = 0; - cb_metadata.cb_specific.nof_filler_bits = (segment_length - cb_info_bits - nof_cb_crc_bits).value(); - cb_metadata.cb_specific.cw_offset = 0; - cb_metadata.cb_specific.nof_crc_bits = nof_tb_crc_bits.value(); - - // Calculate RM length for each codeblock. - rm_length.resize(nof_cb); - cw_offset.resize(nof_cb); + // Calculate RE offset for each codeblock. re_offset.resize(nof_cb); unsigned re_count_sum = 0; for (unsigned i_cb = 0; i_cb != nof_cb; ++i_cb) { @@ -197,12 +184,6 @@ void pdsch_processor_concurrent_impl::save_inputs(resource_grid_writer& grid rm_length_re = nof_re_pdsch / nof_cb; } - // Convert RM length from RE to bits. - rm_length[i_cb] = rm_length_re * nof_layers * bits_per_symbol; - - // Set and increment CW offset. - cw_offset[i_cb] = re_count_sum * nof_layers * bits_per_symbol; - // Set RE offset for the resource mapper. re_offset[i_cb] = re_count_sum; @@ -288,24 +269,21 @@ void pdsch_processor_concurrent_impl::fork_cb_batches() absolute_i_cb = nof_cb - 1 - absolute_i_cb; // Limit the codeblock number of information bits. - units::bits nof_info_bits = std::min(cb_info_bits, tbs - cb_info_bits * absolute_i_cb); + units::bits nof_info_bits = segment_buffer->get_cb_info_bits(absolute_i_cb); // Set CB processor configuration. pdsch_codeblock_processor::configuration cb_config; + cb_config.cb_index = absolute_i_cb; cb_config.tb_offset = cb_info_bits * absolute_i_cb; cb_config.has_cb_crc = nof_cb > 1; cb_config.cb_info_size = nof_info_bits; cb_config.cb_size = segment_length; cb_config.zero_pad = zero_pad; - cb_config.metadata = cb_metadata; + cb_config.metadata = segment_buffer->get_cb_metadata(absolute_i_cb); cb_config.c_init = scrambler->get_state(); - // Update codeblock specific metadata fields. - cb_config.metadata.cb_specific.cw_offset = cw_offset[absolute_i_cb].value(); - cb_config.metadata.cb_specific.rm_length = rm_length[absolute_i_cb].value(); - // Process codeblock. - pdsch_codeblock_processor::result result = cb_processor.process(data.get_buffer(), cb_config); + pdsch_codeblock_processor::result result = cb_processor.process(data.get_buffer(), *segment_buffer, cb_config); // Build resource grid mapper adaptor. resource_grid_mapper::symbol_buffer_adapter buffer(result.cb_symbols); diff --git a/lib/phy/upper/channel_processors/pdsch/pdsch_processor_concurrent_impl.h b/lib/phy/upper/channel_processors/pdsch/pdsch_processor_concurrent_impl.h index 3f0805bf8e..349759bea6 100644 --- a/lib/phy/upper/channel_processors/pdsch/pdsch_processor_concurrent_impl.h +++ b/lib/phy/upper/channel_processors/pdsch/pdsch_processor_concurrent_impl.h @@ -23,6 +23,8 @@ #include "pdsch_codeblock_processor.h" #include "srsran/phy/support/resource_grid_mapper.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_buffer.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx.h" #include "srsran/phy/upper/channel_processors/pdsch/pdsch_processor.h" #include "srsran/phy/upper/sequence_generators/pseudo_random_generator.h" #include "srsran/phy/upper/signal_processors/dmrs_pdsch_processor.h" @@ -46,16 +48,19 @@ class pdsch_processor_concurrent_impl : public pdsch_processor using pdsch_ptrs_generator_pool = concurrent_thread_local_object_pool; /// \brief Creates a concurrent PDSCH processor with all the dependencies. + /// \param[in] segmenter_ LDPC segmenter. /// \param[in] cb_processor_pool_ Codeblock processor pool. /// \param[in] scrambler_ Scrambling pseudo-random generator. /// \param[in] dmrs_generator_pool_ DM-RS for PDSCH generator. /// \param[in] executor_ Asynchronous task executor. - pdsch_processor_concurrent_impl(std::shared_ptr cb_processor_pool_, + pdsch_processor_concurrent_impl(std::unique_ptr segmenter_, + std::shared_ptr cb_processor_pool_, std::unique_ptr scrambler_, std::unique_ptr mapper_, std::shared_ptr dmrs_generator_pool_, std::shared_ptr ptrs_generator_pool_, task_executor& executor_) : + segmenter(std::move(segmenter_)), scrambler(std::move(scrambler_)), mapper(std::move(mapper_)), cb_processor_pool(std::move(cb_processor_pool_)), @@ -63,6 +68,7 @@ class pdsch_processor_concurrent_impl : public pdsch_processor ptrs_generator_pool(std::move(ptrs_generator_pool_)), executor(executor_) { + srsran_assert(segmenter, "Invalid LDPC segmenter."); srsran_assert(scrambler, "Invalid scrambler pointer."); srsran_assert(cb_processor_pool, "Invalid CB processor pool pointer."); srsran_assert(dmrs_generator_pool, "Invalid DM-RS pointer."); @@ -85,6 +91,8 @@ class pdsch_processor_concurrent_impl : public pdsch_processor /// Creates code block processing batches and starts the asynchronous processing. void fork_cb_batches(); + /// Pointer to an LDPC segmenter. + std::unique_ptr segmenter; /// Pseudo-random generator. std::unique_ptr scrambler; /// Resource grid mapper. @@ -97,6 +105,8 @@ class pdsch_processor_concurrent_impl : public pdsch_processor std::shared_ptr ptrs_generator_pool; /// Asynchronous task executor. task_executor& executor; + /// Pointer to an LDPC segmenter output buffer interface. + const ldpc_segmenter_buffer* segment_buffer; resource_grid_writer* grid; pdsch_processor_notifier* notifier; @@ -123,10 +133,6 @@ class pdsch_processor_concurrent_impl : public pdsch_processor re_pattern_list reserved; /// Precoding configuration scaled. precoding_configuration precoding; - /// Rate matching length in bits for each of the segments. - static_vector rm_length; - /// Codeblock bit offset within the codeword. - static_vector cw_offset; /// Codeblock resource block offset. static_vector re_offset; /// Codeblock counter. diff --git a/lib/phy/upper/channel_processors/pdsch/pdsch_processor_lite_impl.cpp b/lib/phy/upper/channel_processors/pdsch/pdsch_processor_lite_impl.cpp index a7ea10b764..de6ec89e2b 100644 --- a/lib/phy/upper/channel_processors/pdsch/pdsch_processor_lite_impl.cpp +++ b/lib/phy/upper/channel_processors/pdsch/pdsch_processor_lite_impl.cpp @@ -69,38 +69,46 @@ void pdsch_block_processor::configure_new_transmission(span encoder_config.nof_layers = nof_layers; encoder_config.nof_ch_symbols = nof_re_pdsch * nof_layers; - // Clear the buffer. - d_segments.clear(); + // Initialize the segmenter. + segment_buffer = &segmenter.new_transmission(data, encoder_config); - // Segmentation (it includes CRC attachment for the entire transport block and each individual segment). - segmenter.segment(d_segments, data, encoder_config); + cb_size = segment_buffer->get_segment_length(); // Initialize the codeblock counter. next_i_cb = 0; + + // Save the pointer to the input data. + transport_block = data; } void pdsch_block_processor::new_codeblock() { - srsran_assert(next_i_cb < d_segments.size(), + srsran_assert(next_i_cb < segment_buffer->get_nof_codeblocks(), "The codeblock index (i.e., {}) exceeds the number of codeblocks (i.e., {})", next_i_cb, - d_segments.size()); + segment_buffer->get_nof_codeblocks()); + + // Prepare codeblock data. + cb_data.resize(cb_size.value()); - // Select segment description. - const described_segment& descr_seg = d_segments[next_i_cb]; + // Retrieve segment description. + const codeblock_metadata cb_metadata = segment_buffer->get_cb_metadata(next_i_cb); // Rate Matching output length. - unsigned rm_length = descr_seg.get_metadata().cb_specific.rm_length; + unsigned rm_length = segment_buffer->get_rm_length(next_i_cb); // Number of symbols. unsigned nof_symbols = rm_length / get_bits_per_symbol(modulation); + // Copy codeblock data, including TB and/or CB CRC if applicable, as well as filler and zero padding bits. + segment_buffer->read_codeblock(cb_data, transport_block, next_i_cb); + // Encode the segment into a codeblock. - const ldpc_encoder_buffer& rm_buffer = encoder.encode(descr_seg.get_data(), descr_seg.get_metadata().tb_common); + const ldpc_encoder_buffer& rm_buffer = encoder.encode(cb_data, cb_metadata.tb_common); // Rate match the codeblock. temp_codeblock.resize(rm_length); - rate_matcher.rate_match(temp_codeblock, rm_buffer, descr_seg.get_metadata()); + rate_matcher.rate_match(temp_codeblock, rm_buffer, cb_metadata); // Apply scrambling sequence in-place. scrambler.apply_xor(temp_codeblock, temp_codeblock); @@ -140,8 +148,8 @@ unsigned pdsch_block_processor::get_max_block_size() const return codeblock_symbols.size(); } - if (next_i_cb != d_segments.size()) { - unsigned rm_length = d_segments[next_i_cb].get_metadata().cb_specific.rm_length; + if (next_i_cb != segment_buffer->get_nof_codeblocks()) { + unsigned rm_length = segment_buffer->get_rm_length(next_i_cb); unsigned bits_per_symbol = get_bits_per_symbol(modulation); return rm_length / bits_per_symbol; } @@ -151,7 +159,7 @@ unsigned pdsch_block_processor::get_max_block_size() const bool pdsch_block_processor::empty() const { - return codeblock_symbols.empty() && (next_i_cb == d_segments.size()); + return codeblock_symbols.empty() && (next_i_cb == segment_buffer->get_nof_codeblocks()); } void pdsch_processor_lite_impl::process(resource_grid_writer& grid, diff --git a/lib/phy/upper/channel_processors/pdsch/pdsch_processor_lite_impl.h b/lib/phy/upper/channel_processors/pdsch/pdsch_processor_lite_impl.h index 7d4b25ec6a..77394e1daf 100644 --- a/lib/phy/upper/channel_processors/pdsch/pdsch_processor_lite_impl.h +++ b/lib/phy/upper/channel_processors/pdsch/pdsch_processor_lite_impl.h @@ -25,6 +25,7 @@ #include "srsran/phy/support/resource_grid_mapper.h" #include "srsran/phy/upper/channel_coding/ldpc/ldpc_encoder.h" #include "srsran/phy/upper/channel_coding/ldpc/ldpc_rate_matcher.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_buffer.h" #include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_tx.h" #include "srsran/phy/upper/channel_modulation/modulation_mapper.h" #include "srsran/phy/upper/channel_processors/pdsch/pdsch_processor.h" @@ -79,8 +80,12 @@ class pdsch_block_processor : public resource_grid_mapper::symbol_buffer pseudo_random_generator& scrambler; /// Modulation mapper. modulation_mapper& modulator; - /// Buffer for storing data segments obtained after transport block segmentation. - static_vector d_segments; + /// Pointer to an LDPC segmenter output buffer interface. + const ldpc_segmenter_buffer* segment_buffer; + /// \brief Temporary codeblock message. + /// + /// It contains codeblock information bits, codeblock CRC (if applicable) and filler bits. + static_bit_buffer cb_data; /// Temporary packed bits. static_bit_buffer temp_codeblock; /// Current transmission modulation. @@ -91,6 +96,10 @@ class pdsch_block_processor : public resource_grid_mapper::symbol_buffer std::array temp_codeblock_symbols; /// Current view of the codeblock modulated symbols. span codeblock_symbols; + /// Codeblock size. It includes information, CRC, and filler bits. + units::bits cb_size; + /// Local pointer to the input data. + span transport_block; /// Processes a new codeblock and writes the new data in \ref temp_codeblock_symbols. void new_codeblock(); diff --git a/lib/phy/upper/channel_processors/prach_detector_generic_impl.cpp b/lib/phy/upper/channel_processors/prach_detector_generic_impl.cpp index cd06f11682..91576c1946 100644 --- a/lib/phy/upper/channel_processors/prach_detector_generic_impl.cpp +++ b/lib/phy/upper/channel_processors/prach_detector_generic_impl.cpp @@ -41,8 +41,6 @@ using namespace srsran; -static const detail::threshold_and_margin_finder threshold_and_margin_table(detail::all_threshold_and_margins); - error_type prach_detector_validator_impl::is_valid(const prach_detector::configuration& config) const { return validate_prach_detector_phy(config.format, config.ra_scs, config.zero_correlation_zone, config.nof_rx_ports); @@ -148,7 +146,7 @@ prach_detection_result prach_detector_generic_impl::detect(const prach_buffer& i th_params.zero_correlation_zone = config.zero_correlation_zone; th_params.combine_symbols = combine_symbols; - auto th_and_margin = threshold_and_margin_table.get(th_params); + auto th_and_margin = detail::get_threshold_and_margin(th_params); float threshold = std::get<0>(th_and_margin); unsigned win_margin = std::get<1>(th_and_margin); srsran_assert((win_margin > 0) && (threshold > 0.0), diff --git a/lib/phy/upper/channel_processors/prach_detector_generic_thresholds.cpp b/lib/phy/upper/channel_processors/prach_detector_generic_thresholds.cpp new file mode 100644 index 0000000000..103c8cb617 --- /dev/null +++ b/lib/phy/upper/channel_processors/prach_detector_generic_thresholds.cpp @@ -0,0 +1,717 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include "prach_detector_generic_thresholds.h" +#include "srsran/adt/span.h" +#include "srsran/adt/to_array.h" +#include "srsran/ran/prach/prach_format_type.h" +#include "srsran/ran/prach/prach_subcarrier_spacing.h" + +using namespace srsran; +using namespace detail; + +namespace { + +/// Overloaded less-than comparison operator. +inline bool operator<(const threshold_params& lhs, const threshold_params& rhs) +{ + if (lhs.nof_rx_ports < rhs.nof_rx_ports) { + return true; + } + if (lhs.nof_rx_ports > rhs.nof_rx_ports) { + return false; + } + + // If we are here, nof_rx_ports is equal. + if (lhs.scs < rhs.scs) { + return true; + } + if (lhs.scs > rhs.scs) { + return false; + } + + // If we are here, nof_rx_ports and scs are equal. + if (lhs.format < rhs.format) { + return true; + } + if (lhs.format > rhs.format) { + return false; + } + + // If we are here, nof_rx_ports, scs and format are equal. + if (lhs.zero_correlation_zone < rhs.zero_correlation_zone) { + return true; + } + if (lhs.zero_correlation_zone > rhs.zero_correlation_zone) { + return false; + } + + // If we are here, nof_rx_ports, scs, format and zero_correlation_zone are equal. + if (!lhs.combine_symbols && rhs.combine_symbols) { + return true; + } + return false; +} + +/// Overloaded equal-to comparison operator. +inline bool operator==(const threshold_params& lhs, const threshold_params& rhs) +{ + if (lhs.nof_rx_ports != rhs.nof_rx_ports) { + return false; + } + + // If we are here, nof_rx_ports is equal. + if (lhs.scs != rhs.scs) { + return false; + } + + // If we are here, nof_rx_ports and scs are equal. + if (lhs.format != rhs.format) { + return false; + } + + // If we are here, nof_rx_ports, scs and format are equal. + if (lhs.zero_correlation_zone != rhs.zero_correlation_zone) { + return false; + } + + // If we are here, nof_rx_ports, scs, format and zero_correlation_zone are equal. + if (lhs.combine_symbols != rhs.combine_symbols) { + return false; + } + return true; +} + +/// Manages the mapping between PRACH configuration and the (threshold, margin) pairs. +class threshold_and_margin_finder +{ +public: + /// Mapping between PRACH configuration and threshold value. + struct threshold_entry { + /// Subset of PRACH configuration parameters affecting the threshold value. + threshold_params configuration; + /// Threshold value and search margin. + threshold_and_margin_type threshold_and_margin; + /// Threshold quality flag. + threshold_flag flag; + }; + + /// Constructor: receives the list of pairings and ensures it is sorted. + explicit threshold_and_margin_finder(span in) : + sorted_thresholds_and_margins(in.begin(), in.end()) + { + std::sort(sorted_thresholds_and_margins.begin(), + sorted_thresholds_and_margins.end(), + [](const threshold_entry& a, const threshold_entry& b) { return (a.configuration < b.configuration); }); + } + + /// Retrieves the (threshold, margin) pair corresponding to the given configuration. + threshold_and_margin_type get(const threshold_params& params) const + { + auto it = + std::lower_bound(sorted_thresholds_and_margins.begin(), + sorted_thresholds_and_margins.end(), + params, + [](const threshold_entry& a, const threshold_params& b) { return (a.configuration < b); }); + + if (it != sorted_thresholds_and_margins.end()) { + return it->threshold_and_margin; + } + + // todo(david): once all cases are covered, replace this by a srsran_assert. + if (is_long_preamble(params.format)) { + return {/* threshold */ 2.0F, /* margin */ 5}; + } + return {/* threshold */ 0.3F, /* margin */ 12}; + } + + /// Checks the quality flag of the threshold for the given configurations. + threshold_flag check_flag(const threshold_params& params) const + { + auto it = + std::lower_bound(sorted_thresholds_and_margins.begin(), + sorted_thresholds_and_margins.end(), + params, + [](const threshold_entry& a, const threshold_params& b) { return (a.configuration < b); }); + + if ((it != sorted_thresholds_and_margins.end()) && (it->configuration == params)) { + return it->flag; + } + return threshold_flag::red; + } + +private: + /// Sorted list of thresholds. + std::vector sorted_thresholds_and_margins; +}; + +using th_flag = threshold_flag; + +} // namespace + +/// Unsorted list of thresholds. +static const auto all_threshold_and_margins = to_array({ + // clang-format off + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 0, /* combine symbols */ true}, {0.147F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 2, /* combine symbols */ true}, {0.874F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 3, /* combine symbols */ true}, {0.774F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 4, /* combine symbols */ true}, {0.640F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 5, /* combine symbols */ true}, {0.551F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 6, /* combine symbols */ true}, {0.449F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 7, /* combine symbols */ true}, {0.388F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 8, /* combine symbols */ true}, {0.321F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 9, /* combine symbols */ true}, {0.255F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 10, /* combine symbols */ true}, {0.205F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 11, /* combine symbols */ true}, {0.167F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 12, /* combine symbols */ true}, {0.147F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 13, /* combine symbols */ true}, {0.148F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 14, /* combine symbols */ true}, {0.148F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 15, /* combine symbols */ true}, {0.148F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 0, /* combine symbols */ true}, {0.085F, 5}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 1, /* combine symbols */ true}, {0.522F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 2, /* combine symbols */ true}, {0.451F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 3, /* combine symbols */ true}, {0.404F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 4, /* combine symbols */ true}, {0.344F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 5, /* combine symbols */ true}, {0.301F, 5}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 6, /* combine symbols */ true}, {0.249F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 7, /* combine symbols */ true}, {0.219F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 8, /* combine symbols */ true}, {0.180F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 9, /* combine symbols */ true}, {0.146F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 10, /* combine symbols */ true}, {0.118F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 11, /* combine symbols */ true}, {0.098F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 12, /* combine symbols */ true}, {0.085F, 5}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 13, /* combine symbols */ true}, {0.086F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 14, /* combine symbols */ true}, {0.085F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 15, /* combine symbols */ true}, {0.085F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 0, /* combine symbols */ true}, {0.053F, 5}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 1, /* combine symbols */ true}, {0.307F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 2, /* combine symbols */ true}, {0.269F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 3, /* combine symbols */ true}, {0.240F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 4, /* combine symbols */ true}, {0.207F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 5, /* combine symbols */ true}, {0.180F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 6, /* combine symbols */ true}, {0.149F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 7, /* combine symbols */ true}, {0.131F, 5}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 8, /* combine symbols */ true}, {0.112F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 9, /* combine symbols */ true}, {0.090F, 5}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 10, /* combine symbols */ true}, {0.072F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 11, /* combine symbols */ true}, {0.060F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 12, /* combine symbols */ true}, {0.052F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 13, /* combine symbols */ true}, {0.052F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 14, /* combine symbols */ true}, {0.052F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 15, /* combine symbols */ true}, {0.053F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 0, /* combine symbols */ true}, {0.025F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 2, /* combine symbols */ true}, {0.869F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 3, /* combine symbols */ true}, {0.781F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 4, /* combine symbols */ true}, {0.636F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 5, /* combine symbols */ true}, {0.548F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 6, /* combine symbols */ true}, {0.455F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 7, /* combine symbols */ true}, {0.387F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 8, /* combine symbols */ true}, {0.328F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 9, /* combine symbols */ true}, {0.256F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 10, /* combine symbols */ true}, {0.205F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 11, /* combine symbols */ true}, {0.169F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 12, /* combine symbols */ true}, {0.134F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 13, /* combine symbols */ true}, {0.097F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 14, /* combine symbols */ true}, {0.060F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 15, /* combine symbols */ true}, {0.041F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 0, /* combine symbols */ true}, {0.014F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 1, /* combine symbols */ true}, {0.510F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 2, /* combine symbols */ true}, {0.459F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 3, /* combine symbols */ true}, {0.409F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 4, /* combine symbols */ true}, {0.341F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 5, /* combine symbols */ true}, {0.299F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 6, /* combine symbols */ true}, {0.250F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 7, /* combine symbols */ true}, {0.213F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 8, /* combine symbols */ true}, {0.183F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 9, /* combine symbols */ true}, {0.145F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 10, /* combine symbols */ true}, {0.118F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 11, /* combine symbols */ true}, {0.097F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 12, /* combine symbols */ true}, {0.078F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 13, /* combine symbols */ true}, {0.057F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 14, /* combine symbols */ true}, {0.035F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 15, /* combine symbols */ true}, {0.024F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 0, /* combine symbols */ true}, {0.009F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 1, /* combine symbols */ true}, {0.304F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 2, /* combine symbols */ true}, {0.269F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 3, /* combine symbols */ true}, {0.242F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 4, /* combine symbols */ true}, {0.206F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 5, /* combine symbols */ true}, {0.180F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 6, /* combine symbols */ true}, {0.150F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 7, /* combine symbols */ true}, {0.132F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 8, /* combine symbols */ true}, {0.112F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 9, /* combine symbols */ true}, {0.090F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 10, /* combine symbols */ true}, {0.072F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 11, /* combine symbols */ true}, {0.060F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 12, /* combine symbols */ true}, {0.048F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 13, /* combine symbols */ true}, {0.035F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 14, /* combine symbols */ true}, {0.022F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 15, /* combine symbols */ true}, {0.015F, 5}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 0, /* combine symbols */ true}, {0.100F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 1, /* combine symbols */ true}, {0.996F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 2, /* combine symbols */ true}, {0.882F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 3, /* combine symbols */ true}, {0.772F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 4, /* combine symbols */ true}, {0.658F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 5, /* combine symbols */ true}, {0.551F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 6, /* combine symbols */ true}, {0.450F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 7, /* combine symbols */ true}, {0.387F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 8, /* combine symbols */ true}, {0.329F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 9, /* combine symbols */ true}, {0.258F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 10, /* combine symbols */ true}, {0.207F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 11, /* combine symbols */ true}, {0.169F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 12, /* combine symbols */ true}, {0.134F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 13, /* combine symbols */ true}, {0.101F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 14, /* combine symbols */ true}, {0.101F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 15, /* combine symbols */ true}, {0.101F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 0, /* combine symbols */ true}, {0.059F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 1, /* combine symbols */ true}, {0.511F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 2, /* combine symbols */ true}, {0.452F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 3, /* combine symbols */ true}, {0.407F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 4, /* combine symbols */ true}, {0.345F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 5, /* combine symbols */ true}, {0.302F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 6, /* combine symbols */ true}, {0.246F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 7, /* combine symbols */ true}, {0.216F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 8, /* combine symbols */ true}, {0.180F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 9, /* combine symbols */ true}, {0.145F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 10, /* combine symbols */ true}, {0.116F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 11, /* combine symbols */ true}, {0.097F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 12, /* combine symbols */ true}, {0.077F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 13, /* combine symbols */ true}, {0.059F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 14, /* combine symbols */ true}, {0.058F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 15, /* combine symbols */ true}, {0.059F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 0, /* combine symbols */ true}, {0.036F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 1, /* combine symbols */ true}, {0.301F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 2, /* combine symbols */ true}, {0.270F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 3, /* combine symbols */ true}, {0.243F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 4, /* combine symbols */ true}, {0.208F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 5, /* combine symbols */ true}, {0.181F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 6, /* combine symbols */ true}, {0.150F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 7, /* combine symbols */ true}, {0.132F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 8, /* combine symbols */ true}, {0.111F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 9, /* combine symbols */ true}, {0.090F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 10, /* combine symbols */ true}, {0.072F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 11, /* combine symbols */ true}, {0.060F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 12, /* combine symbols */ true}, {0.048F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 13, /* combine symbols */ true}, {0.037F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 14, /* combine symbols */ true}, {0.036F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 15, /* combine symbols */ true}, {0.036F, 5}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.610F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.995F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.876F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.787F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.769F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.690F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.637F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.600F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.607F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.604F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.597F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.603F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.598F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.328F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {0.692F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {0.625F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {0.555F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.507F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.461F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.423F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.409F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.371F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.344F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.323F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.323F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.324F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.195F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {0.406F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {0.367F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {0.328F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.303F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.271F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.246F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.243F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.224F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.206F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.196F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.197F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.196F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.196F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.198F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.196F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.605F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.980F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.879F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.785F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.760F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.692F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.631F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.598F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.602F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.611F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.607F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.605F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.607F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.322F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {0.686F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {0.617F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {0.554F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.516F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.460F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.416F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.406F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.374F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.342F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.323F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.325F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.328F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.321F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.195F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {0.401F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {0.358F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {0.325F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.302F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.271F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.244F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.241F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.222F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.204F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.194F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.195F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.196F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.605F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.980F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.879F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.785F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.760F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.692F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.631F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.598F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.602F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.611F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.607F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.605F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.607F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.322F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {0.686F, 12}, th_flag::red}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {0.617F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {0.554F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.516F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.460F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.416F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.406F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.374F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.342F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.323F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.325F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.324F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.321F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.195F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {0.401F, 12}, th_flag::red}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {0.358F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {0.325F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.302F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.271F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.244F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.241F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.222F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.204F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.194F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.195F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 0, /* combine symbols */ true}, {0.352F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 4, /* combine symbols */ true}, {0.987F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 5, /* combine symbols */ true}, {0.868F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 6, /* combine symbols */ true}, {0.789F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 7, /* combine symbols */ true}, {0.762F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 8, /* combine symbols */ true}, {0.696F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 9, /* combine symbols */ true}, {0.633F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 10, /* combine symbols */ true}, {0.602F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 11, /* combine symbols */ true}, {0.516F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 12, /* combine symbols */ true}, {0.461F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 13, /* combine symbols */ true}, {0.388F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 14, /* combine symbols */ true}, {0.350F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 15, /* combine symbols */ true}, {0.353F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 0, /* combine symbols */ true}, {0.192F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 1, /* combine symbols */ true}, {0.688F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 2, /* combine symbols */ true}, {0.625F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 3, /* combine symbols */ true}, {0.568F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 4, /* combine symbols */ true}, {0.516F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 5, /* combine symbols */ true}, {0.460F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 6, /* combine symbols */ true}, {0.416F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 7, /* combine symbols */ true}, {0.407F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 8, /* combine symbols */ true}, {0.372F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 9, /* combine symbols */ true}, {0.342F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 10, /* combine symbols */ true}, {0.324F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 11, /* combine symbols */ true}, {0.281F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 12, /* combine symbols */ true}, {0.255F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 13, /* combine symbols */ true}, {0.218F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 14, /* combine symbols */ true}, {0.193F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 15, /* combine symbols */ true}, {0.192F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 0, /* combine symbols */ true}, {0.119F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 1, /* combine symbols */ true}, {0.402F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 2, /* combine symbols */ true}, {0.363F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 3, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 4, /* combine symbols */ true}, {0.304F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 5, /* combine symbols */ true}, {0.273F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 6, /* combine symbols */ true}, {0.248F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 7, /* combine symbols */ true}, {0.243F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 8, /* combine symbols */ true}, {0.224F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 9, /* combine symbols */ true}, {0.206F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 10, /* combine symbols */ true}, {0.195F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 11, /* combine symbols */ true}, {0.172F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 12, /* combine symbols */ true}, {0.154F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 13, /* combine symbols */ true}, {0.131F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 14, /* combine symbols */ true}, {0.118F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 15, /* combine symbols */ true}, {0.119F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 0, /* combine symbols */ true}, {0.352F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 4, /* combine symbols */ true}, {0.993F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 5, /* combine symbols */ true}, {0.863F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 6, /* combine symbols */ true}, {0.786F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 7, /* combine symbols */ true}, {0.776F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 8, /* combine symbols */ true}, {0.699F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 9, /* combine symbols */ true}, {0.633F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 10, /* combine symbols */ true}, {0.607F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 11, /* combine symbols */ true}, {0.518F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 12, /* combine symbols */ true}, {0.464F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 13, /* combine symbols */ true}, {0.392F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 14, /* combine symbols */ true}, {0.349F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 15, /* combine symbols */ true}, {0.353F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 0, /* combine symbols */ true}, {0.194F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 1, /* combine symbols */ true}, {0.694F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 2, /* combine symbols */ true}, {0.611F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 3, /* combine symbols */ true}, {0.550F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 4, /* combine symbols */ true}, {0.510F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 5, /* combine symbols */ true}, {0.452F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 6, /* combine symbols */ true}, {0.411F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 7, /* combine symbols */ true}, {0.400F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 8, /* combine symbols */ true}, {0.367F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 9, /* combine symbols */ true}, {0.340F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 10, /* combine symbols */ true}, {0.323F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 11, /* combine symbols */ true}, {0.282F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 12, /* combine symbols */ true}, {0.256F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 13, /* combine symbols */ true}, {0.212F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 14, /* combine symbols */ true}, {0.193F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 15, /* combine symbols */ true}, {0.193F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 0, /* combine symbols */ true}, {0.118F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 1, /* combine symbols */ true}, {0.404F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 2, /* combine symbols */ true}, {0.362F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 3, /* combine symbols */ true}, {0.328F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 4, /* combine symbols */ true}, {0.301F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 5, /* combine symbols */ true}, {0.272F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 6, /* combine symbols */ true}, {0.247F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 7, /* combine symbols */ true}, {0.242F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 8, /* combine symbols */ true}, {0.222F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 9, /* combine symbols */ true}, {0.205F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 10, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 11, /* combine symbols */ true}, {0.170F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 12, /* combine symbols */ true}, {0.155F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 13, /* combine symbols */ true}, {0.131F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 14, /* combine symbols */ true}, {0.119F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 15, /* combine symbols */ true}, {0.119F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.234F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.995F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.876F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.789F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.763F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.707F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.646F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.603F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.517F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.465F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.388F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.304F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.232F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.134F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {0.687F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {0.616F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {0.553F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.510F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.456F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.408F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.402F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.370F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.348F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.289F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.252F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.217F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.173F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.133F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.080F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {0.401F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {0.360F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {0.324F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.298F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.270F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.246F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.242F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.223F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.205F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.193F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.171F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.153F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.130F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.104F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.080F, 12}, th_flag::orange}, + // The following commented value is the calibrated one - however, we noticed that in synthetic environments with no + // noise/interference, the resulting false-alarm probability is a bit too high. We increase a bit the threshold. + // {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.229F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.458F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.986F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.883F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.783F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.770F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.700F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.637F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.599F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.516F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.464F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.385F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.304F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.232F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.131F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {0.677F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {0.622F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {0.553F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.511F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.458F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.414F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.409F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.374F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.345F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.325F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.283F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.255F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.213F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.171F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.132F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.081F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {0.404F, 12}, th_flag::red}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {0.363F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {0.322F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.298F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.268F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.246F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.241F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.219F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.205F, 12}, th_flag::orange}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.197F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.171F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.155F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.131F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.105F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.081F, 12}, th_flag::green}, + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.458F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.986F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.883F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.783F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.770F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.700F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.637F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.599F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.516F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.464F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.385F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.304F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.232F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.131F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {0.677F, 12}, th_flag::red}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {0.622F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {0.553F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.511F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.458F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.414F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.409F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.374F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.345F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.325F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.283F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.255F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.213F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.171F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.132F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.081F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {0.404F, 12}, th_flag::red}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {0.363F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {0.322F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.298F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.268F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.246F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.241F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.219F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.205F, 12}, th_flag::orange}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.197F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.171F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.155F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.131F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.105F, 12}, th_flag::green}, // provisional + {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.081F, 12}, th_flag::green}, // provisional + // clang-format on +}); + +static const threshold_and_margin_finder threshold_and_margin_table(all_threshold_and_margins); + +threshold_and_margin_type srsran::detail::get_threshold_and_margin(const threshold_params& params) +{ + return threshold_and_margin_table.get(params); +} + +threshold_flag srsran::detail::get_threshold_flag(const threshold_params& params) +{ + return threshold_and_margin_table.check_flag(params); +} diff --git a/lib/phy/upper/channel_processors/prach_detector_generic_thresholds.h b/lib/phy/upper/channel_processors/prach_detector_generic_thresholds.h index 419621aef5..970731e973 100644 --- a/lib/phy/upper/channel_processors/prach_detector_generic_thresholds.h +++ b/lib/phy/upper/channel_processors/prach_detector_generic_thresholds.h @@ -25,13 +25,20 @@ #pragma once -#include "srsran/adt/span.h" -#include "srsran/adt/to_array.h" #include "srsran/ran/prach/prach_format_type.h" #include "srsran/ran/prach/prach_subcarrier_spacing.h" -namespace srsran { -namespace detail { +namespace srsran::detail { + +/// Flag applied to each threshold entry. +enum class threshold_flag { + /// Threshold entry not suitable for proper PRACH detection. + red, + /// Threshold entry with suboptimal PRACH detection performance. + orange, + /// Threshold entry meeting PRACH detection requirements. + green +}; /// Parameters that affect the detection threshold value. struct threshold_params { @@ -47,688 +54,13 @@ struct threshold_params { bool combine_symbols = false; }; -/// Overloaded less-than comparison operator. -inline bool operator<(const threshold_params& lhs, const threshold_params& rhs) -{ - if (lhs.nof_rx_ports < rhs.nof_rx_ports) { - return true; - } - if (lhs.nof_rx_ports > rhs.nof_rx_ports) { - return false; - } - - // If we are here, nof_rx_ports is equal. - if (lhs.scs < rhs.scs) { - return true; - } - if (lhs.scs > rhs.scs) { - return false; - } - - // If we are here, nof_rx_ports and scs are equal. - if (lhs.format < rhs.format) { - return true; - } - if (lhs.format > rhs.format) { - return false; - } - - // If we are here, nof_rx_ports, scs and format are equal. - if (lhs.zero_correlation_zone < rhs.zero_correlation_zone) { - return true; - } - if (lhs.zero_correlation_zone > rhs.zero_correlation_zone) { - return false; - } - - // If we are here, nof_rx_ports, scs, format and zero_correlation_zone are equal. - if (!lhs.combine_symbols && rhs.combine_symbols) { - return true; - } - return false; -} - -/// Overloaded equal-to comparison operator. -inline bool operator==(const threshold_params& lhs, const threshold_params& rhs) -{ - if (lhs.nof_rx_ports != rhs.nof_rx_ports) { - return false; - } - - // If we are here, nof_rx_ports is equal. - if (lhs.scs != rhs.scs) { - return false; - } - - // If we are here, nof_rx_ports and scs are equal. - if (lhs.format != rhs.format) { - return false; - } - - // If we are here, nof_rx_ports, scs and format are equal. - if (lhs.zero_correlation_zone != rhs.zero_correlation_zone) { - return false; - } - - // If we are here, nof_rx_ports, scs, format and zero_correlation_zone are equal. - if (lhs.combine_symbols != rhs.combine_symbols) { - return false; - } - return true; -} +/// Pairs the PRACH detector threshold and margin. +using threshold_and_margin_type = std::pair; -/// Manages the mapping between PRACH configuration and the (threshold, margin) pairs. -class threshold_and_margin_finder -{ -public: - /// Flag applied to each threshold entry. - enum class threshold_flag { - /// Threshold entry not suitable for proper PRACH detection. - red, - /// Threshold entry with suboptimal PRACH detection performance. - orange, - /// Threshold entry meeting PRACH detection requirements. - green - }; - - using threshold_and_margin_type = std::pair; - - /// Mapping between PRACH configuration and threshold value. - struct threshold_entry { - /// Subset of PRACH configuration parameters affecting the threshold value. - threshold_params configuration; - /// Threshold value and search margin. - threshold_and_margin_type threshold_and_margin; - /// Threshold quality flag. - threshold_flag flag; - }; - - /// Constructor: receives the list of pairings and ensures it is sorted. - explicit threshold_and_margin_finder(span in) : - sorted_thresholds_and_margins(in.begin(), in.end()) - { - std::sort(sorted_thresholds_and_margins.begin(), - sorted_thresholds_and_margins.end(), - [](const threshold_entry& a, const threshold_entry& b) { return (a.configuration < b.configuration); }); - } - - /// Retrieves the (threshold, margin) pair corresponding to the given configuration. - threshold_and_margin_type get(const threshold_params& params) const - { - auto it = - std::lower_bound(sorted_thresholds_and_margins.begin(), - sorted_thresholds_and_margins.end(), - params, - [](const threshold_entry& a, const threshold_params& b) { return (a.configuration < b); }); - - if (it != sorted_thresholds_and_margins.end()) { - return it->threshold_and_margin; - } - - // todo(david): once all cases are covered, replace this by a srsran_assert. - if (is_long_preamble(params.format)) { - return {/* threshold */ 2.0F, /* margin */ 5}; - } - return {/* threshold */ 0.3F, /* margin */ 12}; - } - - /// Checks the quality flag of the threshold for the given configurations. - threshold_flag check_flag(const threshold_params& params) const - { - auto it = - std::lower_bound(sorted_thresholds_and_margins.begin(), - sorted_thresholds_and_margins.end(), - params, - [](const threshold_entry& a, const threshold_params& b) { return (a.configuration < b); }); - - if ((it != sorted_thresholds_and_margins.end()) && (it->configuration == params)) { - return it->flag; - } - return threshold_flag::red; - } - -private: - /// Sorted list of thresholds. - std::vector sorted_thresholds_and_margins; -}; +/// Checks the quality flag of the threshold for the given configurations. +threshold_flag get_threshold_flag(const threshold_params& params); -using th_flag = threshold_and_margin_finder::threshold_flag; -/// Unsorted list of thresholds. -static const auto all_threshold_and_margins = to_array({ - // clang-format off - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 0, /* combine symbols */ true}, {0.147F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 2, /* combine symbols */ true}, {0.874F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 3, /* combine symbols */ true}, {0.774F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 4, /* combine symbols */ true}, {0.640F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 5, /* combine symbols */ true}, {0.551F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 6, /* combine symbols */ true}, {0.449F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 7, /* combine symbols */ true}, {0.388F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 8, /* combine symbols */ true}, {0.321F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 9, /* combine symbols */ true}, {0.255F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 10, /* combine symbols */ true}, {0.205F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 11, /* combine symbols */ true}, {0.167F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 12, /* combine symbols */ true}, {0.147F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 13, /* combine symbols */ true}, {0.148F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 14, /* combine symbols */ true}, {0.148F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 15, /* combine symbols */ true}, {0.148F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 0, /* combine symbols */ true}, {0.085F, 5}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 1, /* combine symbols */ true}, {0.522F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 2, /* combine symbols */ true}, {0.451F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 3, /* combine symbols */ true}, {0.404F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 4, /* combine symbols */ true}, {0.344F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 5, /* combine symbols */ true}, {0.301F, 5}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 6, /* combine symbols */ true}, {0.249F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 7, /* combine symbols */ true}, {0.219F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 8, /* combine symbols */ true}, {0.180F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 9, /* combine symbols */ true}, {0.146F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 10, /* combine symbols */ true}, {0.118F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 11, /* combine symbols */ true}, {0.098F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 12, /* combine symbols */ true}, {0.085F, 5}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 13, /* combine symbols */ true}, {0.086F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 14, /* combine symbols */ true}, {0.085F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 15, /* combine symbols */ true}, {0.085F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 0, /* combine symbols */ true}, {0.053F, 5}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 1, /* combine symbols */ true}, {0.307F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 2, /* combine symbols */ true}, {0.269F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 3, /* combine symbols */ true}, {0.240F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 4, /* combine symbols */ true}, {0.207F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 5, /* combine symbols */ true}, {0.180F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 6, /* combine symbols */ true}, {0.149F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 7, /* combine symbols */ true}, {0.131F, 5}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 8, /* combine symbols */ true}, {0.112F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 9, /* combine symbols */ true}, {0.090F, 5}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 10, /* combine symbols */ true}, {0.072F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 11, /* combine symbols */ true}, {0.060F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 12, /* combine symbols */ true}, {0.052F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 13, /* combine symbols */ true}, {0.052F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 14, /* combine symbols */ true}, {0.052F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::zero, /* ZCZ */ 15, /* combine symbols */ true}, {0.053F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 0, /* combine symbols */ true}, {0.025F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 2, /* combine symbols */ true}, {0.869F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 3, /* combine symbols */ true}, {0.781F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 4, /* combine symbols */ true}, {0.636F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 5, /* combine symbols */ true}, {0.548F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 6, /* combine symbols */ true}, {0.455F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 7, /* combine symbols */ true}, {0.387F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 8, /* combine symbols */ true}, {0.328F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 9, /* combine symbols */ true}, {0.256F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 10, /* combine symbols */ true}, {0.205F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 11, /* combine symbols */ true}, {0.169F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 12, /* combine symbols */ true}, {0.134F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 13, /* combine symbols */ true}, {0.097F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 14, /* combine symbols */ true}, {0.060F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 15, /* combine symbols */ true}, {0.041F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 0, /* combine symbols */ true}, {0.014F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 1, /* combine symbols */ true}, {0.510F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 2, /* combine symbols */ true}, {0.459F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 3, /* combine symbols */ true}, {0.409F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 4, /* combine symbols */ true}, {0.341F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 5, /* combine symbols */ true}, {0.299F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 6, /* combine symbols */ true}, {0.250F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 7, /* combine symbols */ true}, {0.213F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 8, /* combine symbols */ true}, {0.183F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 9, /* combine symbols */ true}, {0.145F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 10, /* combine symbols */ true}, {0.118F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 11, /* combine symbols */ true}, {0.097F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 12, /* combine symbols */ true}, {0.078F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 13, /* combine symbols */ true}, {0.057F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 14, /* combine symbols */ true}, {0.035F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 15, /* combine symbols */ true}, {0.024F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 0, /* combine symbols */ true}, {0.009F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 1, /* combine symbols */ true}, {0.304F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 2, /* combine symbols */ true}, {0.269F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 3, /* combine symbols */ true}, {0.242F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 4, /* combine symbols */ true}, {0.206F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 5, /* combine symbols */ true}, {0.180F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 6, /* combine symbols */ true}, {0.150F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 7, /* combine symbols */ true}, {0.132F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 8, /* combine symbols */ true}, {0.112F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 9, /* combine symbols */ true}, {0.090F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 10, /* combine symbols */ true}, {0.072F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 11, /* combine symbols */ true}, {0.060F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 12, /* combine symbols */ true}, {0.048F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 13, /* combine symbols */ true}, {0.035F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 14, /* combine symbols */ true}, {0.022F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::one, /* ZCZ */ 15, /* combine symbols */ true}, {0.015F, 5}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 0, /* combine symbols */ true}, {0.100F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 1, /* combine symbols */ true}, {0.996F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 2, /* combine symbols */ true}, {0.882F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 3, /* combine symbols */ true}, {0.772F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 4, /* combine symbols */ true}, {0.658F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 5, /* combine symbols */ true}, {0.551F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 6, /* combine symbols */ true}, {0.450F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 7, /* combine symbols */ true}, {0.387F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 8, /* combine symbols */ true}, {0.329F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 9, /* combine symbols */ true}, {0.258F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 10, /* combine symbols */ true}, {0.207F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 11, /* combine symbols */ true}, {0.169F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 12, /* combine symbols */ true}, {0.134F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 13, /* combine symbols */ true}, {0.101F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 14, /* combine symbols */ true}, {0.101F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 15, /* combine symbols */ true}, {0.101F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 0, /* combine symbols */ true}, {0.059F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 1, /* combine symbols */ true}, {0.511F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 2, /* combine symbols */ true}, {0.452F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 3, /* combine symbols */ true}, {0.407F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 4, /* combine symbols */ true}, {0.345F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 5, /* combine symbols */ true}, {0.302F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 6, /* combine symbols */ true}, {0.246F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 7, /* combine symbols */ true}, {0.216F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 8, /* combine symbols */ true}, {0.180F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 9, /* combine symbols */ true}, {0.145F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 10, /* combine symbols */ true}, {0.116F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 11, /* combine symbols */ true}, {0.097F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 12, /* combine symbols */ true}, {0.077F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 13, /* combine symbols */ true}, {0.059F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 14, /* combine symbols */ true}, {0.058F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 15, /* combine symbols */ true}, {0.059F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 0, /* combine symbols */ true}, {0.036F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 1, /* combine symbols */ true}, {0.301F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 2, /* combine symbols */ true}, {0.270F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 3, /* combine symbols */ true}, {0.243F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 4, /* combine symbols */ true}, {0.208F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 5, /* combine symbols */ true}, {0.181F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 6, /* combine symbols */ true}, {0.150F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 7, /* combine symbols */ true}, {0.132F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 8, /* combine symbols */ true}, {0.111F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 9, /* combine symbols */ true}, {0.090F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 10, /* combine symbols */ true}, {0.072F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 11, /* combine symbols */ true}, {0.060F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 12, /* combine symbols */ true}, {0.048F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 13, /* combine symbols */ true}, {0.037F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 14, /* combine symbols */ true}, {0.036F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz1_25, prach_format_type::two, /* ZCZ */ 15, /* combine symbols */ true}, {0.036F, 5}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.610F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.995F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.876F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.787F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.769F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.690F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.637F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.600F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.607F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.604F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.597F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.603F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.598F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.328F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {0.692F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {0.625F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {0.555F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.507F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.461F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.423F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.409F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.371F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.344F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.323F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.323F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.324F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.195F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {0.406F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {0.367F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {0.328F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.303F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.271F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.246F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.243F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.224F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.206F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.196F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.197F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.196F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.196F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.198F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.196F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.605F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.980F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.879F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.785F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.760F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.692F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.631F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.598F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.602F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.611F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.607F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.605F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.607F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.322F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {0.686F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {0.617F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {0.554F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.516F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.460F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.416F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.406F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.374F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.342F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.323F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.325F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.328F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.321F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.195F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {0.401F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {0.358F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {0.325F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.302F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.271F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.244F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.241F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.222F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.204F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.194F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.195F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.196F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.605F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.980F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.879F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.785F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.760F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.692F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.631F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.598F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.602F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.611F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.607F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.605F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.607F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.322F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {0.686F, 12}, th_flag::red}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {0.617F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {0.554F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.516F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.460F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.416F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.406F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.374F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.342F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.323F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.325F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.324F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.321F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 0, /* combine symbols */ true}, {0.195F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 1, /* combine symbols */ true}, {0.401F, 12}, th_flag::red}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 2, /* combine symbols */ true}, {0.358F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 3, /* combine symbols */ true}, {0.325F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 4, /* combine symbols */ true}, {0.302F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 5, /* combine symbols */ true}, {0.271F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 6, /* combine symbols */ true}, {0.244F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 7, /* combine symbols */ true}, {0.241F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 8, /* combine symbols */ true}, {0.222F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 9, /* combine symbols */ true}, {0.204F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 10, /* combine symbols */ true}, {0.194F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 11, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 12, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 13, /* combine symbols */ true}, {0.195F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 14, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::A1, /* ZCZ */ 15, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 0, /* combine symbols */ true}, {0.352F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 4, /* combine symbols */ true}, {0.987F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 5, /* combine symbols */ true}, {0.868F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 6, /* combine symbols */ true}, {0.789F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 7, /* combine symbols */ true}, {0.762F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 8, /* combine symbols */ true}, {0.696F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 9, /* combine symbols */ true}, {0.633F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 10, /* combine symbols */ true}, {0.602F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 11, /* combine symbols */ true}, {0.516F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 12, /* combine symbols */ true}, {0.461F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 13, /* combine symbols */ true}, {0.388F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 14, /* combine symbols */ true}, {0.350F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 15, /* combine symbols */ true}, {0.353F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 0, /* combine symbols */ true}, {0.192F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 1, /* combine symbols */ true}, {0.688F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 2, /* combine symbols */ true}, {0.625F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 3, /* combine symbols */ true}, {0.568F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 4, /* combine symbols */ true}, {0.516F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 5, /* combine symbols */ true}, {0.460F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 6, /* combine symbols */ true}, {0.416F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 7, /* combine symbols */ true}, {0.407F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 8, /* combine symbols */ true}, {0.372F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 9, /* combine symbols */ true}, {0.342F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 10, /* combine symbols */ true}, {0.324F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 11, /* combine symbols */ true}, {0.281F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 12, /* combine symbols */ true}, {0.255F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 13, /* combine symbols */ true}, {0.218F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 14, /* combine symbols */ true}, {0.193F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 15, /* combine symbols */ true}, {0.192F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 0, /* combine symbols */ true}, {0.119F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 1, /* combine symbols */ true}, {0.402F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 2, /* combine symbols */ true}, {0.363F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 3, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 4, /* combine symbols */ true}, {0.304F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 5, /* combine symbols */ true}, {0.273F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 6, /* combine symbols */ true}, {0.248F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 7, /* combine symbols */ true}, {0.243F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 8, /* combine symbols */ true}, {0.224F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 9, /* combine symbols */ true}, {0.206F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 10, /* combine symbols */ true}, {0.195F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 11, /* combine symbols */ true}, {0.172F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 12, /* combine symbols */ true}, {0.154F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 13, /* combine symbols */ true}, {0.131F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 14, /* combine symbols */ true}, {0.118F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::A2, /* ZCZ */ 15, /* combine symbols */ true}, {0.119F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 0, /* combine symbols */ true}, {0.352F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 4, /* combine symbols */ true}, {0.993F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 5, /* combine symbols */ true}, {0.863F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 6, /* combine symbols */ true}, {0.786F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 7, /* combine symbols */ true}, {0.776F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 8, /* combine symbols */ true}, {0.699F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 9, /* combine symbols */ true}, {0.633F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 10, /* combine symbols */ true}, {0.607F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 11, /* combine symbols */ true}, {0.518F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 12, /* combine symbols */ true}, {0.464F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 13, /* combine symbols */ true}, {0.392F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 14, /* combine symbols */ true}, {0.349F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 15, /* combine symbols */ true}, {0.353F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 0, /* combine symbols */ true}, {0.194F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 1, /* combine symbols */ true}, {0.694F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 2, /* combine symbols */ true}, {0.611F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 3, /* combine symbols */ true}, {0.550F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 4, /* combine symbols */ true}, {0.510F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 5, /* combine symbols */ true}, {0.452F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 6, /* combine symbols */ true}, {0.411F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 7, /* combine symbols */ true}, {0.400F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 8, /* combine symbols */ true}, {0.367F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 9, /* combine symbols */ true}, {0.340F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 10, /* combine symbols */ true}, {0.323F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 11, /* combine symbols */ true}, {0.282F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 12, /* combine symbols */ true}, {0.256F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 13, /* combine symbols */ true}, {0.212F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 14, /* combine symbols */ true}, {0.193F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 15, /* combine symbols */ true}, {0.193F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 0, /* combine symbols */ true}, {0.118F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 1, /* combine symbols */ true}, {0.404F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 2, /* combine symbols */ true}, {0.362F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 3, /* combine symbols */ true}, {0.328F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 4, /* combine symbols */ true}, {0.301F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 5, /* combine symbols */ true}, {0.272F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 6, /* combine symbols */ true}, {0.247F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 7, /* combine symbols */ true}, {0.242F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 8, /* combine symbols */ true}, {0.222F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 9, /* combine symbols */ true}, {0.205F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 10, /* combine symbols */ true}, {0.195F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 11, /* combine symbols */ true}, {0.170F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 12, /* combine symbols */ true}, {0.155F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 13, /* combine symbols */ true}, {0.131F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 14, /* combine symbols */ true}, {0.119F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::A2, /* ZCZ */ 15, /* combine symbols */ true}, {0.119F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.234F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.995F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.876F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.789F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.763F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.707F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.646F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.603F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.517F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.465F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.388F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.304F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.232F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.134F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {0.687F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {0.616F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {0.553F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.510F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.456F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.408F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.402F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.370F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.348F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.326F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.289F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.252F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.217F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.173F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.133F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.080F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {0.401F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {0.360F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {0.324F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.298F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.270F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.246F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.242F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.223F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.205F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.193F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.171F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.153F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.130F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.104F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz15, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.080F, 12}, th_flag::orange}, - // The following commented value is the calibrated one - however, we noticed that in synthetic environments with no - // noise/interference, the resulting false-alarm probability is a bit too high. We increase a bit the threshold. - // {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.229F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.458F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.986F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.883F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.783F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.770F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.700F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.637F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.599F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.516F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.464F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.385F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.304F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.232F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.131F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {0.677F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {0.622F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {0.553F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.511F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.458F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.414F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.409F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.374F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.345F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.325F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.283F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.255F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.213F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.171F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.132F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.081F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {0.404F, 12}, th_flag::red}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {0.363F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {0.322F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.298F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.268F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.246F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.241F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.219F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.205F, 12}, th_flag::orange}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.197F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.171F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.155F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.131F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.105F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz30, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.081F, 12}, th_flag::green}, - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.458F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {1.000F, 12}, th_flag::red}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {1.000F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.986F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.883F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.783F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.770F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.700F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.637F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.599F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.516F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.464F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.385F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.304F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 1, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.232F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.131F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {0.677F, 12}, th_flag::red}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {0.622F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {0.553F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.511F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.458F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.414F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.409F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.374F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.345F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.325F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.283F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.255F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.213F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.171F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 2, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.132F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 0, /* combine symbols */ true}, {0.081F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 1, /* combine symbols */ true}, {0.404F, 12}, th_flag::red}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 2, /* combine symbols */ true}, {0.363F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 3, /* combine symbols */ true}, {0.322F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 4, /* combine symbols */ true}, {0.298F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 5, /* combine symbols */ true}, {0.268F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 6, /* combine symbols */ true}, {0.246F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 7, /* combine symbols */ true}, {0.241F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 8, /* combine symbols */ true}, {0.219F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 9, /* combine symbols */ true}, {0.205F, 12}, th_flag::orange}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 10, /* combine symbols */ true}, {0.197F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 11, /* combine symbols */ true}, {0.171F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 12, /* combine symbols */ true}, {0.155F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 13, /* combine symbols */ true}, {0.131F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 14, /* combine symbols */ true}, {0.105F, 12}, th_flag::green}, // provisional - {{/* nof_rx_ports */ 4, prach_subcarrier_spacing::kHz120, prach_format_type::B4, /* ZCZ */ 15, /* combine symbols */ true}, {0.081F, 12}, th_flag::green}, // provisional - // clang-format on -}); +/// Retrieves the (threshold, margin) pair corresponding to the given configuration. +threshold_and_margin_type get_threshold_and_margin(const threshold_params& params); -} // namespace detail -} // namespace srsran +} // namespace srsran::detail diff --git a/lib/phy/upper/channel_processors/prach_detector_phy_validator_impl.cpp b/lib/phy/upper/channel_processors/prach_detector_phy_validator_impl.cpp index 916fe18790..21239d0622 100644 --- a/lib/phy/upper/channel_processors/prach_detector_phy_validator_impl.cpp +++ b/lib/phy/upper/channel_processors/prach_detector_phy_validator_impl.cpp @@ -45,10 +45,9 @@ error_type srsran::validate_prach_detector_phy(prach_format_type th_params.zero_correlation_zone = zero_correlation_zone; th_params.combine_symbols = true; - const detail::threshold_and_margin_finder threshold_and_margin_table(detail::all_threshold_and_margins); - auto flag = threshold_and_margin_table.check_flag(th_params); + auto flag = detail::get_threshold_flag(th_params); - if (flag == detail::threshold_and_margin_finder::threshold_flag::red) { + if (flag == detail::threshold_flag::red) { fmt::print("\nThe PRACH detector does not support the configuration {{Format {}, ZCZ {}, SCS {}, Rx ports {}}}.\n", to_string(format), zero_correlation_zone, diff --git a/lib/phy/upper/channel_processors/prach_generator_impl.cpp b/lib/phy/upper/channel_processors/prach_generator_impl.cpp index 8207b75ce5..29cf66e670 100644 --- a/lib/phy/upper/channel_processors/prach_generator_impl.cpp +++ b/lib/phy/upper/channel_processors/prach_generator_impl.cpp @@ -185,7 +185,7 @@ span prach_generator_impl::generate_y_u_v_long(unsigned sequence_num 2935, 3355, 419}; // Create view of the sequence. - span y_u_v = sequence.first(LONG); + span y_u_v = span(sequence).first(LONG); // Sequence compression factor and offset. uint64_t factor = static_cast(compression_factor_table[sequence_number]); @@ -231,7 +231,7 @@ span prach_generator_impl::generate_y_u_v_short(unsigned sequence_nu 199, 547, 339, 409, 479, 271, 341, 411, 481, 551, 65, 135, 205, 553, 345, 137, 207, 555, 69}; // Create view of the sequence. - span y_u_v = sequence.first(SHORT); + span y_u_v = span(sequence).first(SHORT); // Sequence compression factor and offset. uint64_t factor = static_cast(compression_factor_table[sequence_number]); diff --git a/lib/phy/upper/channel_processors/prach_generator_impl.h b/lib/phy/upper/channel_processors/prach_generator_impl.h index b3788cef64..0dbbbceac1 100644 --- a/lib/phy/upper/channel_processors/prach_generator_impl.h +++ b/lib/phy/upper/channel_processors/prach_generator_impl.h @@ -24,7 +24,6 @@ #include "srsran/phy/upper/channel_processors/prach_generator.h" #include "srsran/ran/prach/prach_constants.h" -#include "srsran/srsvec/aligned_vec.h" namespace srsran { @@ -45,7 +44,7 @@ class prach_generator_impl : public prach_generator static constexpr unsigned SHORT = prach_constants::SHORT_SEQUENCE_LENGTH; /// Temporary sequence. - srsvec::aligned_vec sequence; + std::vector sequence; /// Calculates sequence number \f$u\f$ as per TS38.211 Table 6.3.3.1-3. static unsigned get_sequence_number_long(unsigned root_sequence_index); diff --git a/lib/phy/upper/channel_processors/pucch/CMakeLists.txt b/lib/phy/upper/channel_processors/pucch/CMakeLists.txt index 7b263453a4..94b1f22574 100644 --- a/lib/phy/upper/channel_processors/pucch/CMakeLists.txt +++ b/lib/phy/upper/channel_processors/pucch/CMakeLists.txt @@ -28,4 +28,5 @@ add_library(srsran_pucch_detector STATIC add_library(srsran_pucch_demodulator STATIC factories.cpp pucch_demodulator_format2.cpp - pucch_demodulator_format3.cpp) + pucch_demodulator_format3.cpp + pucch_demodulator_format4.cpp) diff --git a/lib/phy/upper/channel_processors/pucch/factories.cpp b/lib/phy/upper/channel_processors/pucch/factories.cpp index 2d52435b5d..3a061bd69b 100644 --- a/lib/phy/upper/channel_processors/pucch/factories.cpp +++ b/lib/phy/upper/channel_processors/pucch/factories.cpp @@ -188,7 +188,14 @@ class pucch_demodulator_factory_sw : public pucch_demodulator_factory prg_factory->create(), precoder_factory->create()); - return std::make_unique(std::move(demodulator_format2), std::move(demodulator_format3)); + std::unique_ptr demodulator_format4 = + std::make_unique(equalizer_factory->create(), + demodulation_factory->create_demodulation_mapper(), + prg_factory->create(), + precoder_factory->create()); + + return std::make_unique( + std::move(demodulator_format2), std::move(demodulator_format3), std::move(demodulator_format4)); } pucch_demodulator_factory_sw(std::shared_ptr equalizer_factory_, diff --git a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.cpp b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.cpp index 0eeede81f2..210b87e21d 100644 --- a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.cpp +++ b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.cpp @@ -72,7 +72,7 @@ void pucch_demodulator_format2::demodulate(span // Resize data and channel estimation buffers. ch_re.resize(nof_rx_ports, nof_re_port); - ch_estimates.resize(nof_re_port, nof_rx_ports, SINGLE_TX_LAYER); + ch_estimates.resize(nof_re_port, nof_rx_ports, pucch_constants::MAX_LAYERS); // Resize equalized data and post equalization noise variance buffers. eq_re.resize(nof_re_port); diff --git a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.h b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.h index 6728a41980..097ebfe61d 100644 --- a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.h +++ b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.h @@ -59,9 +59,6 @@ class pucch_demodulator_format2 const pucch_demodulator::format2_configuration& config); private: - /// PUCCH uses a single TX layer. - static constexpr unsigned SINGLE_TX_LAYER = 1; - /// \brief Gets PUCCH Resource Elements and channel estimation coefficients given a PUCCH Format 2 allocation. /// /// Extracts and loads the inner buffers with the PUCCH control data RE from the provided \c resource_grid, and their diff --git a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format3.cpp b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format3.cpp index af515de2da..898cdf6570 100644 --- a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format3.cpp +++ b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format3.cpp @@ -26,7 +26,6 @@ #include "pucch_demodulator_format3.h" #include "srsran/phy/support/mask_types.h" #include "srsran/phy/support/resource_grid_reader.h" -#include "srsran/phy/upper/equalization/modular_ch_est_list.h" #include "srsran/phy/upper/pucch_formats_3_4_helpers.h" using namespace srsran; @@ -36,23 +35,18 @@ void pucch_demodulator_format3::demodulate(span const channel_estimate& estimates, const pucch_demodulator::format3_configuration& config) { - // Number of receive antenna ports. - auto nof_rx_ports = static_cast(config.rx_ports.size()); - - // Number of REs per OFDM symbol. - const unsigned nof_re_symb = config.nof_prb * NRE; - - // Index of the first symbol allocated to the second subcarrier when intra-slot frequency hopping is enabled. - const unsigned second_hop_start = (config.nof_symbols / 2) + config.start_symbol_index; - // PUCCH Format 3 modulation scheme can be QPSK or pi/2-BPSK, as per TS38.211 Section 6.3.2.6.2. modulation_scheme mod_scheme = config.pi2_bpsk ? modulation_scheme::PI_2_BPSK : modulation_scheme::QPSK; - // Number of data Resource Elements in a slot for a single Rx port. + // Get a boolean mask of the OFDM symbols carrying DM-RS. symbol_slot_mask dmrs_symb_mask = get_pucch_formats_3_4_dmrs_symbol_mask( config.nof_symbols, config.second_hop_prb.has_value(), config.additional_dmrs); - const unsigned nof_re_port = (config.nof_symbols - dmrs_symb_mask.count()) * config.nof_prb * NRE; + // Number of REs per OFDM symbol. + const unsigned nof_re_symb = config.nof_prb * NRE; + + // Number of data Resource Elements in a slot for a single Rx port. + const unsigned nof_re_port = (config.nof_symbols - dmrs_symb_mask.count()) * nof_re_symb; // Assert that allocations are valid. srsran_assert(config.nof_prb && config.nof_prb <= pucch_constants::FORMAT3_MAX_NPRB, @@ -83,72 +77,25 @@ void pucch_demodulator_format3::demodulate(span eq_re.resize(nof_re_port); eq_noise_vars.resize(nof_re_port); - // Extract the Rx port noise variances from the channel estimation. - for (unsigned i_port = 0; i_port != nof_rx_ports; ++i_port) { - noise_var_estimates[i_port] = estimates.get_noise_variance(i_port, 0); - } - - // Get a view on the equalized RE buffer. - span eq_re_view = eq_re; - span noise_vars_view = eq_noise_vars; - // Assert that the number of RE returned by the channel equalizer matches the expected number of LLR. srsran_assert(eq_re.size() == llr.size() / get_bits_per_symbol(mod_scheme), "Number of equalized RE (i.e. {}) does not match the expected LLR data length (i.e. {})", eq_re.size(), llr.size() / get_bits_per_symbol(mod_scheme)); - for (unsigned i_symbol = config.start_symbol_index, i_symbol_end = config.start_symbol_index + config.nof_symbols; - i_symbol != i_symbol_end; - ++i_symbol) { - // Skip DM-RS symbols. - if (dmrs_symb_mask.test(i_symbol - config.start_symbol_index)) { - continue; - } - - // Calculate the lowest resource element containing PUCCH Format 3 within the OFDM symbol. - unsigned first_subc = config.first_prb * NRE; - if ((i_symbol >= second_hop_start) && config.second_hop_prb.has_value()) { - // Intra-slot frequency hopping. - first_subc = config.second_hop_prb.value() * NRE; - } - - // Create modular buffers to hold the spans for this symbol. - modular_re_buffer_reader re_symb(config.rx_ports.size(), nof_re_symb); - modular_ch_est_list estimates_symb(nof_re_symb, config.rx_ports.size(), SINGLE_TX_LAYER); - - for (unsigned i_port = 0, i_port_end = nof_rx_ports; i_port != i_port_end; ++i_port) { - // Extract data RE from the resource grid. - unsigned i_port_grid = config.rx_ports[i_port]; - span re_symb_view = grid.get_view(i_port_grid, i_symbol).subspan(first_subc, nof_re_symb); - re_symb.set_slice(i_port, re_symb_view); - - // Extract estimates from the estimates buffer. - estimates_symb.set_channel( - estimates.get_symbol_ch_estimate(i_symbol, i_port).subspan(first_subc, nof_re_symb), i_port, 0); - } - - // Get a view of the equalized RE buffer for a single symbol. - span eq_re_symb = eq_re_view.first(nof_re_symb); - - // Get a view of the equalized RE buffer for a single symbol. - span eq_noise_vars_symb = noise_vars_view.first(nof_re_symb); - - // Equalize the data RE for a single symbol. - equalizer->equalize(eq_re_symb, - eq_noise_vars_symb, - re_symb, - estimates_symb, - span(noise_var_estimates).first(nof_rx_ports), - 1.0F); - - // Revert transform precoding for a single symbol. - precoder->deprecode_ofdm_symbol(eq_re_symb, eq_re_symb); - - // Advance the equalized RE and noise vars views. - eq_re_view = eq_re_view.subspan(nof_re_symb, eq_re_view.size() - nof_re_symb); - noise_vars_view = noise_vars_view.subspan(nof_re_symb, noise_vars_view.size() - nof_re_symb); - } + pucch_3_4_extract_and_equalize(eq_re, + eq_noise_vars, + *equalizer, + *precoder, + grid, + estimates, + dmrs_symb_mask, + config.start_symbol_index, + config.nof_symbols, + config.nof_prb, + config.first_prb, + config.second_hop_prb, + config.rx_ports); // Apply soft symbol demodulation. demapper->demodulate_soft(llr, eq_re, eq_noise_vars, mod_scheme); diff --git a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format3.h b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format3.h index 1f99286055..89019a7824 100644 --- a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format3.h +++ b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format3.h @@ -62,9 +62,6 @@ class pucch_demodulator_format3 const pucch_demodulator::format3_configuration& config); private: - /// PUCCH uses a single TX layer. - static constexpr unsigned SINGLE_TX_LAYER = 1; - /// Channel equalization component, also in charge of combining contributions of all receive antenna ports. std::unique_ptr equalizer; /// Demodulation mapper component: transforms channel symbols into log-likelihood ratios (i.e., soft bits). @@ -81,9 +78,6 @@ class pucch_demodulator_format3 /// \brief Buffer used to transfer symbol noise variances at the equalizer output. /// \remark The symbols are arranged in two dimensions, i.e., resource element and transmit layer. static_vector eq_noise_vars; - - /// Buffer used to transfer noise variance estimates from the channel estimate to the equalizer. - std::array noise_var_estimates; }; } // namespace srsran diff --git a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format4.cpp b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format4.cpp new file mode 100644 index 0000000000..69ef4f921c --- /dev/null +++ b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format4.cpp @@ -0,0 +1,130 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/// \file +/// \brief PUCCH Format 4 demodulator definition. + +#include "pucch_demodulator_format4.h" +#include "srsran/phy/support/mask_types.h" +#include "srsran/phy/support/resource_grid_reader.h" +#include "srsran/phy/upper/pucch_formats_3_4_helpers.h" +#include "srsran/phy/upper/pucch_orthogonal_sequence.h" +#include "srsran/srsvec/sc_prod.h" + +using namespace srsran; + +void pucch_demodulator_format4::demodulate(span llr, + const resource_grid_reader& grid, + const channel_estimate& estimates, + const pucch_demodulator::format4_configuration& config) +{ + // PUCCH Format 4 modulation scheme can be QPSK or pi/2-BPSK, as per TS38.211 Section 6.3.2.6.2. + modulation_scheme mod_scheme = config.pi2_bpsk ? modulation_scheme::PI_2_BPSK : modulation_scheme::QPSK; + + // Get a boolean mask of the OFDM symbols carrying DM-RS. + symbol_slot_mask dmrs_symb_mask = get_pucch_formats_3_4_dmrs_symbol_mask( + config.nof_symbols, config.second_hop_prb.has_value(), config.additional_dmrs); + + // Number of REs per OFDM symbol. PUCCH Format 4 only gets a PRB. + unsigned nof_re_symb = NRE; + + // Number of data Resource Elements in a slot for a single Rx port. + unsigned nof_re_port = (config.nof_symbols - dmrs_symb_mask.count()) * nof_re_symb; + + // Assert that allocations are valid. + srsran_assert(config.first_prb * NRE <= grid.get_nof_subc(), + "PUCCH Format 4: PRB allocation outside grid (first hop). Requested {}, grid has {} PRBs.", + config.first_prb, + grid.get_nof_subc() / NRE); + + srsran_assert(!config.second_hop_prb.has_value() || (config.second_hop_prb.value() * NRE <= grid.get_nof_subc()), + "PUCCH Format 4: PRB allocation outside grid (second hop). Requested {}, grid has {} PRBs.", + config.second_hop_prb.value(), + grid.get_nof_subc() / NRE); + + interval nof_symbols_range(pucch_constants::FORMAT4_MIN_NSYMB, pucch_constants::FORMAT4_MAX_NSYMB); + srsran_assert(nof_symbols_range.contains(config.nof_symbols), + "Invalid Number of OFDM symbols allocated to PUCCH Format 4, i.e., {}. Valid range is {}.", + config.nof_symbols, + nof_symbols_range); + + // Resize equalized data and post equalization noise variance buffers. + eq_re.resize(nof_re_port); + eq_noise_vars.resize(nof_re_port); + + // Assert that the number of RE returned by the channel equalizer matches the expected number of LLR. + srsran_assert(eq_re.size() / config.occ_length == llr.size() / get_bits_per_symbol(mod_scheme), + "Number of equalized REs (i.e. {}) does not match the expected LLR data length (i.e. {})", + eq_re.size() / config.occ_length, + llr.size() / get_bits_per_symbol(mod_scheme)); + + pucch_3_4_extract_and_equalize(eq_re, + eq_noise_vars, + *equalizer, + *precoder, + grid, + estimates, + dmrs_symb_mask, + config.start_symbol_index, + config.nof_symbols, + 1, + config.first_prb, + config.second_hop_prb, + config.rx_ports); + + // Create vectors to hold the output of the inverse blockwise spreading. + static_vector original(eq_re.size() / config.occ_length, cf_t()); + static_vector noise_vars(original.size(), 0.0f); + + // Inverse block-wise spreading, as per TS38.211 Section 6.3.2.6.3. + inverse_blockwise_spreading(original, noise_vars, eq_re, eq_noise_vars, config); + + // Apply soft symbol demodulation. + demapper->demodulate_soft(llr, original, noise_vars, mod_scheme); + + // Descramble, as per TS38.211 Section 6.3.2.6.1. + unsigned c_init = (config.rnti * pow2(15)) + config.n_id; + descrambler->init(c_init); + descrambler->apply_xor(llr, llr); +} + +void pucch_demodulator_format4::inverse_blockwise_spreading(span original, + span noise_vars, + span eq_re, + span eq_noise_vars, + const pucch_demodulator::format4_configuration& config) +{ + unsigned mod = 12 / config.occ_length; + span wn = pucch_orthogonal_sequence_format4::get_sequence(config.occ_length, config.occ_index); + + for (unsigned k = 0; k != NRE; ++k) { + for (unsigned l = 0, l_end = eq_re.size() / NRE; l != l_end; ++l) { + unsigned original_index = (l * mod) + (k % mod); + unsigned spread_index = (l * 12) + k; + original[original_index] += eq_re[spread_index] / wn[k]; + noise_vars[original_index] += eq_noise_vars[spread_index]; + } + } + + // Scale according to the spreading factor. + srsvec::sc_prod(original, 1.0F / config.occ_length, original); +} diff --git a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format4.h b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format4.h new file mode 100644 index 0000000000..6c2cf32d99 --- /dev/null +++ b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format4.h @@ -0,0 +1,99 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/// \file +/// \brief PUCCH Format 4 demodulator declaration. + +#pragma once + +#include "srsran/adt/to_array.h" +#include "srsran/phy/generic_functions/transform_precoding/transform_precoder.h" +#include "srsran/phy/upper/channel_modulation/demodulation_mapper.h" +#include "srsran/phy/upper/channel_processors/pucch/pucch_demodulator.h" +#include "srsran/phy/upper/equalization/dynamic_ch_est_list.h" +#include "srsran/phy/upper/sequence_generators/pseudo_random_generator.h" +#include "srsran/ran/pucch/pucch_constants.h" + +namespace srsran { + +/// PUCCH Format 4 demodulator. +class pucch_demodulator_format4 +{ +public: + /// Constructor: sets up internal components and acquires their ownership. + pucch_demodulator_format4(std::unique_ptr equalizer_, + std::unique_ptr demapper_, + std::unique_ptr descrambler_, + std::unique_ptr precoder_) : + equalizer(std::move(equalizer_)), + demapper(std::move(demapper_)), + descrambler(std::move(descrambler_)), + precoder(std::move(precoder_)) + { + srsran_assert(equalizer, "Invalid pointer to channel_equalizer object."); + srsran_assert(demapper, "Invalid pointer to demodulation_mapper object."); + srsran_assert(descrambler, "Invalid pointer to pseudo_random_generator object."); + srsran_assert(precoder, "Invalid pointer to transform precoder object."); + } + + /// Demodulates a PUCCH Format 4 transmission. See \ref pucch_demodulator for more details. + void demodulate(span llr, + const resource_grid_reader& grid, + const channel_estimate& estimates, + const pucch_demodulator::format4_configuration& config); + +private: + /// \brief Channel equalizer. + /// + /// Combines the contributions of all receive antenna ports, equalizing the channels. + std::unique_ptr equalizer; + /// \brief Demodulation mapper. + /// + /// Transforms channel symbols into log-likelihood ratios (i.e., soft bits). + std::unique_ptr demapper; + /// Descrambler component. + std::unique_ptr descrambler; + /// Transform precoder component. + std::unique_ptr precoder; + + /// \brief Buffer used to store channel modulation resource elements at the equalizer output. + /// \remark The symbols are arranged in two dimensions, i.e., resource element and transmit layer. + static_vector eq_re; + + /// \brief Buffer used to transfer symbol noise variances at the equalizer output. + /// \remark The symbols are arranged in two dimensions, i.e., resource element and transmit layer. + static_vector eq_noise_vars; + + /// \brief Reverts the block-wise spreading applied to PUCCH Format 4, as per TS38.211 Section 6.3.2.6.3. + /// \param[out] original Destination buffer for the original symbols. + /// \param[out] noise_vars Destination buffer for the noise variances. + /// \param[in] eq_re Input buffer for the spread symbols. + /// \param[in] eq_noise_vars Input buffer for the noise variances. + /// \param[in] config PUCCH Format 4 configuration parameters. + static void inverse_blockwise_spreading(span original, + span noise_vars, + span eq_re, + span eq_noise_vars, + const pucch_demodulator::format4_configuration& config); +}; + +} // namespace srsran diff --git a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_impl.h b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_impl.h index 47440d1979..806f5a65e6 100644 --- a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_impl.h +++ b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_impl.h @@ -27,6 +27,7 @@ #include "pucch_demodulator_format2.h" #include "pucch_demodulator_format3.h" +#include "pucch_demodulator_format4.h" #include "srsran/phy/upper/channel_processors/pucch/pucch_demodulator.h" namespace srsran { @@ -39,11 +40,15 @@ class pucch_demodulator_impl : public pucch_demodulator /// \param[in] demodulator_format2_ PUCCH Format 2 demodulator. /// \param[in] demodulator_format3_ PUCCH Format 3 demodulator. pucch_demodulator_impl(std::unique_ptr demodulator_format2_, - std::unique_ptr demodulator_format3_) : - demodulator_format2(std::move(demodulator_format2_)), demodulator_format3(std::move(demodulator_format3_)) + std::unique_ptr demodulator_format3_, + std::unique_ptr demodulator_format4_) : + demodulator_format2(std::move(demodulator_format2_)), + demodulator_format3(std::move(demodulator_format3_)), + demodulator_format4(std::move(demodulator_format4_)) { srsran_assert(demodulator_format2, "Invalid pointer to pucch_demodulator_format2 object."); srsran_assert(demodulator_format3, "Invalid pointer to pucch_demodulator_format3 object."); + srsran_assert(demodulator_format4, "Invalid pointer to pucch_demodulator_format4 object."); } // See interface for the documentation. @@ -70,7 +75,7 @@ class pucch_demodulator_impl : public pucch_demodulator const channel_estimate& estimates, const format4_configuration& config) override { - srsran_assertion_failure("PUCCH Format 4 not supported."); + demodulator_format4->demodulate(llr, grid, estimates, config); } private: @@ -78,6 +83,8 @@ class pucch_demodulator_impl : public pucch_demodulator std::unique_ptr demodulator_format2; /// PUCCH demodulator Format 3 component. std::unique_ptr demodulator_format3; + /// PUCCH demodulator Format 4 component. + std::unique_ptr demodulator_format4; }; } // namespace srsran diff --git a/lib/phy/upper/channel_processors/pucch/pucch_detector_format1.cpp b/lib/phy/upper/channel_processors/pucch/pucch_detector_format1.cpp index 753c4f06bf..5b0841ec36 100644 --- a/lib/phy/upper/channel_processors/pucch/pucch_detector_format1.cpp +++ b/lib/phy/upper/channel_processors/pucch/pucch_detector_format1.cpp @@ -31,7 +31,7 @@ using namespace srsran; // Pre-generated orthogonal cover code. -static const pucch_orthogonal_sequence occ; +static const pucch_orthogonal_sequence_format1 occ; static void validate_config(const pucch_detector::format1_configuration& config) { diff --git a/lib/phy/upper/signal_processors/pucch/dmrs_pucch_processor_format1_impl.cpp b/lib/phy/upper/signal_processors/pucch/dmrs_pucch_processor_format1_impl.cpp index 4f07929268..75270dfaac 100644 --- a/lib/phy/upper/signal_processors/pucch/dmrs_pucch_processor_format1_impl.cpp +++ b/lib/phy/upper/signal_processors/pucch/dmrs_pucch_processor_format1_impl.cpp @@ -21,14 +21,14 @@ */ #include "dmrs_pucch_processor_format1_impl.h" +#include "../../../../../include/srsran/phy/upper/pucch_orthogonal_sequence.h" #include "srsran/phy/upper/pucch_helper.h" -#include "srsran/phy/upper/pucch_orthogonal_sequence.h" #include "srsran/srsvec/sc_prod.h" using namespace srsran; // Pre-generated orthogonal cover code. -static const pucch_orthogonal_sequence occ; +static const pucch_orthogonal_sequence_format1 occ; // DM-RS symbol allocation pattern for PUCCH Format 1, as defined in TS38.211 Section 6.4.1.3.1.2. static const bounded_bitset pucch_format1_dmrs_symb_mask = diff --git a/lib/phy/upper/signal_processors/srs/srs_estimator_generic_impl.cpp b/lib/phy/upper/signal_processors/srs/srs_estimator_generic_impl.cpp index 21e652b542..ca454de1fa 100644 --- a/lib/phy/upper/signal_processors/srs/srs_estimator_generic_impl.cpp +++ b/lib/phy/upper/signal_processors/srs/srs_estimator_generic_impl.cpp @@ -31,8 +31,11 @@ #include "srsran/ran/srs/srs_constants.h" #include "srsran/ran/srs/srs_information.h" #include "srsran/srsvec/add.h" +#include "srsran/srsvec/dot_prod.h" #include "srsran/srsvec/mean.h" #include "srsran/srsvec/prod.h" +#include "srsran/srsvec/sc_prod.h" +#include "srsran/srsvec/subtract.h" using namespace srsran; @@ -67,8 +70,8 @@ srs_estimator_result srs_estimator_generic_impl::estimate(const resource_grid_re srsran_assert(!config.ports.empty(), "Receive port list is empty."); unsigned nof_rx_ports = config.ports.size(); - unsigned nof_antenna_ports = static_cast(config.resource.nof_antenna_ports); - unsigned nof_symbols = static_cast(config.resource.nof_symbols); + auto nof_antenna_ports = static_cast(config.resource.nof_antenna_ports); + auto nof_symbols = static_cast(config.resource.nof_symbols); unsigned nof_symbols_per_slot = get_nsymb_per_slot(cyclic_prefix::NORMAL); srsran_assert(config.resource.start_symbol.value() + nof_symbols <= nof_symbols_per_slot, "The start symbol index (i.e., {}) plus the number of symbols (i.e., {}) exceeds the number of symbols " @@ -81,7 +84,7 @@ srs_estimator_result srs_estimator_generic_impl::estimate(const resource_grid_re subcarrier_spacing scs = to_subcarrier_spacing(config.slot.numerology()); // Extract comb size. - unsigned comb_size = static_cast(config.resource.comb_size); + auto comb_size = static_cast(config.resource.comb_size); srs_information common_info = get_srs_information(config.resource, 0); @@ -103,21 +106,40 @@ srs_estimator_result srs_estimator_generic_impl::estimate(const resource_grid_re static_tensor<3, cf_t, max_seq_length * srs_constants::max_nof_rx_ports * srs_constants::max_nof_tx_ports> temp_lse( {sequence_length, nof_rx_ports, nof_antenna_ports}); + // All sequences of pilots. + static_tensor<2, cf_t, max_seq_length * srs_constants::max_nof_tx_ports> all_sequences( + {sequence_length, nof_antenna_ports}); + + // Auxiliary buffer for noise computation. + static_tensor<3, cf_t, 2 * max_seq_length * srs_constants::max_nof_rx_ports> temp_noise( + {sequence_length, 2, nof_rx_ports}); + srsvec::zero(temp_noise.get_data()); + + srs_information info_port0 = get_srs_information(config.resource, /*antenna_port*/ 0); + bool interleaved_pilots = (nof_antenna_ports == 4) && (info_port0.n_cs >= info_port0.n_cs_max / 2); + + float epre = 0; // Iterate transmit ports. for (unsigned i_antenna_port = 0; i_antenna_port != nof_antenna_ports; ++i_antenna_port) { // Obtain SRS information for a given SRS antenna port. srs_information info = get_srs_information(config.resource, i_antenna_port); - // Generate sequence. - static_vector sequence(info.sequence_length); + // Generate sequence and store them in all_sequences. + span sequence = all_sequences.get_view({i_antenna_port}); deps.sequence_generator->generate(sequence, info.sequence_group, info.sequence_number, info.n_cs, info.n_cs_max); + // For the current Tx antenna, keep track of all the LSEs at all Rx ports. + modular_re_buffer_reader port_lse(nof_rx_ports, sequence_length); + // Iterate receive ports. for (unsigned i_rx_port_index = 0; i_rx_port_index != nof_rx_ports; ++i_rx_port_index) { unsigned i_rx_port = config.ports[i_rx_port_index]; // View to the mean LSE for a port combination. span mean_lse = temp_lse.get_view({i_rx_port_index, i_antenna_port}); + // View for noise computation: with interleaved pilots, we need to keep track of two different sets of REs - those + // for odd-indexed ports and those for even-indexed ports. + span noise_help = temp_noise.get_view({(interleaved_pilots) ? i_antenna_port % 2 : 0U, i_rx_port_index}); // Extract sequence for all symbols and average LSE. for (unsigned i_symbol = config.resource.start_symbol.value(), @@ -128,6 +150,13 @@ srs_estimator_result srs_estimator_generic_impl::estimate(const resource_grid_re static_vector rx_sequence(info.sequence_length); grid.get(rx_sequence, i_rx_port, i_symbol, info.mapping_initial_subcarrier, info.comb_size); + // Since the same SRS sequence is sent over all symbols, it makes sense to average out the noise. When pilots + // are interleaved, we need to keep track of two different sets of REs. + if ((i_antenna_port == 0) || (interleaved_pilots && (i_antenna_port == 1))) { + srsvec::add(noise_help, rx_sequence, noise_help); + epre += srsvec::average_power(rx_sequence); + } + // Temporary LSE. span lse = rx_sequence; @@ -150,23 +179,25 @@ srs_estimator_result srs_estimator_generic_impl::estimate(const resource_grid_re srsvec::sc_prod(mean_lse, 1.0 / static_cast(nof_symbols), mean_lse); } - // Estimate TA. - time_alignment_measurement ta_meas = deps.ta_estimator->estimate(mean_lse, info.comb_size, scs, max_ta); - - // Combine time alignment measurement. - result.time_alignment.time_alignment += ta_meas.time_alignment; - result.time_alignment.min = std::max(result.time_alignment.min, ta_meas.min); - result.time_alignment.max = std::min(result.time_alignment.max, ta_meas.max); - result.time_alignment.resolution = std::max(result.time_alignment.resolution, ta_meas.resolution); + port_lse.set_slice(i_rx_port_index, mean_lse); } + // Estimate TA. + time_alignment_measurement ta_meas = deps.ta_estimator->estimate(port_lse, info.comb_size, scs, max_ta); + + // Combine time alignment measurements. + result.time_alignment.time_alignment += ta_meas.time_alignment; + result.time_alignment.min = std::max(result.time_alignment.min, ta_meas.min); + result.time_alignment.max = std::min(result.time_alignment.max, ta_meas.max); + result.time_alignment.resolution = std::max(result.time_alignment.resolution, ta_meas.resolution); } // Average time alignment across all paths. - result.time_alignment.time_alignment /= nof_antenna_ports * nof_rx_ports; + result.time_alignment.time_alignment /= nof_antenna_ports; + float noise_var = 0; // Compensate time alignment and estimate channel coefficients. - for (unsigned i_antenna_port = 0; i_antenna_port != nof_antenna_ports; ++i_antenna_port) { - for (unsigned i_rx_port = 0; i_rx_port != nof_rx_ports; ++i_rx_port) { + for (unsigned i_rx_port = 0; i_rx_port != nof_rx_ports; ++i_rx_port) { + for (unsigned i_antenna_port = 0; i_antenna_port != nof_antenna_ports; ++i_antenna_port) { // View to the mean LSE for a port combination. span mean_lse = temp_lse.get_view({i_rx_port, i_antenna_port}); @@ -174,7 +205,7 @@ srs_estimator_result srs_estimator_generic_impl::estimate(const resource_grid_re srs_information info = get_srs_information(config.resource, i_antenna_port); // Calculate subcarrier phase shift in radians. - float phase_shift_subcarrier = + auto phase_shift_subcarrier = static_cast(TWOPI * result.time_alignment.time_alignment * scs_to_khz(scs) * 1000 * comb_size); // Calculate the initial phase shift in radians. @@ -186,11 +217,44 @@ srs_estimator_result srs_estimator_generic_impl::estimate(const resource_grid_re // Calculate channel wideband coefficient. cf_t coefficient = srsvec::mean(mean_lse); result.channel_matrix.set_coefficient(coefficient, i_rx_port, i_antenna_port); + + // View for noise computation: with interleaved pilots, we need to keep track of two different sets of REs - those + // for odd-indexed ports and those for even-indexed ports. + span noise_help = temp_noise.get_view({(interleaved_pilots) ? i_antenna_port % 2 : 0U, i_rx_port}); + + if ((i_antenna_port == 0) || (interleaved_pilots && (i_antenna_port == 1))) { + compensate_phase_shift(noise_help, phase_shift_subcarrier, phase_shift_offset); + } + + // We recover the signal by multiplying the SRS sequence by the channel coefficient and we remove it from + // noise_help. Recall that the latter contains the contribution of all symbols, so the reconstructed symbol must + // also be counted nof_symbols times. + static_vector recovered_signal(noise_help.size()); + srsvec::sc_prod( + all_sequences.get_view({i_antenna_port}), static_cast(nof_symbols) * coefficient, recovered_signal); + srsvec::subtract(noise_help, recovered_signal, noise_help); + } + span noise_help = temp_noise.get_view({0U, i_rx_port}); + noise_var += srsvec::average_power(noise_help) * noise_help.size(); + + if (interleaved_pilots) { + noise_help = temp_noise.get_view({1U, i_rx_port}); + noise_var += srsvec::average_power(noise_help) * noise_help.size(); } } - // Set noise variance to zero. - result.noise_variance = near_zero; + // At this point, noise_var contains the sum of all the squared errors between the received signal and the + // reconstructed one. For each Rx port, the number of degrees of freedom used to estimate the channel coefficients is + // usually equal nof_antenna_ports, but when pilots are interleaved, in which case it's 2. Also, when interleaving + // pilots, we look at double the samples. + unsigned nof_estimates = (interleaved_pilots ? 2 : nof_antenna_ports); + unsigned correction_factor = (interleaved_pilots ? 2 : 1); + noise_var /= static_cast((nof_symbols * sequence_length - nof_estimates) * correction_factor * nof_rx_ports); + epre /= static_cast(nof_symbols * correction_factor * nof_rx_ports); + + // Set noise variance and EPRE. + result.noise_variance = noise_var; + result.epre_dB = convert_power_to_dB(epre); return result; } diff --git a/lib/phy/upper/signal_processors/srs/srs_validator_generic_impl.cpp b/lib/phy/upper/signal_processors/srs/srs_validator_generic_impl.cpp index 2c8437c331..cb119c9863 100644 --- a/lib/phy/upper/signal_processors/srs/srs_validator_generic_impl.cpp +++ b/lib/phy/upper/signal_processors/srs/srs_validator_generic_impl.cpp @@ -25,27 +25,27 @@ using namespace srsran; -bool srs_validator_generic_impl::is_valid(const srs_estimator_configuration& config) const +error_type srs_validator_generic_impl::is_valid(const srs_estimator_configuration& config) const { // Check the SRS resource is valid. if (!config.resource.is_valid()) { - return false; + return make_unexpected("Invalid SRS resource configuration."); } // Frequency hopping is not supported. if (config.resource.has_frequency_hopping()) { - return false; + return make_unexpected("The SRS estimator does not support frequency hopping."); } // Sequence and group hopping is not supported. if (config.resource.hopping != srs_resource_configuration::group_or_sequence_hopping_enum::neither) { - return false; + return make_unexpected("The SRS estimator does not support group or sequence hopping."); } // Invalid receive port list. if (config.ports.empty()) { - return false; + return make_unexpected("Empty list of Rx ports for the SRS estimator."); } - return true; + return default_success_t(); } diff --git a/lib/phy/upper/signal_processors/srs/srs_validator_generic_impl.h b/lib/phy/upper/signal_processors/srs/srs_validator_generic_impl.h index 0ba093bfbb..988602d91d 100644 --- a/lib/phy/upper/signal_processors/srs/srs_validator_generic_impl.h +++ b/lib/phy/upper/signal_processors/srs/srs_validator_generic_impl.h @@ -33,7 +33,7 @@ class srs_validator_generic_impl : public srs_estimator_configuration_validator { public: // See interface for documentation. - bool is_valid(const srs_estimator_configuration& config) const override; + error_type is_valid(const srs_estimator_configuration& config) const override; }; } // namespace srsran diff --git a/lib/phy/upper/uplink_processor_impl.cpp b/lib/phy/upper/uplink_processor_impl.cpp index 14ffef8f5e..a4cbba60fe 100644 --- a/lib/phy/upper/uplink_processor_impl.cpp +++ b/lib/phy/upper/uplink_processor_impl.cpp @@ -128,18 +128,23 @@ void uplink_processor_impl::process_pucch(upper_phy_rx_results_notifier& not switch (pdu.context.format) { case pucch_format::FORMAT_0: proc_result = pucch_proc->process(grid.get_reader(), pdu.format0); + l1_tracer << trace_event("pucch0", tp); break; case pucch_format::FORMAT_1: proc_result = pucch_proc->process(grid.get_reader(), pdu.format1); + l1_tracer << trace_event("pucch1", tp); break; case pucch_format::FORMAT_2: proc_result = pucch_proc->process(grid.get_reader(), pdu.format2); + l1_tracer << trace_event("pucch2", tp); break; case pucch_format::FORMAT_3: proc_result = pucch_proc->process(grid.get_reader(), pdu.format3); + l1_tracer << trace_event("pucch3", tp); break; case pucch_format::FORMAT_4: proc_result = pucch_proc->process(grid.get_reader(), pdu.format4); + l1_tracer << trace_event("pucch4", tp); break; default: srsran_assert(0, "Invalid PUCCH format={}", pdu.context.format); @@ -152,8 +157,6 @@ void uplink_processor_impl::process_pucch(upper_phy_rx_results_notifier& not // Notify the PUCCH results. notifier.on_new_pucch_results(result); - - l1_tracer << trace_event("process_pucch", tp); } void uplink_processor_impl::process_srs(upper_phy_rx_results_notifier& notifier, diff --git a/lib/phy/upper/upper_phy_factories.cpp b/lib/phy/upper/upper_phy_factories.cpp index 254b957dbe..14b01f8611 100644 --- a/lib/phy/upper/upper_phy_factories.cpp +++ b/lib/phy/upper/upper_phy_factories.cpp @@ -35,6 +35,7 @@ #include "srsran/phy/upper/channel_processors/pdsch/factories.h" #include "srsran/phy/upper/channel_processors/pucch/factories.h" #include "srsran/phy/upper/channel_processors/pusch/factories.h" +#include "srsran/phy/upper/channel_processors/pusch/pusch_processor_phy_capabilities.h" #include "srsran/phy/upper/signal_processors/srs/srs_estimator_factory.h" #include "srsran/phy/upper/unique_rx_buffer.h" #include "srsran/support/error_handling.h" @@ -347,6 +348,14 @@ create_ul_resource_grid_pool(const upper_phy_config& config, std::shared_ptr create_ul_processor_factory(const upper_phy_config& config) { + // Verify the PUSCH processor capabilities. + pusch_processor_phy_capabilities pusch_capabilities = get_pusch_processor_phy_capabilities(); + report_fatal_error_if_not(pusch_capabilities.max_nof_layers >= config.pusch_max_nof_layers, + "The configured PUSCH maximum number of layers (i.e., {}) exceeds the maximum PUSCH " + "processor capable number of layers (i.e., {}).", + pusch_capabilities.max_nof_layers, + config.pusch_max_nof_layers); + std::shared_ptr sequence_factory = create_low_papr_sequence_generator_sw_factory(); report_fatal_error_if_not(sequence_factory, "Invalid sequence factory."); @@ -424,7 +433,7 @@ static std::shared_ptr create_ul_processor_factory(con decoder_config.nof_pusch_decoder_threads = config.nof_pusch_decoder_threads; decoder_config.executor = config.pusch_decoder_executor; decoder_config.nof_prb = config.ul_bw_rb; - decoder_config.nof_layers = 1; + decoder_config.nof_layers = config.pusch_max_nof_layers; pusch_config.decoder_factory = create_pusch_decoder_factory_sw(decoder_config); } else { pusch_decoder_factory_hw_configuration decoder_config; @@ -478,7 +487,7 @@ static std::shared_ptr create_ul_processor_factory(con // :TODO: check these values in the future. Extract them to more public config. pusch_config.ch_estimate_dimensions.nof_symbols = MAX_NSYMB_PER_SLOT; - pusch_config.ch_estimate_dimensions.nof_tx_layers = 1; + pusch_config.ch_estimate_dimensions.nof_tx_layers = config.pusch_max_nof_layers; pusch_config.ch_estimate_dimensions.nof_prb = config.ul_bw_rb; pusch_config.ch_estimate_dimensions.nof_rx_ports = config.nof_rx_ports; std::shared_ptr pusch_factory = create_pusch_processor_factory_sw(pusch_config); @@ -794,7 +803,7 @@ srsran::create_downlink_processor_factory_sw(const downlink_processor_factory_sw // Create concurrent PDSCH processor factory base. pdsch_proc_factory = - create_pdsch_concurrent_processor_factory_sw(crc_calc_factory, + create_pdsch_concurrent_processor_factory_sw(ldpc_seg_tx_factory, ldpc_enc_factory, ldpc_rm_factory, prg_factory, diff --git a/lib/phy/upper/upper_phy_pdu_validators.h b/lib/phy/upper/upper_phy_pdu_validators.h index 4da91feb36..0f592c808a 100644 --- a/lib/phy/upper/upper_phy_pdu_validators.h +++ b/lib/phy/upper/upper_phy_pdu_validators.h @@ -74,7 +74,10 @@ class uplink_processor_validator_impl : public uplink_pdu_validator { return pusch->is_valid(config); } - bool is_valid(const srs_estimator_configuration& config) const override { return srs->is_valid(config); } + error_type is_valid(const srs_estimator_configuration& config) const override + { + return srs->is_valid(config); + } private: std::unique_ptr prach; diff --git a/lib/phy/upper/upper_phy_rx_symbol_handler_impl.h b/lib/phy/upper/upper_phy_rx_symbol_handler_impl.h index a70e6ee85f..0881bf9be9 100644 --- a/lib/phy/upper/upper_phy_rx_symbol_handler_impl.h +++ b/lib/phy/upper/upper_phy_rx_symbol_handler_impl.h @@ -46,7 +46,7 @@ class rx_payload_buffer_pool /// Maximum number of slots to store. static constexpr size_t nof_slots = 40U; /// Maximum number of bits that could potentially be allocated in a slot. - static constexpr units::bits max_buffer_size = units::bits(MAX_RB * 156 * 8); + static constexpr units::bits max_buffer_size = units::bits(MAX_RB * 156 * 8 * 2); /// Minimum block size. It ensures that the payload offsets are selected using multiples of blocks. static constexpr unsigned min_block_size = 64; @@ -57,7 +57,8 @@ class rx_payload_buffer_pool // Convert the maximum buffer size from bits to bytes for comparison and allocation. static constexpr units::bytes max_buffer_size_bytes = max_buffer_size.truncate_to_bytes(); - srsran_assert(size <= max_buffer_size_bytes, "Buffer size (i.e., {}) exceeds maximum {}.", size, pool.size()); + srsran_assert( + size <= max_buffer_size_bytes, "Buffer size (i.e., {}) exceeds maximum {}.", size, max_buffer_size_bytes); // Round the number of consumed bytes to the next block. size_t count = divide_ceil(size.value(), min_block_size) * min_block_size; diff --git a/lib/phy/upper/upper_phy_rx_symbol_handler_printer_decorator.h b/lib/phy/upper/upper_phy_rx_symbol_handler_printer_decorator.h index 91210fb5ee..c98825f228 100644 --- a/lib/phy/upper/upper_phy_rx_symbol_handler_printer_decorator.h +++ b/lib/phy/upper/upper_phy_rx_symbol_handler_printer_decorator.h @@ -24,7 +24,6 @@ #include "srsran/phy/support/resource_grid_reader.h" #include "srsran/phy/support/shared_resource_grid.h" -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/conversion.h" #include "srsran/support/error_handling.h" #include "srsran/support/executors/task_worker.h" @@ -122,7 +121,7 @@ class upper_phy_rx_symbol_handler_printer_decorator : public upper_phy_rx_symbol span samples = buffer.get_symbol(i_port, 0, 0, i_replica); // Convert samples to complex float. - span samples_cf = temp_prach_buffer.first(samples.size()); + span samples_cf = span(temp_prach_buffer).first(samples.size()); srsvec::convert(samples_cf, samples); // Write file. @@ -154,8 +153,8 @@ class upper_phy_rx_symbol_handler_printer_decorator : public upper_phy_rx_symbol srslog::basic_logger& logger; std::ofstream file; task_worker worker; - srsvec::aligned_vec temp_buffer; - srsvec::aligned_vec temp_prach_buffer; + std::vector temp_buffer; + std::vector temp_prach_buffer; unsigned nof_symbols; unsigned start_port; unsigned end_port; diff --git a/lib/ran/pusch/pusch_antenna_port_mapping.cpp b/lib/ran/pusch/pusch_antenna_port_mapping.cpp index ba1248abac..1d5decb017 100644 --- a/lib/ran/pusch/pusch_antenna_port_mapping.cpp +++ b/lib/ran/pusch/pusch_antenna_port_mapping.cpp @@ -27,10 +27,10 @@ using namespace srsran; // Current range of supported number of layers. static constexpr interval nof_layers_range(1, 1); -unsigned srsran::get_pusch_antenna_port_mapping_row_index(unsigned nof_layers, - bool transform_precoder, - dmrs_config_type dmrs_cfg_type, - dmrs_max_length dmrs_max_len) +SRSRAN_WEAK_SYMB unsigned srsran::get_pusch_antenna_port_mapping_row_index(unsigned nof_layers, + bool transform_precoder, + dmrs_config_type dmrs_cfg_type, + dmrs_max_length dmrs_max_len) { srsran_assert(nof_layers_range.contains(nof_layers), "The number of layers (i.e., {}) is out of the range {}.", @@ -50,13 +50,14 @@ unsigned srsran::get_pusch_antenna_port_mapping_row_index(unsigned nof_l return 2; } -unsigned srsran::get_pusch_precoding_info_row_index(unsigned nof_layers, - unsigned max_rank, - srs_resource_configuration::one_two_four_enum nof_srs_ports, - bool transform_precoder, - dmrs_config_type dmrs_cfg_type, - dmrs_max_length dmrs_max_len, - unsigned tpmi) +SRSRAN_WEAK_SYMB unsigned +srsran::get_pusch_precoding_info_row_index(unsigned nof_layers, + unsigned max_rank, + srs_resource_configuration::one_two_four_enum nof_srs_ports, + bool transform_precoder, + dmrs_config_type dmrs_cfg_type, + dmrs_max_length dmrs_max_len, + unsigned tpmi) { static constexpr interval max_rank_range(1, 4); srsran_assert( diff --git a/lib/ran/pusch/pusch_tpmi_select.cpp b/lib/ran/pusch/pusch_tpmi_select.cpp index bb83a453b2..c1934a15d6 100644 --- a/lib/ran/pusch/pusch_tpmi_select.cpp +++ b/lib/ran/pusch/pusch_tpmi_select.cpp @@ -308,11 +308,12 @@ static pusch_tpmi_select_info::tpmi_info get_tpmi_select_info_2layer(const srs_c pusch_tpmi_select_info srsran::get_tpmi_select_info(const srs_channel_matrix& channel, float noise_variance, + unsigned max_rank, tx_scheme_codebook_subset codebook_subset) { unsigned nof_tx_ports = channel.get_nof_tx_ports(); unsigned nof_rx_ports = channel.get_nof_rx_ports(); - unsigned max_nof_layers = std::min(nof_tx_ports, nof_rx_ports); + unsigned max_nof_layers = std::min(std::min(nof_tx_ports, nof_rx_ports), max_rank); static_vector info; diff --git a/lib/rlc/rlc_rx_am_entity.cpp b/lib/rlc/rlc_rx_am_entity.cpp index 94156c55c6..b29689c8df 100644 --- a/lib/rlc/rlc_rx_am_entity.cpp +++ b/lib/rlc/rlc_rx_am_entity.cpp @@ -21,7 +21,6 @@ */ #include "rlc_rx_am_entity.h" -#include "../support/sdu_window_impl.h" #include "srsran/adt/scope_exit.h" #include "srsran/instrumentation/traces/up_traces.h" #include "srsran/support/format/fmt_optional.h" @@ -41,7 +40,7 @@ rlc_rx_am_entity::rlc_rx_am_entity(gnb_du_id_t gnb_du_id, cfg(config), mod(cardinality(to_number(cfg.sn_field_length))), am_window_size(window_size(to_number(cfg.sn_field_length))), - rx_window(create_rx_window(cfg.sn_field_length)), + rx_window(logger, window_size(to_number(cfg.sn_field_length))), status_buf({rlc_am_status_pdu(cfg.sn_field_length), rlc_am_status_pdu(cfg.sn_field_length), rlc_am_status_pdu(cfg.sn_field_length)}), @@ -81,9 +80,13 @@ rlc_rx_am_entity::rlc_rx_am_entity(gnb_du_id_t gnb_du_id, max_nof_sn_per_status_report = window_size(to_number(cfg.sn_field_length)); } - // initialize status report - status_cached->ack_sn = st.rx_next_highest; - status_report_size.store(status_cached->get_packed_size(), std::memory_order_relaxed); + // initialize cached status report + if (!std::atomic::is_always_lock_free) { + logger.log_error("The status PDU exchange is not lock free. TX real-time performance can be impaired."); + } + rlc_am_status_pdu& init_status_for_exchange = *status_for_exchange.load(std::memory_order_relaxed); + init_status_for_exchange.ack_sn = st.rx_next_highest; + status_report_size.store(init_status_for_exchange.get_packed_size(), std::memory_order_relaxed); logger.log_info("RLC AM configured. {}", cfg); } @@ -128,7 +131,7 @@ void rlc_rx_am_entity::handle_data_pdu(byte_buffer_slice buf) auto on_function_exit = make_scope_exit([&]() { logger.log_debug( "Post-processing for AMD PDU: status_changed={} status_requested={}", status_changed, status_requested); - if (status_changed) { + if (status_changed || status_requested) { refresh_status_report(); } if (status_requested) { @@ -179,7 +182,7 @@ void rlc_rx_am_entity::handle_data_pdu(byte_buffer_slice buf) } // Section 5.2.3.2.2, discard duplicate PDUs - if (rx_window->has_sn(header.sn) && (*rx_window)[header.sn].fully_received) { + if (rx_window.has_sn(header.sn) && rx_window[header.sn].fully_received) { logger.log_debug("Discarded PDU duplicate. sn={}", header.sn); return; } @@ -207,12 +210,12 @@ void rlc_rx_am_entity::handle_data_pdu(byte_buffer_slice buf) /* * - if all bytes of the RLC SDU with SN = x are received: */ - if (rx_window->has_sn(header.sn) && (*rx_window)[header.sn].fully_received) { + if (rx_window.has_sn(header.sn) && rx_window[header.sn].fully_received) { /* * - reassemble the RLC SDU from AMD PDU(s) with SN = x, remove RLC headers when doing so and deliver * the reassembled RLC SDU to upper layer; */ - rlc_rx_am_sdu_info& sdu_info = (*rx_window)[header.sn]; + rlc_rx_am_sdu_info& sdu_info = rx_window[header.sn]; expected sdu = reassemble_sdu(sdu_info, header.sn); if (!sdu) { logger.log_error("Dropped SDU, failed to reassemble. sn={}", header.sn); @@ -238,8 +241,8 @@ void rlc_rx_am_entity::handle_data_pdu(byte_buffer_slice buf) uint32_t sn_upd = 0; for (sn_upd = (st.rx_highest_status + 1) % mod; rx_mod_base(sn_upd) < rx_mod_base(st.rx_next_highest); sn_upd = (sn_upd + 1) % mod) { - if (rx_window->has_sn(sn_upd)) { - if (not(*rx_window)[sn_upd].fully_received) { + if (rx_window.has_sn(sn_upd)) { + if (not rx_window[sn_upd].fully_received) { break; // first SDU not fully received } } else { @@ -261,13 +264,13 @@ void rlc_rx_am_entity::handle_data_pdu(byte_buffer_slice buf) // move rx_next forward and remove all fully received SDUs from rx_window for (sn_upd = (st.rx_next) % mod; rx_mod_base(sn_upd) < rx_mod_base(st.rx_next_highest); sn_upd = (sn_upd + 1) % mod) { - if (rx_window->has_sn(sn_upd)) { - if (not(*rx_window)[sn_upd].fully_received) { + if (rx_window.has_sn(sn_upd)) { + if (not rx_window[sn_upd].fully_received) { break; // first SDU not fully received } // RX_Next serves as the lower edge of the receiving window // As such, we remove any SDU from the window if we update this value - rx_window->remove_sn(sn_upd); + rx_window.remove_sn(sn_upd); } else { break; // first SDU not fully received } @@ -294,7 +297,7 @@ void rlc_rx_am_entity::handle_data_pdu(byte_buffer_slice buf) stop_reassembly_timer = true; } if (rx_mod_base(st.rx_next_status_trigger) == rx_mod_base(st.rx_next + 1)) { - if (not(*rx_window)[st.rx_next].has_gap) { + if (not rx_window[st.rx_next].has_gap) { stop_reassembly_timer = true; } } @@ -321,7 +324,7 @@ void rlc_rx_am_entity::handle_data_pdu(byte_buffer_slice buf) restart_reassembly_timer = true; } if (rx_mod_base(st.rx_next_highest) == rx_mod_base(st.rx_next + 1)) { - if (rx_window->has_sn(st.rx_next) && (*rx_window)[st.rx_next].has_gap) { + if (rx_window.has_sn(st.rx_next) && rx_window[st.rx_next].has_gap) { restart_reassembly_timer = true; } } @@ -344,8 +347,8 @@ bool rlc_rx_am_entity::handle_full_data_sdu(const rlc_am_pdu_header& header, byt logger.log_debug("RX SDU. payload_len={} {}", payload.length(), header); // Add new SN to RX window if no segments have been received yet - rlc_rx_am_sdu_info& rx_sdu = rx_window->has_sn(header.sn) ? (*rx_window)[header.sn] : ([&]() -> rlc_rx_am_sdu_info& { - rlc_rx_am_sdu_info& sdu = rx_window->add_sn(header.sn); + rlc_rx_am_sdu_info& rx_sdu = rx_window.has_sn(header.sn) ? rx_window[header.sn] : ([&]() -> rlc_rx_am_sdu_info& { + rlc_rx_am_sdu_info& sdu = rx_window.add_sn(header.sn); sdu.time_of_arrival = std::chrono::steady_clock::now(); return sdu; })(); @@ -368,8 +371,8 @@ bool rlc_rx_am_entity::handle_segment_data_sdu(const rlc_am_pdu_header& header, logger.log_debug("RX SDU segment. payload_len={} {}", payload.length(), header); // Add new SN to RX window if no segments have been received yet - rlc_rx_am_sdu_info& rx_sdu = rx_window->has_sn(header.sn) ? (*rx_window)[header.sn] : ([&]() -> rlc_rx_am_sdu_info& { - rlc_rx_am_sdu_info& sdu = rx_window->add_sn(header.sn); + rlc_rx_am_sdu_info& rx_sdu = rx_window.has_sn(header.sn) ? rx_window[header.sn] : ([&]() -> rlc_rx_am_sdu_info& { + rlc_rx_am_sdu_info& sdu = rx_window.add_sn(header.sn); sdu.time_of_arrival = std::chrono::steady_clock::now(); return sdu; })(); @@ -547,7 +550,7 @@ expected rlc_rx_am_entity::reassemble_sdu(rlc_rx_am_sdu_info& void rlc_rx_am_entity::refresh_status_report() { - status_builder->reset(); + status_owned_by_writer->reset(); /* * - for the RLC SDUs with SN such that RX_Next <= SN < RX_Highest_Status that has not been completely * received yet, in increasing SN order of RLC SDUs and increasing byte segment order within RLC SDUs, @@ -562,22 +565,22 @@ void rlc_rx_am_entity::refresh_status_report() logger.log_debug( "Generating status PDU. rx_next={} rx_highest_status={} stop_sn={}", st.rx_next, st.rx_highest_status, stop_sn); for (uint32_t i = st.rx_next; rx_mod_base(i) < rx_mod_base(stop_sn); i = (i + 1) % mod) { - if ((rx_window->has_sn(i) && (*rx_window)[i].fully_received)) { + if ((rx_window.has_sn(i) && rx_window[i].fully_received)) { logger.log_debug("SDU complete. sn={}", i); } else { - if (not rx_window->has_sn(i)) { + if (not rx_window.has_sn(i)) { // No segment received, NACK the whole SDU rlc_am_status_nack nack; nack.nack_sn = i; nack.has_so = false; logger.log_debug("Adding nack={}.", nack); - status_builder->push_nack(nack); - } else if (not(*rx_window)[i].fully_received) { - srsran_assert(std::holds_alternative((*rx_window)[i].sdu_data), + status_owned_by_writer->push_nack(nack); + } else if (not rx_window[i].fully_received) { + srsran_assert(std::holds_alternative(rx_window[i].sdu_data), "Invalid sdu_data variant of incomplete SDU in rx_window. sn={}", i); rlc_rx_am_sdu_info::segment_set_t& segments = - std::get((*rx_window)[i].sdu_data); + std::get(rx_window[i].sdu_data); // Some segments were received, but not all. // NACK non consecutive missing bytes uint32_t last_so = 0; @@ -591,7 +594,7 @@ void rlc_rx_am_entity::refresh_status_report() nack.so_start = last_so; nack.so_end = segm->so - 1; // set to last missing byte logger.log_debug("Adding nack={}.", nack); - status_builder->push_nack(nack); + status_owned_by_writer->push_nack(nack); // Sanity check if (nack.so_start > nack.so_end) { @@ -620,7 +623,7 @@ void rlc_rx_am_entity::refresh_status_report() nack.so_start = last_so; nack.so_end = rlc_am_status_nack::so_end_of_sdu; logger.log_debug("Adding nack={}.", nack); - status_builder->push_nack(nack); + status_owned_by_writer->push_nack(nack); // Sanity check srsran_assert(nack.so_start <= nack.so_end, "Invalid segment offsets in nack={}.", nack); } @@ -632,16 +635,17 @@ void rlc_rx_am_entity::refresh_status_report() * - 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. */ - status_builder->ack_sn = stop_sn; - logger.log_debug("Refreshed status_report. {}", *status_builder); + status_owned_by_writer->ack_sn = stop_sn; + logger.log_debug("Refreshed status_report. {}", *status_owned_by_writer); store_status_report(); } void rlc_rx_am_entity::store_status_report() { - std::unique_lock lock(status_report_mutex); - std::swap(status_builder, status_cached); - status_report_size.store(status_cached->get_packed_size(), std::memory_order_relaxed); + // Minor inacurracy between status_report_size and status_for_exchange is tolerated here + uint32_t latest_status_report_size = status_owned_by_writer->get_packed_size(); + status_owned_by_writer = status_for_exchange.exchange(status_owned_by_writer, std::memory_order_relaxed); + status_report_size.store(latest_status_report_size, std::memory_order_release); } rlc_am_status_pdu& rlc_rx_am_entity::get_status_pdu() @@ -653,14 +657,13 @@ rlc_am_status_pdu& rlc_rx_am_entity::get_status_pdu() } status_prohibit_timer_is_running.store(true, std::memory_order_relaxed); } - std::unique_lock lock(status_report_mutex); - std::swap(status_shared, status_cached); - return *status_shared; + status_owned_by_reader = status_for_exchange.exchange(status_owned_by_reader, std::memory_order_relaxed); + return *status_owned_by_reader; } uint32_t rlc_rx_am_entity::get_status_pdu_length() { - return status_report_size.load(std::memory_order_relaxed); + return status_report_size.load(std::memory_order_acquire); } bool rlc_rx_am_entity::status_report_required() @@ -677,26 +680,6 @@ void rlc_rx_am_entity::notify_status_report_changed() } } -std::unique_ptr> rlc_rx_am_entity::create_rx_window(rlc_am_sn_size sn_size) -{ - std::unique_ptr> rx_window_; - switch (sn_size) { - case rlc_am_sn_size::size12bits: - rx_window_ = std::make_unique< - sdu_window_impl>( - logger); - break; - case rlc_am_sn_size::size18bits: - rx_window_ = std::make_unique< - sdu_window_impl>( - logger); - break; - default: - srsran_assertion_failure("Cannot create rx_window for unsupported sn_size={}.", to_number(sn_size)); - } - return rx_window_; -} - /* * Timer handling functions */ @@ -734,7 +717,7 @@ void rlc_rx_am_entity::on_expired_reassembly_timer() */ uint32_t sn_upd = st.rx_next_status_trigger; for (; rx_mod_base(sn_upd) < rx_mod_base(st.rx_next_highest); sn_upd = (sn_upd + 1) % mod) { - if (not rx_window->has_sn(sn_upd) || (rx_window->has_sn(sn_upd) && not(*rx_window)[sn_upd].fully_received)) { + if (not rx_window.has_sn(sn_upd) || (rx_window.has_sn(sn_upd) && not rx_window[sn_upd].fully_received)) { break; } } @@ -749,7 +732,7 @@ void rlc_rx_am_entity::on_expired_reassembly_timer() restart_reassembly_timer = true; } if (rx_mod_base(st.rx_next_highest) == rx_mod_base(st.rx_highest_status + 1)) { - if (rx_window->has_sn(st.rx_highest_status) && (*rx_window)[st.rx_highest_status].has_gap) { + if (rx_window.has_sn(st.rx_highest_status) && rx_window[st.rx_highest_status].has_gap) { restart_reassembly_timer = true; } } @@ -769,6 +752,5 @@ void rlc_rx_am_entity::on_expired_reassembly_timer() notify_status_report_changed(); log_state(srslog::basic_levels::debug); - logger.log_debug("RX window state: nof_sdus={}", rx_window->size()); - return; + logger.log_debug("RX window state: nof_sdus={}", rx_window.size()); } diff --git a/lib/rlc/rlc_rx_am_entity.h b/lib/rlc/rlc_rx_am_entity.h index 5456e70d83..6c47e62f96 100644 --- a/lib/rlc/rlc_rx_am_entity.h +++ b/lib/rlc/rlc_rx_am_entity.h @@ -30,7 +30,7 @@ #include "srsran/support/sdu_window.h" #include "srsran/support/timers.h" #include "fmt/format.h" -#include +#include #include namespace srsran { @@ -110,7 +110,7 @@ class rlc_rx_am_entity : public rlc_rx_entity, public rlc_rx_am_status_provider const uint32_t am_window_size; /// Rx window - std::unique_ptr> rx_window; + sdu_window rx_window; /// Indicates the rx_window has not been changed, i.e. no need to rebuild status report. static const bool rx_window_not_changed = false; /// Indicates the rx_window has been changed, i.e. need to rebuild status report. @@ -119,20 +119,17 @@ class rlc_rx_am_entity : public rlc_rx_entity, public rlc_rx_am_status_provider /// Pre-allocated status reports for (re)-building, caching, and sharing with TX entity std::array status_buf; - /// Status report for (re)-building - rlc_am_status_pdu* status_builder = &status_buf[0]; - /// Status report for caching - rlc_am_status_pdu* status_cached = &status_buf[1]; - /// Status report for sharing - rlc_am_status_pdu* status_shared = &status_buf[2]; + /// Status report owned by writer for (re)-building + rlc_am_status_pdu* status_owned_by_writer = &status_buf[0]; + /// Status report for exchange that is accessed by writer and reader + std::atomic status_for_exchange = &status_buf[1]; + /// Status report owned by reader for transmission + rlc_am_status_pdu* status_owned_by_reader = &status_buf[2]; /// Size of the cached status report std::atomic status_report_size; std::atomic status_prohibit_timer_is_running{false}; - /// Mutex for controlled access to the cached status report, e.g. read by the Tx entity in a different executor - std::mutex status_report_mutex; - /// \brief t-StatusProhibit /// This timer is used by the receiving side of an AM RLC entity in order to prohibit transmission of a STATUS PDU /// (see sub clause 5.3.4). @@ -171,7 +168,7 @@ class rlc_rx_am_entity : public rlc_rx_entity, public rlc_rx_am_status_provider reassembly_timer.stop(); stopped = true; } - }; + } // Rx/Tx interconnect void set_status_handler(rlc_tx_am_status_handler* status_handler_) { status_handler = status_handler_; } @@ -316,11 +313,6 @@ class rlc_rx_am_entity : public rlc_rx_entity, public rlc_rx_am_status_provider /// \param timeout_id The timer ID void on_expired_reassembly_timer(); - /// Creates the rx_window according to sn_size - /// \param sn_size Size of the sequence number (SN) - /// \return unique pointer to rx_window instance - std::unique_ptr> create_rx_window(rlc_am_sn_size sn_size); - void log_state(srslog::basic_levels level) { logger.log(level, "RX entity state. {}", st); } }; diff --git a/lib/rlc/rlc_rx_um_entity.cpp b/lib/rlc/rlc_rx_um_entity.cpp index 2adfd764a4..c879b4c2a2 100644 --- a/lib/rlc/rlc_rx_um_entity.cpp +++ b/lib/rlc/rlc_rx_um_entity.cpp @@ -21,7 +21,6 @@ */ #include "rlc_rx_um_entity.h" -#include "../support/sdu_window_impl.h" using namespace srsran; @@ -38,7 +37,7 @@ rlc_rx_um_entity::rlc_rx_um_entity(gnb_du_id_t gnb_du_id, cfg(config), mod(cardinality(to_number(cfg.sn_field_length))), um_window_size(window_size(to_number(cfg.sn_field_length))), - rx_window(create_rx_window(cfg.sn_field_length)), + rx_window(logger, window_size(to_number(cfg.sn_field_length))), reassembly_timer(ue_timer_factory.create_timer()), pcap_context(ue_index, rb_id, config) { @@ -116,12 +115,12 @@ void rlc_rx_um_entity::handle_pdu(byte_buffer_slice buf) /* * - if all byte segments with SN = x are received: */ - if (rx_window->has_sn(header.sn) && (*rx_window)[header.sn].fully_received) { + if (rx_window.has_sn(header.sn) && rx_window[header.sn].fully_received) { /* * - reassemble the RLC SDU from all byte segments with SN = x, remove RLC headers and deliver the reassembled * RLC SDU to upper layer; */ - rlc_rx_um_sdu_info& sdu_info = (*rx_window)[header.sn]; + rlc_rx_um_sdu_info& sdu_info = rx_window[header.sn]; expected sdu = reassemble_sdu(sdu_info, header.sn); if (!sdu) { logger.log_error("Dropped SDU, failed to reassemble. sn={}", header.sn); @@ -150,13 +149,13 @@ void rlc_rx_um_entity::handle_pdu(byte_buffer_slice buf) // move rx_next_reassembly forward and remove all fully received SDUs from rx_window for (sn_upd = (st.rx_next_reassembly) % mod; rx_mod_base(sn_upd) < rx_mod_base(st.rx_next_highest); sn_upd = (sn_upd + 1) % mod) { - if (rx_window->has_sn(sn_upd)) { - if (not(*rx_window)[sn_upd].fully_received) { + if (rx_window.has_sn(sn_upd)) { + if (not rx_window[sn_upd].fully_received) { break; // first SDU not fully received } // rx_next_reassembly serves as the lower edge of the receiving window // As such, we remove any SDU from the window if we update this value - rx_window->remove_sn(sn_upd); + rx_window.remove_sn(sn_upd); } else { break; // first SDU not fully received } @@ -185,13 +184,13 @@ void rlc_rx_um_entity::handle_pdu(byte_buffer_slice buf) (st.rx_next_highest - um_window_size) % mod, st.rx_next_highest, sn_upd); - if (rx_window->has_sn(sn_upd)) { - if (not(*rx_window)[sn_upd].fully_received) { + if (rx_window.has_sn(sn_upd)) { + if (not rx_window[sn_upd].fully_received) { // count incomplete SDUs as lost logger.log_info("Discarding incomplete SDU. sn={}", sn_upd); metrics.metrics_add_lost_pdus(1); } - rx_window->remove_sn(sn_upd); + rx_window.remove_sn(sn_upd); } else { // count non-existing SDUs as lost logger.log_info("Discarding SDU. sn={}", sn_upd); @@ -206,10 +205,10 @@ void rlc_rx_um_entity::handle_pdu(byte_buffer_slice buf) // Since sn_upd just entered the reassembly window in the previous loop (and everything below was cleaned), // we continue (and clean) until we face any SN that is not fully received for (; rx_mod_base(sn_upd) < rx_mod_base(st.rx_next_highest); sn_upd = (sn_upd + 1) % mod) { - if (rx_window->has_sn(sn_upd) && (*rx_window)[sn_upd].fully_received) { + if (rx_window.has_sn(sn_upd) && rx_window[sn_upd].fully_received) { // rx_next_reassembly serves as the lower edge of the receiving window // As such, we remove any SDU from the window if we update this value - rx_window->remove_sn(sn_upd); + rx_window.remove_sn(sn_upd); } else { break; // first SDU not fully received } @@ -241,7 +240,7 @@ void rlc_rx_um_entity::handle_pdu(byte_buffer_slice buf) stop_reassembly_timer = true; } if (st.rx_next_highest == st.rx_next_reassembly + 1) { - if (rx_window->has_sn(st.rx_next_highest) && not(*rx_window)[st.rx_next_highest].has_gap) { + if (rx_window.has_sn(st.rx_next_highest) && not rx_window[st.rx_next_highest].has_gap) { stop_reassembly_timer = true; } } @@ -267,7 +266,7 @@ void rlc_rx_um_entity::handle_pdu(byte_buffer_slice buf) restart_reassembly_timer = true; } if (rx_mod_base(st.rx_next_highest) == rx_mod_base(st.rx_next_reassembly + 1)) { - if (rx_window->has_sn(st.rx_next_highest) && (*rx_window)[st.rx_next_highest].has_gap) { + if (rx_window.has_sn(st.rx_next_highest) && rx_window[st.rx_next_highest].has_gap) { restart_reassembly_timer = true; } } @@ -291,8 +290,8 @@ bool rlc_rx_um_entity::handle_segment_data_sdu(const rlc_um_pdu_header& header, logger.log_debug("RX SDU segment. payload_len={} {}", payload.length(), header); // Add new SN to RX window if no segments have been received yet - rlc_rx_um_sdu_info& rx_sdu = rx_window->has_sn(header.sn) ? (*rx_window)[header.sn] : ([&]() -> rlc_rx_um_sdu_info& { - rlc_rx_um_sdu_info& sdu = rx_window->add_sn(header.sn); + rlc_rx_um_sdu_info& rx_sdu = rx_window.has_sn(header.sn) ? rx_window[header.sn] : ([&]() -> rlc_rx_um_sdu_info& { + rlc_rx_um_sdu_info& sdu = rx_window.add_sn(header.sn); sdu.time_of_arrival = std::chrono::steady_clock::now(); return sdu; })(); @@ -461,13 +460,13 @@ void rlc_rx_um_entity::on_expired_reassembly_timer() for (; rx_mod_base(sn_upd) < rx_mod_base(st.rx_next_highest); sn_upd = (sn_upd + 1) % mod) { if (rx_mod_base(sn_upd) < rx_mod_base(st.rx_timer_trigger)) { // remove anything below rx_timer_trigger - if (rx_window->has_sn(sn_upd)) { - if (not(*rx_window)[sn_upd].fully_received) { + if (rx_window.has_sn(sn_upd)) { + if (not rx_window[sn_upd].fully_received) { // count incomplete SDUs as lost logger.log_info("Discarding incomplete SDU. sn={}", sn_upd); metrics.metrics_add_lost_pdus(1); } - rx_window->remove_sn(sn_upd); + rx_window.remove_sn(sn_upd); } else { // count non-existing SDUs as lost logger.log_info("Discarding SDU. sn={}", sn_upd); @@ -475,8 +474,8 @@ void rlc_rx_um_entity::on_expired_reassembly_timer() } } else { // continue removing fully received SDUs starting from rx_timer_trigger; stop at first incomplete or unseen SDU. - if (rx_window->has_sn(sn_upd) && (*rx_window)[sn_upd].fully_received) { - rx_window->remove_sn(sn_upd); + if (rx_window.has_sn(sn_upd) && rx_window[sn_upd].fully_received) { + rx_window.remove_sn(sn_upd); } else { break; // first SDU not fully received } @@ -497,7 +496,7 @@ void rlc_rx_um_entity::on_expired_reassembly_timer() restart_reassembly_timer = true; } if (rx_mod_base(st.rx_next_highest) == rx_mod_base(st.rx_next_reassembly + 1)) { - if (rx_window->has_sn(st.rx_next_highest) && (*rx_window)[st.rx_next_highest].has_gap) { + if (rx_window.has_sn(st.rx_next_highest) && rx_window[st.rx_next_highest].has_gap) { restart_reassembly_timer = true; } } @@ -524,23 +523,3 @@ bool rlc_rx_um_entity::sn_invalid_for_rx_buffer(const uint32_t sn) return (rx_mod_base(st.rx_next_highest - um_window_size) <= rx_mod_base(sn) && rx_mod_base(sn) < rx_mod_base(st.rx_next_reassembly)); } - -std::unique_ptr> rlc_rx_um_entity::create_rx_window(rlc_um_sn_size sn_size) -{ - std::unique_ptr> rx_window_; - switch (sn_size) { - case rlc_um_sn_size::size6bits: - rx_window_ = std::make_unique< - sdu_window_impl>( - logger); - break; - case rlc_um_sn_size::size12bits: - rx_window_ = std::make_unique< - sdu_window_impl>( - logger); - break; - default: - srsran_assertion_failure("Cannot create rx_window for unsupported sn_size={}.", to_number(sn_size)); - } - return rx_window_; -} diff --git a/lib/rlc/rlc_rx_um_entity.h b/lib/rlc/rlc_rx_um_entity.h index c9b6b2357e..75fce16bca 100644 --- a/lib/rlc/rlc_rx_um_entity.h +++ b/lib/rlc/rlc_rx_um_entity.h @@ -92,7 +92,7 @@ class rlc_rx_um_entity : public rlc_rx_entity const uint32_t um_window_size; /// Rx window - std::unique_ptr> rx_window; + sdu_window rx_window; /// \brief t-Reassembly /// This timer is used by [...] the receiving side of an UM RLC entity in order to detect loss of RLC PDUs at lower @@ -118,7 +118,7 @@ class rlc_rx_um_entity : public rlc_rx_entity { // Stop all timers. Any queued handlers of timers that just expired before this call are canceled automatically reassembly_timer.stop(); - }; + } void on_expired_reassembly_timer(); @@ -155,11 +155,6 @@ class rlc_rx_um_entity : public rlc_rx_entity /// \return The reassembled SDU in case of success, default_error_t{} otherwise. expected reassemble_sdu(rlc_rx_um_sdu_info& sdu_info, uint32_t sn); - /// Creates the rx_window according to sn_size - /// \param sn_size Size of the sequence number (SN) - /// \return unique pointer to rx_window instance - std::unique_ptr> create_rx_window(rlc_um_sn_size sn_size); - bool sn_in_reassembly_window(const uint32_t sn); bool sn_invalid_for_rx_buffer(const uint32_t sn); diff --git a/lib/rlc/rlc_tx_am_entity.cpp b/lib/rlc/rlc_tx_am_entity.cpp index d512c5fc1a..e0b60dacd1 100644 --- a/lib/rlc/rlc_tx_am_entity.cpp +++ b/lib/rlc/rlc_tx_am_entity.cpp @@ -21,11 +21,11 @@ */ #include "rlc_tx_am_entity.h" -#include "../support/sdu_window_impl.h" #include "srsran/adt/scope_exit.h" #include "srsran/instrumentation/traces/du_traces.h" #include "srsran/pdcp/pdcp_sn_util.h" #include "srsran/ran/pdsch/pdsch_constants.h" +#include "srsran/support/rtsan.h" #include "srsran/support/srsran_assert.h" #include "srsran/support/tracing/event_tracing.h" @@ -59,7 +59,7 @@ rlc_tx_am_entity::rlc_tx_am_entity(gnb_du_id_t gnb_du_i retx_queue(window_size(to_number(cfg.sn_field_length))), mod(cardinality(to_number(cfg.sn_field_length))), am_window_size(window_size(to_number(cfg.sn_field_length))), - tx_window(create_tx_window(cfg.sn_field_length)), + tx_window(logger, window_size(to_number(cfg.sn_field_length))), pdu_recycler(window_size(to_number(cfg.sn_field_length)), logger), head_min_size(rlc_am_pdu_header_min_size(cfg.sn_field_length)), head_max_size(rlc_am_pdu_header_max_size(cfg.sn_field_length)), @@ -142,7 +142,7 @@ void rlc_tx_am_entity::discard_sdu(uint32_t pdcp_sn) } // TS 38.322 v16.2.0 Sec. 5.2.3.1 -size_t rlc_tx_am_entity::pull_pdu(span rlc_pdu_buf) +size_t rlc_tx_am_entity::pull_pdu(span rlc_pdu_buf) SRSRAN_RTSAN_NONBLOCKING { std::chrono::time_point pull_begin; if (metrics_low.is_enabled()) { @@ -155,7 +155,7 @@ size_t rlc_tx_am_entity::pull_pdu(span rlc_pdu_buf) } const size_t grant_len = rlc_pdu_buf.size(); - logger.log_debug("MAC opportunity. grant_len={} tx_window_size={}", grant_len, tx_window->size()); + logger.log_debug("MAC opportunity. grant_len={} tx_window_size={}", grant_len, tx_window.size()); // TX STATUS if requested if (status_provider->status_report_required()) { @@ -214,8 +214,8 @@ size_t rlc_tx_am_entity::pull_pdu(span rlc_pdu_buf) // Send remaining segment, if it exists if (sn_under_segmentation != INVALID_RLC_SN) { - if (tx_window->has_sn(sn_under_segmentation)) { - size_t pdu_len = build_continued_sdu_segment(rlc_pdu_buf, (*tx_window)[sn_under_segmentation]); + if (tx_window.has_sn(sn_under_segmentation)) { + size_t pdu_len = build_continued_sdu_segment(rlc_pdu_buf, tx_window[sn_under_segmentation]); pcap.push_pdu(pcap_context, rlc_pdu_buf.subspan(0, pdu_len)); return pdu_len; } @@ -267,7 +267,7 @@ size_t rlc_tx_am_entity::build_new_pdu(span rlc_pdu_buf) // insert newly assigned SN into window and use reference for in-place operations // NOTE: from now on, we can't return from this function anymore before increasing tx_next - rlc_tx_am_sdu_info& sdu_info = tx_window->add_sn(st.tx_next); + rlc_tx_am_sdu_info& sdu_info = tx_window.add_sn(st.tx_next); sdu_info.sdu = std::move(sdu.buf); // Move SDU into TX window SDU info sdu_info.is_retx = sdu.is_retx; sdu_info.pdcp_sn = sdu.pdcp_sn; @@ -510,7 +510,7 @@ size_t rlc_tx_am_entity::build_retx_pdu(span rlc_pdu_buf) } // Sanity check - drop any retx SNs not present in tx_window - while (not tx_window->has_sn(retx_queue.front().sn)) { + while (not tx_window.has_sn(retx_queue.front().sn)) { logger.log_info("Could not find sn={} in tx window, dropping RETX.", retx_queue.front().sn); retx_queue.pop(); if (retx_queue.empty()) { @@ -524,7 +524,7 @@ size_t rlc_tx_am_entity::build_retx_pdu(span rlc_pdu_buf) retx_sn = retx.sn; // Get sdu_info info from tx_window - rlc_tx_am_sdu_info& sdu_info = (*tx_window)[retx.sn]; + rlc_tx_am_sdu_info& sdu_info = tx_window[retx.sn]; // Check RETX boundaries if (retx.so + retx.length > sdu_info.sdu.length()) { @@ -579,7 +579,7 @@ size_t rlc_tx_am_entity::build_retx_pdu(span rlc_pdu_buf) // the polling bit, to make sure the poll bit is calculated correctly if (retx_complete) { // Check if this SN triggered max_retx - if ((*tx_window)[retx.sn].retx_count == cfg.max_retx_thresh) { + if (tx_window[retx.sn].retx_count == cfg.max_retx_thresh) { max_retx_reached = true; } // remove RETX from queue @@ -648,11 +648,25 @@ void rlc_tx_am_entity::on_status_pdu(rlc_am_status_pdu status) } } -void rlc_tx_am_entity::handle_status_pdu(rlc_am_status_pdu status) +void rlc_tx_am_entity::handle_status_pdu(rlc_am_status_pdu status) SRSRAN_RTSAN_NONBLOCKING { trace_point status_tp = l2_tracer.now(); auto t_start = std::chrono::high_resolution_clock::now(); + auto on_function_exit = make_scope_exit([&]() { + auto t_end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(t_end - t_start); + logger.log_info("Handled status report. t={}us {}", duration.count(), status); + + // redirect deletion of status report to UE executor + auto delete_status_pdu_func = [status = std::move(status)]() mutable { + // leaving this scope will implicitly delete the status PDU + }; + if (not ue_executor.execute(std::move(delete_status_pdu_func))) { + logger.log_error("Unable to delete status report in UE executor. Deleting from pcell executor"); + } + }); + /* * Sanity check the received status report. * 1. Checking if the ACK_SN is inside the valid ACK_SN window (the TX window "off-by-one") @@ -686,12 +700,6 @@ void rlc_tx_am_entity::handle_status_pdu(rlc_am_status_pdu status) } } - auto on_function_exit = make_scope_exit([&]() { - auto t_end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(t_end - t_start); - logger.log_info("Handled status report. t={}us {}", duration.count(), status); - }); - /** * Section 5.3.3.3: Reception of a STATUS report * - if the STATUS report comprises a positive or negative acknowledgement for the RLC SDU with sequence @@ -717,7 +725,7 @@ void rlc_tx_am_entity::handle_status_pdu(rlc_am_status_pdu status) * retransmission. */ // Process ACKs - uint32_t stop_sn = status.get_nacks().size() == 0 + uint32_t stop_sn = status.get_nacks().empty() ? status.ack_sn : status.get_nacks()[0].nack_sn; // Stop processing ACKs at the first NACK, if it exists. @@ -725,13 +733,13 @@ void rlc_tx_am_entity::handle_status_pdu(rlc_am_status_pdu status) std::optional max_deliv_retx_pdcp_sn = {}; // initialize with not value set bool recycle_bin_full = false; for (uint32_t sn = st.tx_next_ack; tx_mod_base(sn) < tx_mod_base(stop_sn); sn = (sn + 1) % mod) { - if (tx_window->has_sn(sn)) { - rlc_tx_am_sdu_info& sdu_info = (*tx_window)[sn]; + if (tx_window.has_sn(sn)) { + rlc_tx_am_sdu_info& sdu_info = tx_window[sn]; if (sdu_info.pdcp_sn.has_value()) { if (sdu_info.is_retx) { - max_deliv_retx_pdcp_sn = (*tx_window)[sn].pdcp_sn; + max_deliv_retx_pdcp_sn = tx_window[sn].pdcp_sn; } else { - max_deliv_pdcp_sn = (*tx_window)[sn].pdcp_sn; + max_deliv_pdcp_sn = tx_window[sn].pdcp_sn; } } // move the PDU's byte_buffer from tx_window into pdu_recycler (if possible) for deletion off the critical path. @@ -739,7 +747,7 @@ void rlc_tx_am_entity::handle_status_pdu(rlc_am_status_pdu status) // recycle bin is full and the PDU was deleted on the spot, which may slow down this worker. Warn later. recycle_bin_full = true; } - tx_window->remove_sn(sn); // remove the from tx_window + tx_window.remove_sn(sn); // remove the from tx_window st.tx_next_ack = (sn + 1) % mod; } else { logger.log_error("Could not find ACK'ed sn={} in TX window.", sn); @@ -761,7 +769,7 @@ void rlc_tx_am_entity::handle_status_pdu(rlc_am_status_pdu status) while (!retx_queue.empty()) { const rlc_tx_amd_retx& retx = retx_queue.front(); if (retx_sn != retx.sn) { - if (tx_window->has_sn(retx.sn)) { + if (tx_window.has_sn(retx.sn)) { decrement_retx_count(retx.sn); } retx_sn = retx.sn; @@ -847,7 +855,7 @@ bool rlc_tx_am_entity::handle_nack(rlc_am_status_nack nack) return false; } - const rlc_tx_am_sdu_info& sdu_info = (*tx_window)[nack.nack_sn]; + const rlc_tx_am_sdu_info& sdu_info = tx_window[nack.nack_sn]; uint32_t sdu_length = sdu_info.sdu.length(); // Convert NACK for full SDUs into NACK with segment offset and length @@ -929,7 +937,7 @@ bool rlc_tx_am_entity::handle_nack(rlc_am_status_nack nack) void rlc_tx_am_entity::increment_retx_count(uint32_t sn) { - auto& pdu = (*tx_window)[sn]; + auto& pdu = tx_window[sn]; // Increment retx_count if (pdu.retx_count == RETX_COUNT_NOT_STARTED) { // Set retx_count = 0 on first RE-transmission of associated SDU (38.322 Sec. 5.3.2) @@ -945,7 +953,7 @@ void rlc_tx_am_entity::increment_retx_count(uint32_t sn) void rlc_tx_am_entity::decrement_retx_count(uint32_t sn) { - auto& pdu = (*tx_window)[sn]; + auto& pdu = tx_window[sn]; if (pdu.retx_count == RETX_COUNT_NOT_STARTED) { return; } @@ -958,8 +966,8 @@ void rlc_tx_am_entity::decrement_retx_count(uint32_t sn) void rlc_tx_am_entity::check_sn_reached_max_retx(uint32_t sn) { - if ((*tx_window)[sn].retx_count == cfg.max_retx_thresh) { - logger.log_warning("Reached maximum number of RETX. sn={} retx_count={}", sn, (*tx_window)[sn].retx_count); + if (tx_window[sn].retx_count == cfg.max_retx_thresh) { + logger.log_warning("Reached maximum number of RETX. sn={} retx_count={}", sn, tx_window[sn].retx_count); upper_cn.on_max_retx(); } } @@ -1003,8 +1011,8 @@ uint32_t rlc_tx_am_entity::get_buffer_state() // minimum bytes needed to tx SDU under segmentation + header (if applicable) uint32_t segment_bytes = 0; if (sn_under_segmentation != INVALID_RLC_SN) { - if (tx_window->has_sn(sn_under_segmentation)) { - rlc_tx_am_sdu_info& sdu_info = (*tx_window)[sn_under_segmentation]; + if (tx_window.has_sn(sn_under_segmentation)) { + rlc_tx_am_sdu_info& sdu_info = tx_window[sn_under_segmentation]; segment_bytes = sdu_info.sdu.length() - sdu_info.next_so + head_max_size; } else { logger.log_info("Buffer state ignores SDU under segmentation. sn={} not in tx_window.", sn_under_segmentation); @@ -1124,15 +1132,15 @@ void rlc_tx_am_entity::on_expired_poll_retransmit_timer() * - consider any RLC SDU which has not been positively acknowledged for retransmission. */ if ((sdu_queue.is_empty() && retx_queue.empty() && sn_under_segmentation == INVALID_RLC_SN) || is_tx_window_full()) { - if (tx_window->empty()) { + if (tx_window.empty()) { logger.log_info( - "Poll retransmit timer expired, but the TX window is empty. {} tx_window_size={}", st, tx_window->size()); + "Poll retransmit timer expired, but the TX window is empty. {} tx_window_size={}", st, tx_window.size()); return; } - if (not tx_window->has_sn(st.tx_next_ack)) { + if (not tx_window.has_sn(st.tx_next_ack)) { logger.log_info("Poll retransmit timer expired, but tx_next_ack is not in the TX window. {} tx_window_size={}", st, - tx_window->size()); + tx_window.size()); return; } // RETX first RLC SDU that has not been ACKed @@ -1141,7 +1149,7 @@ void rlc_tx_am_entity::on_expired_poll_retransmit_timer() rlc_tx_amd_retx retx = {}; retx.so = 0; retx.sn = st.tx_next_ack; - retx.length = (*tx_window)[st.tx_next_ack].sdu.length(); + retx.length = tx_window[st.tx_next_ack].sdu.length(); bool retx_enqueued = retx_queue.try_push(retx); // // TODO: Revise this: shall we send a minimum-sized segment instead? @@ -1166,26 +1174,6 @@ void rlc_tx_am_entity::on_expired_poll_retransmit_timer() is_poll_retransmit_timer_expired.store(true, std::memory_order_relaxed); } -std::unique_ptr> rlc_tx_am_entity::create_tx_window(rlc_am_sn_size sn_size) -{ - std::unique_ptr> tx_window_; - switch (sn_size) { - case rlc_am_sn_size::size12bits: - tx_window_ = std::make_unique< - sdu_window_impl>( - logger); - break; - case rlc_am_sn_size::size18bits: - tx_window_ = std::make_unique< - sdu_window_impl>( - logger); - break; - default: - srsran_assertion_failure("Cannot create tx_window for unsupported sn_size={}.", to_number(sn_size)); - } - return tx_window_; -} - bool rlc_tx_am_entity::inside_tx_window(uint32_t sn) const { // TX_Next_Ack <= SN < TX_Next_Ack + AM_Window_Size @@ -1195,7 +1183,7 @@ bool rlc_tx_am_entity::inside_tx_window(uint32_t sn) const bool rlc_tx_am_entity::is_tx_window_full() const { // TX window is full, or we reached our virtual max window size - return tx_window->full() || (cfg.max_window != 0 && tx_mod_base(st.tx_next) > cfg.max_window); + return tx_window.full() || (cfg.max_window != 0 && tx_mod_base(st.tx_next) > cfg.max_window); } bool rlc_tx_am_entity::valid_ack_sn(uint32_t sn) const diff --git a/lib/rlc/rlc_tx_am_entity.h b/lib/rlc/rlc_tx_am_entity.h index 65acf3f6cb..3b1cc31b02 100644 --- a/lib/rlc/rlc_tx_am_entity.h +++ b/lib/rlc/rlc_tx_am_entity.h @@ -38,7 +38,7 @@ namespace srsran { /// Container to hold a SDU for transmission, the progress in case of segmentation, and associated meta data struct rlc_tx_am_sdu_info { - byte_buffer sdu = {}; ///< SDU buffer + byte_buffer sdu; ///< SDU buffer bool is_retx = false; ///< Determines whether this SDU is a PDCP retransmission std::optional pdcp_sn; ///< Optional PDCP sequence number std::chrono::system_clock::time_point time_of_arrival; @@ -105,7 +105,7 @@ class rlc_tx_am_entity : public rlc_tx_entity, public rlc_tx_am_status_handler, const uint32_t am_window_size; /// TX window - std::unique_ptr> tx_window; + sdu_window tx_window; /// Recycler for discarded PDUs (from tx_window) that shall be deleted by a different executor off the critical path rlc_pdu_recycler pdu_recycler; @@ -350,11 +350,6 @@ class rlc_tx_am_entity : public rlc_tx_entity, public rlc_tx_am_status_handler, /// \param force_notify forces a notification of the lower layer regardless of the current/previous buffer state. void update_mac_buffer_state(bool force_notify); - /// Creates the tx_window according to sn_size - /// \param sn_size Size of the sequence number (SN) - /// \return unique pointer to tx_window instance - std::unique_ptr> create_tx_window(rlc_am_sn_size sn_size); - void log_state(srslog::basic_levels level) { if (sn_under_segmentation == INVALID_RLC_SN) { diff --git a/lib/rrc/ue/rrc_ue_message_handlers.cpp b/lib/rrc/ue/rrc_ue_message_handlers.cpp index 5f7a8c62ef..0fe2008f8f 100644 --- a/lib/rrc/ue/rrc_ue_message_handlers.cpp +++ b/lib/rrc/ue/rrc_ue_message_handlers.cpp @@ -144,11 +144,9 @@ void rrc_ue_impl::handle_pdu(const srb_id_t srb_id, byte_buffer rrc_pdu) } // Log Rx message - if (logger.get_basic_logger().debug.enabled()) { - fmt::memory_buffer fmtbuf; - fmt::format_to(fmtbuf, "{} DCCH UL", srb_id); - log_rrc_message(logger, Rx, rrc_pdu, ul_dcch_msg, to_c_str(fmtbuf)); - } + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, "{} DCCH UL", srb_id); + log_rrc_message(logger, Rx, rrc_pdu, ul_dcch_msg, to_c_str(fmtbuf)); switch (ul_dcch_msg.msg.c1().type().value) { case ul_dcch_msg_type_c::c1_c_::types_opts::options::ul_info_transfer: @@ -447,8 +445,9 @@ async_task rrc_ue_impl::handle_handover_reconfiguration_complete_expected( context.state = rrc_state::connected; } else { - logger.log_debug("Did not receive RRC Reconfiguration Complete after HO. Cause: {}", + logger.log_debug("Did not receive RRC Reconfiguration Complete after HO. Cause: {}. Requesting UE release", transaction.failure_cause() == protocol_transaction_failure::timeout ? "timeout" : "canceled"); + on_ue_release_required(ngap_cause_radio_network_t::ho_fail_in_target_5_gc_ngran_node_or_target_sys); } CORO_RETURN(procedure_result); diff --git a/lib/scheduler/config/sched_config_manager.cpp b/lib/scheduler/config/sched_config_manager.cpp index 61d4080fab..bf7a9b3f83 100644 --- a/lib/scheduler/config/sched_config_manager.cpp +++ b/lib/scheduler/config/sched_config_manager.cpp @@ -272,7 +272,7 @@ void sched_config_manager::flush_ues_to_rem() { // Note: This should be called by a thread outside of the critical path. - // clear the UEs to rem. + // Clear the UEs to rem. while (ues_to_rem.try_pop()) { } } diff --git a/lib/scheduler/config/serving_cell_config_factory.cpp b/lib/scheduler/config/serving_cell_config_factory.cpp index 3a57f87c86..6cf52115cd 100644 --- a/lib/scheduler/config/serving_cell_config_factory.cpp +++ b/lib/scheduler/config/serving_cell_config_factory.cpp @@ -441,7 +441,7 @@ ssb_configuration srsran::config_helpers::make_default_ssb_config(const cell_con pusch_config srsran::config_helpers::make_default_pusch_config(const cell_config_builder_params_extended& params) { // Default PUSCH transmission scheme is codebook with at maximum one layer. It assumes the UE Capability parameter - // pusch-TransCoherence is fullCoherent. + // pusch-TransCoherence is nonCoherent. static constexpr unsigned max_rank = 1; static constexpr tx_scheme_codebook_subset codebook_subset = tx_scheme_codebook_subset::non_coherent; diff --git a/lib/scheduler/config/ue_configuration.h b/lib/scheduler/config/ue_configuration.h index 202cab2fda..20c4f36c2a 100644 --- a/lib/scheduler/config/ue_configuration.h +++ b/lib/scheduler/config/ue_configuration.h @@ -182,12 +182,12 @@ class ue_cell_configuration return cell_cfg_ded.ul_config->init_ul_bwp.pusch_cfg->trans_precoder == pusch_config::transform_precoder::enabled; } - /// \brief Gets the PUSCH transmit scheme codebook subset. + /// \brief Gets the PUSCH transmit scheme codebook configuration. /// - /// The codebook subset is selection procedure is described in TS 38.214 Section 6.1.1.1. + /// The codebook subset selection procedure is described in TS 38.214 Section 6.1.1.1. /// /// \remark An assertion is triggered if the transmission scheme is not present or not set to codebook. - tx_scheme_codebook_subset get_pusch_codebook_subset() const + const tx_scheme_codebook& get_pusch_codebook_config() const { srsran_assert(cell_cfg_ded.ul_config.has_value(), "Missing dedicated UL configuration."); srsran_assert(cell_cfg_ded.ul_config.value().init_ul_bwp.pusch_cfg.has_value(), @@ -197,29 +197,7 @@ class ue_cell_configuration srsran_assert(std::holds_alternative( cell_cfg_ded.ul_config.value().init_ul_bwp.pusch_cfg.value().tx_cfg.value()), "PUSCH Transmission scheme must be set to codebook."); - const auto& tx_config = - std::get(cell_cfg_ded.ul_config.value().init_ul_bwp.pusch_cfg.value().tx_cfg.value()); - return tx_config.codebook_subset; - } - - /// \brief Gets the PUSCH maximum number of layers. - /// - /// The maximum number of layers selection procedudre is described in TS 38.214 Section 6.1.1.1. - /// - /// \remark An assertion is triggered if the transmission scheme is not present or not set to codebook. - uint8_t get_pusch_max_rank() const - { - srsran_assert(cell_cfg_ded.ul_config.has_value(), "Missing dedicated UL configuration."); - srsran_assert(cell_cfg_ded.ul_config.value().init_ul_bwp.pusch_cfg.has_value(), - "Missing dedicated PUSCH configuration."); - srsran_assert(cell_cfg_ded.ul_config.value().init_ul_bwp.pusch_cfg.value().tx_cfg.has_value(), - "Missing transmit configuration."); - srsran_assert(std::holds_alternative( - cell_cfg_ded.ul_config.value().init_ul_bwp.pusch_cfg.value().tx_cfg.value()), - "PUSCH Transmission scheme must be set to codebook."); - const auto& tx_config = - std::get(cell_cfg_ded.ul_config.value().init_ul_bwp.pusch_cfg.value().tx_cfg.value()); - return tx_config.max_rank.to_uint(); + return std::get(cell_cfg_ded.ul_config.value().init_ul_bwp.pusch_cfg.value().tx_cfg.value()); } private: diff --git a/lib/scheduler/support/dci_builder.cpp b/lib/scheduler/support/dci_builder.cpp index 2486739107..5f5e1dfd1f 100644 --- a/lib/scheduler/support/dci_builder.cpp +++ b/lib/scheduler/support/dci_builder.cpp @@ -420,7 +420,7 @@ void srsran::build_dci_f0_1_c_rnti(dci_ul_info& dci, nof_layers, use_transform_precoder, dmrs_config_type::type1, dmrs_max_length::len1); f0_1.precoding_info_nof_layers = get_pusch_precoding_info_row_index(nof_layers, - ue_cell_cfg.get_pusch_max_rank(), + ue_cell_cfg.get_pusch_codebook_config().max_rank.value(), srs_resource_configuration::one_two_four_enum::four, use_transform_precoder, dmrs_config_type::type1, diff --git a/lib/scheduler/ue_context/ue.cpp b/lib/scheduler/ue_context/ue.cpp index 162f01bd27..905072a6dd 100644 --- a/lib/scheduler/ue_context/ue.cpp +++ b/lib/scheduler/ue_context/ue.cpp @@ -81,9 +81,18 @@ void ue::deactivate() ul_lc_ch_mgr.deactivate(); // Cancel HARQ retransmissions in all UE cells. - for (unsigned i = 0; i != ue_du_cells.size(); ++i) { - if (ue_du_cells[i] != nullptr) { - ue_du_cells[i]->deactivate(); + for (auto& cell : ue_du_cells) { + if (cell != nullptr) { + cell->deactivate(); + } + } +} + +void ue::release_resources() +{ + for (auto& cell : ue_du_cells) { + if (cell != nullptr) { + cell->harqs.reset(); } } } @@ -99,7 +108,7 @@ void ue::handle_reconfiguration_request(const ue_reconf_command& cmd) // Cell configuration. // Handle removed cells. - for (unsigned i = 0; i != ue_du_cells.size(); ++i) { + for (unsigned i = 0, e = ue_du_cells.size(); i != e; ++i) { if (ue_du_cells[i] != nullptr) { if (not ue_ded_cfg->contains(to_du_cell_index(i))) { // TODO: Handle SCell deletions. @@ -107,7 +116,7 @@ void ue::handle_reconfiguration_request(const ue_reconf_command& cmd) } } // Handle new cell creations or reconfigurations. - for (unsigned ue_cell_index = 0; ue_cell_index != ue_ded_cfg->nof_cells(); ++ue_cell_index) { + for (unsigned ue_cell_index = 0, e = ue_ded_cfg->nof_cells(); ue_cell_index != e; ++ue_cell_index) { du_cell_index_t cell_index = ue_ded_cfg->ue_cell_cfg(to_ue_cell_index(ue_cell_index)).cell_cfg_common.cell_index; auto& ue_cell_inst = ue_du_cells[cell_index]; if (ue_cell_inst == nullptr) { @@ -123,7 +132,7 @@ void ue::handle_reconfiguration_request(const ue_reconf_command& cmd) // Recompute mapping of UE cell indexing to DU cell indexing. ue_cells.resize(ue_ded_cfg->nof_cells(), nullptr); - for (unsigned ue_cell_index = 0; ue_cell_index != ue_ded_cfg->nof_cells(); ++ue_cell_index) { + for (unsigned ue_cell_index = 0, e = ue_ded_cfg->nof_cells(); ue_cell_index != e; ++ue_cell_index) { auto& ue_cell_inst = ue_du_cells[ue_ded_cfg->ue_cell_cfg(to_ue_cell_index(ue_cell_index)).cell_cfg_common.cell_index]; ue_cells[ue_cell_index] = ue_cell_inst.get(); diff --git a/lib/scheduler/ue_context/ue.h b/lib/scheduler/ue_context/ue.h index 0391c17f7a..a0019f8960 100644 --- a/lib/scheduler/ue_context/ue.h +++ b/lib/scheduler/ue_context/ue.h @@ -59,6 +59,8 @@ class ue void deactivate(); + void release_resources(); + ue_cell* find_cell(du_cell_index_t cell_index) { srsran_assert(cell_index < MAX_NOF_DU_CELLS, "Invalid cell_index={}", cell_index); diff --git a/lib/scheduler/ue_context/ue_cell.cpp b/lib/scheduler/ue_context/ue_cell.cpp index cae99885be..0db268e3a8 100644 --- a/lib/scheduler/ue_context/ue_cell.cpp +++ b/lib/scheduler/ue_context/ue_cell.cpp @@ -304,7 +304,7 @@ int ue_cell::handle_crc_pdu(slot_point pusch_slot, const ul_crc_pdu_indication& void ue_cell::handle_srs_channel_matrix(const srs_channel_matrix& channel_matrix) { - channel_state.update_srs_channel_matrix(channel_matrix, ue_cfg->get_pusch_codebook_subset()); + channel_state.update_srs_channel_matrix(channel_matrix, ue_cfg->get_pusch_codebook_config()); } void ue_cell::handle_csi_report(const csi_report_data& csi_report) diff --git a/lib/scheduler/ue_context/ue_channel_state_manager.cpp b/lib/scheduler/ue_context/ue_channel_state_manager.cpp index 3aa25be5a1..34d9569de7 100644 --- a/lib/scheduler/ue_context/ue_channel_state_manager.cpp +++ b/lib/scheduler/ue_context/ue_channel_state_manager.cpp @@ -75,11 +75,16 @@ bool ue_channel_state_manager::handle_csi_report(const csi_report_data& csi_repo return true; } -void ue_channel_state_manager::update_srs_channel_matrix(const srsran::srs_channel_matrix& channel_matrix, - tx_scheme_codebook_subset cb_subset) +void ue_channel_state_manager::update_srs_channel_matrix(const srs_channel_matrix& channel_matrix, + tx_scheme_codebook codebook_cfg) { - float norm = channel_matrix.frobenius_norm(); - last_pusch_tpmi_select_info = get_tpmi_select_info(channel_matrix, norm * norm, cb_subset); + // [Implementation-defined] Assume noise variance is 30dB below the average received power. + float norm = channel_matrix.frobenius_norm(); + float noise_var = norm * norm / 1000; + + // Calculate TPMI information. + last_pusch_tpmi_select_info = + get_tpmi_select_info(channel_matrix, noise_var, codebook_cfg.max_rank.value(), codebook_cfg.codebook_subset); } SRSRAN_WEAK_SYMB unsigned ue_channel_state_manager::get_nof_ul_layers() const diff --git a/lib/scheduler/ue_context/ue_channel_state_manager.h b/lib/scheduler/ue_context/ue_channel_state_manager.h index ea356f9fb5..1de91357bf 100644 --- a/lib/scheduler/ue_context/ue_channel_state_manager.h +++ b/lib/scheduler/ue_context/ue_channel_state_manager.h @@ -80,7 +80,7 @@ class ue_channel_state_manager bool handle_csi_report(const csi_report_data& csi_report); /// Update UE with the latest Sounding Reference Signal (SRS) channel matrix. - void update_srs_channel_matrix(const srs_channel_matrix& channel_matrix, tx_scheme_codebook_subset cb_subset); + void update_srs_channel_matrix(const srs_channel_matrix& channel_matrix, tx_scheme_codebook codebook_cfg); /// Update UE with the latest PHR for a given cell. void handle_phr(const cell_ph_report& phr); diff --git a/lib/scheduler/ue_scheduling/ue_event_manager.cpp b/lib/scheduler/ue_scheduling/ue_event_manager.cpp index fde42cd6ef..ba1f0de0c4 100644 --- a/lib/scheduler/ue_scheduling/ue_event_manager.cpp +++ b/lib/scheduler/ue_scheduling/ue_event_manager.cpp @@ -197,7 +197,7 @@ ue_event_manager::ue_event_manager(ue_repository& ue_db_) : { } -ue_event_manager::~ue_event_manager() {} +ue_event_manager::~ue_event_manager() = default; void ue_event_manager::handle_ue_creation(ue_config_update_event ev) { @@ -246,6 +246,9 @@ void ue_event_manager::handle_ue_creation(ue_config_update_event ev) if (not common_events.try_push(common_event_t{INVALID_DU_UE_INDEX, std::move(handle_ue_creation_impl)})) { logger.warning("ue={}: Discarding UE creation. Cause: Event queue is full", ue_idx); } + + // Destroy any pending UEs in the repository outside the critical section. + ue_db.destroy_pending_ues(); } void ue_event_manager::handle_ue_reconfiguration(ue_config_update_event ev) @@ -344,6 +347,9 @@ void ue_event_manager::handle_ue_deletion(ue_config_delete_event ev) if (not common_events.try_push(common_event_t{ue_index, std::move(handle_ue_deletion_impl)})) { logger.warning("ue={}: Discarding UE deletion. Cause: Event queue is full", ue_index); } + + // Destroy any pending UEs in the repository outside the critical section. + ue_db.destroy_pending_ues(); } void ue_event_manager::handle_ue_config_applied(du_ue_index_t ue_idx) diff --git a/lib/scheduler/ue_scheduling/ue_repository.cpp b/lib/scheduler/ue_scheduling/ue_repository.cpp index 0f54dc4e12..ca9d6908ae 100644 --- a/lib/scheduler/ue_scheduling/ue_repository.cpp +++ b/lib/scheduler/ue_scheduling/ue_repository.cpp @@ -26,7 +26,7 @@ using namespace srsran; -ue_repository::ue_repository() : logger(srslog::fetch_basic_logger("SCHED")) +ue_repository::ue_repository() : logger(srslog::fetch_basic_logger("SCHED")), ues_to_destroy(MAX_NOF_DU_UES) { rnti_to_ue_index_lookup.reserve(MAX_NOF_DU_UES); } @@ -39,12 +39,12 @@ static bool is_ue_ready_for_removal(ue& u) unsigned nof_ue_cells = u.nof_cells(); for (unsigned cell_idx = 0; cell_idx != nof_ue_cells; ++cell_idx) { const ue_cell& c = u.get_cell((ue_cell_index_t)cell_idx); - for (unsigned i = 0; i != c.harqs.nof_dl_harqs(); ++i) { + for (unsigned i = 0, e = c.harqs.nof_dl_harqs(); i != e; ++i) { if (c.harqs.dl_harq(to_harq_id(i)).has_value()) { return false; } } - for (unsigned i = 0; i != c.harqs.nof_ul_harqs(); ++i) { + for (unsigned i = 0, e = c.harqs.nof_ul_harqs(); i != e; ++i) { if (c.harqs.ul_harq(to_harq_id(i)).has_value()) { return false; } @@ -101,8 +101,12 @@ void ue_repository::slot_indication(slot_point sl_tx) logger.error("ue={} rnti={}: UE with provided c-rnti not found in RNTI-to-UE-index lookup table.", ue_idx, crnti); } - // Remove UE from the repository. - ues.erase(ue_idx); + // Take the UE from the repository and schedule its destruction outside the critical section. + auto ue_ptr = ues.take(ue_idx); + ue_ptr->release_resources(); + if (not ues_to_destroy.try_push(std::move(ue_ptr))) { + logger.warning("Failed to offload UE destruction. Performance may be affected"); + } // Marks UE config removal as complete. rem_ev.reset(); @@ -161,3 +165,9 @@ const ue* ue_repository::find_by_rnti(rnti_t rnti) const auto it = search_rnti(rnti_to_ue_index_lookup, rnti); return it != rnti_to_ue_index_lookup.end() ? ues[it->second].get() : nullptr; } + +void ue_repository::destroy_pending_ues() +{ + while (ues_to_destroy.try_pop()) { + } +} diff --git a/lib/scheduler/ue_scheduling/ue_repository.h b/lib/scheduler/ue_scheduling/ue_repository.h index 3e5ecadbea..6dae90d39c 100644 --- a/lib/scheduler/ue_scheduling/ue_repository.h +++ b/lib/scheduler/ue_scheduling/ue_repository.h @@ -74,6 +74,8 @@ class ue_repository const_iterator lower_bound(du_ue_index_t ue_index) const { return ues.lower_bound(ue_index); } + void destroy_pending_ues(); + private: srslog::basic_logger& logger; @@ -89,6 +91,12 @@ class ue_repository // Last slot indication. slot_point last_sl_tx; + + // UE objects pending to be destroyed by a low priority thread. + concurrent_queue, + concurrent_queue_policy::lockfree_mpmc, + concurrent_queue_wait_policy::non_blocking> + ues_to_destroy; }; } // namespace srsran diff --git a/lib/srsvec/CMakeLists.txt b/lib/srsvec/CMakeLists.txt index d682ad18bb..b28e56f13b 100644 --- a/lib/srsvec/CMakeLists.txt +++ b/lib/srsvec/CMakeLists.txt @@ -21,7 +21,6 @@ set(SOURCES accumulate.cpp add.cpp - aligned_vec.cpp bit.cpp clip.cpp compare.cpp diff --git a/lib/srsvec/conversion.cpp b/lib/srsvec/conversion.cpp index af98c44a6c..a3e44d7c97 100644 --- a/lib/srsvec/conversion.cpp +++ b/lib/srsvec/conversion.cpp @@ -363,8 +363,8 @@ static void convert_scaled_int16_to_bf16_simd(bf16_t* out, const int16_t* in, co __m256i input_vec_2 = _mm256_loadu_si256(reinterpret_cast(in + i + 16)); // Load the scale factor into a vector register. - __m512 scale_vec_1 = _mm512_load_ps(in_gain + i); - __m512 scale_vec_2 = _mm512_load_ps(in_gain + i + 16); + __m512 scale_vec_1 = _mm512_loadu_ps(in_gain + i); + __m512 scale_vec_2 = _mm512_loadu_ps(in_gain + i + 16); // Convert the int16_t elements to float and scale them. __m512 float_vec_1 = _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(input_vec_1)); @@ -382,7 +382,7 @@ static void convert_scaled_int16_to_bf16_simd(bf16_t* out, const int16_t* in, co __m256i input_vec = _mm256_loadu_si256(reinterpret_cast(in + i)); // Load the scale factor into a vector register. - __m512 scale_vec = _mm512_load_ps(in_gain + i); + __m512 scale_vec = _mm512_loadu_ps(in_gain + i); // Convert the int16_t elements to float and scale them. __m512 float_vec = _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(input_vec)); @@ -429,8 +429,8 @@ static void convert_scaled_int16_to_bf16_simd(bf16_t* out, const int16_t* in, co __m128i input_vec_2 = _mm_loadu_si128(reinterpret_cast(in + i + 8)); // Load the scale factor into a vector register. - __m256 scale_vec_1 = _mm256_load_ps(in_gain + i); - __m256 scale_vec_2 = _mm256_load_ps(in_gain + i + 8); + __m256 scale_vec_1 = _mm256_loadu_ps(in_gain + i); + __m256 scale_vec_2 = _mm256_loadu_ps(in_gain + i + 8); // Convert the int16_t elements to float and scale them __m256 float_vec_1 = _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(input_vec_1)); diff --git a/lib/support/cpu_architecture_info.cpp b/lib/support/cpu_architecture_info.cpp index 2a627302a7..bb03a2579d 100644 --- a/lib/support/cpu_architecture_info.cpp +++ b/lib/support/cpu_architecture_info.cpp @@ -58,12 +58,6 @@ static interval parse_cpu_range(const std::string& value) return {range[0], range[1]}; } -/// Obtain CPU description at the start of the application. This value is affected by commands or tools like taskset, -/// which limit the number of cores available to the application. However, frameworks (e.g. DPDK) that affect the -/// affinities of the main thread in the main() function will not affect this value. -const cpu_architecture_info::cpu_description cpu_architecture_info::cpu_desc = - cpu_architecture_info::discover_cpu_architecture(); - cpu_architecture_info::cpu_description cpu_architecture_info::discover_cpu_architecture() { // Check if custom cgroups exist in the system (possibly left from a previous run). diff --git a/lib/support/sdu_window_impl.h b/lib/support/sdu_window_impl.h deleted file mode 100644 index a95ee5286d..0000000000 --- a/lib/support/sdu_window_impl.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#pragma once - -#include "srsran/adt/circular_map.h" -#include "srsran/support/sdu_window.h" -#include "srsran/support/srsran_assert.h" - -namespace srsran { - -/// \brief This class provides a container for the Tx/Rx windows holding SDU info objects that are indexed by -/// Sequence Numbers (SN) -/// @tparam T storage type -/// @tparam WINDOW_SIZE size of the window -/// @tparam PREFIXED_LOGGER an implementation of a prefixed_logger for logging -template -class sdu_window_impl final : public sdu_window -{ -public: - sdu_window_impl(PREFIXED_LOGGER& logger_) : logger(logger_) {} - ~sdu_window_impl() = default; - - T& add_sn(size_t sn) override - { - if (has_sn(sn)) { - logger.log_error("sn={} already present in window, overwriting.", sn); - srsran_assertion_failure("sn={} already present in window.", sn); - } else { - logger.log_debug("Adding sn={} to window.", sn); - } - window.overwrite(sn, T()); - return window[sn]; - } - void remove_sn(size_t sn) override - { - if (not has_sn(sn)) { - logger.log_error("Cannot remove sn={} because not contained in the window.", sn); - srsran_assertion_failure("Cannot remove sn={} because not contained in the window.", sn); - return; - } - logger.log_debug("Removing sn={} from window", sn); - window.erase(sn); - } - 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 override { return window.contains(sn); } - -private: - PREFIXED_LOGGER& logger; - static_circular_map window; -}; - -} // namespace srsran diff --git a/tests/benchmarks/phy/upper/channel_processors/CMakeLists.txt b/tests/benchmarks/phy/upper/channel_processors/CMakeLists.txt index 323b8312e9..8b53a9ffde 100644 --- a/tests/benchmarks/phy/upper/channel_processors/CMakeLists.txt +++ b/tests/benchmarks/phy/upper/channel_processors/CMakeLists.txt @@ -18,6 +18,7 @@ # and at http://www.gnu.org/licenses/. # +add_subdirectory(pucch) add_subdirectory(pusch) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test_data) diff --git a/tests/benchmarks/phy/upper/channel_processors/pdsch_processor_benchmark.cpp b/tests/benchmarks/phy/upper/channel_processors/pdsch_processor_benchmark.cpp index 8eb4235423..b317fb6e25 100644 --- a/tests/benchmarks/phy/upper/channel_processors/pdsch_processor_benchmark.cpp +++ b/tests/benchmarks/phy/upper/channel_processors/pdsch_processor_benchmark.cpp @@ -588,7 +588,7 @@ static pdsch_processor_factory& get_processor_factory() std::shared_ptr ldpc_rm_factory = create_ldpc_rate_matcher_factory_sw(); TESTASSERT(ldpc_rm_factory); - // Create LDPC desegmenter factory. + // Create LDPC segmenter factory. std::shared_ptr ldpc_segm_tx_factory = create_ldpc_segmenter_tx_factory_sw(crc_calc_factory); TESTASSERT(ldpc_segm_tx_factory); @@ -658,7 +658,7 @@ static pdsch_processor_factory& get_processor_factory() "pdsch_proc", nof_pdsch_processor_concurrent_threads, 1024); executor = std::make_unique>(*worker_pool); - pdsch_proc_factory = create_pdsch_concurrent_processor_factory_sw(crc_calc_factory, + pdsch_proc_factory = create_pdsch_concurrent_processor_factory_sw(ldpc_segm_tx_factory, ldpc_enc_factory, ldpc_rm_factory, prg_factory, diff --git a/tests/benchmarks/phy/upper/channel_processors/pucch/CMakeLists.txt b/tests/benchmarks/phy/upper/channel_processors/pucch/CMakeLists.txt new file mode 100644 index 0000000000..dfaeb23526 --- /dev/null +++ b/tests/benchmarks/phy/upper/channel_processors/pucch/CMakeLists.txt @@ -0,0 +1,37 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_executable(pucch_processor_benchmark pucch_processor_benchmark.cpp) +set(pucch_PROCESSOR_LIBRARIES srsran_phy_support + srsran_pucch_processor + srsran_channel_equalizer + srsran_channel_processors + srsran_transform_precoding + srslog + srsvec) + +target_link_libraries(pucch_processor_benchmark + srsran_channel_equalizer + srsran_channel_processors + srsran_pucch_processor + srsran_transform_precoding + srslog + srsvec) +add_test(pucch_processor_benchmark pucch_processor_benchmark -m silent -R 1 -B 1 -T 2) diff --git a/tests/benchmarks/phy/upper/channel_processors/pucch/pucch_processor_benchmark.cpp b/tests/benchmarks/phy/upper/channel_processors/pucch/pucch_processor_benchmark.cpp new file mode 100644 index 0000000000..c8a829cb0a --- /dev/null +++ b/tests/benchmarks/phy/upper/channel_processors/pucch/pucch_processor_benchmark.cpp @@ -0,0 +1,589 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/adt/to_array.h" +#include "srsran/phy/support/resource_grid_reader.h" +#include "srsran/phy/support/support_factories.h" +#include "srsran/phy/upper/channel_processors/pucch/factories.h" +#include "srsran/ran/pucch/pucch_constants.h" +#include "srsran/support/benchmark_utils.h" +#include "srsran/support/complex_normal_random.h" +#include "srsran/support/executors/task_worker_pool.h" +#include "srsran/support/executors/unique_thread.h" +#include "srsran/support/math/math_utils.h" +#include "srsran/support/srsran_test.h" +#include +#include + +using namespace srsran; + +// The benchmark configuration consists any of the PUCCH configurations. +using pucch_configuration = std::variant; + +namespace { + +enum class benchmark_modes : unsigned { silent, latency, throughput_total, throughput_thread, all, invalid }; + +} // namespace + +static const char* to_string(benchmark_modes mode) +{ + switch (mode) { + case benchmark_modes::silent: + return "silent"; + case benchmark_modes::latency: + return "latency"; + case benchmark_modes::throughput_total: + return "throughput_total"; + case benchmark_modes::throughput_thread: + return "throughput_thread"; + case benchmark_modes::all: + return "all"; + case benchmark_modes::invalid: + default: + return "invalid"; + } +} + +static benchmark_modes to_benchmark_mode(const char* string) +{ + for (unsigned mode_i = static_cast(benchmark_modes::silent); + mode_i != static_cast(benchmark_modes::invalid); + ++mode_i) { + benchmark_modes mode = static_cast(mode_i); + if (strcmp(to_string(mode), string) == 0) { + return mode; + } + } + return benchmark_modes::invalid; +} + +// Maximum number of threads given the CPU hardware. +static const unsigned max_nof_threads = std::thread::hardware_concurrency(); + +// General test configuration parameters. +static constexpr subcarrier_spacing scs = subcarrier_spacing::kHz30; +static constexpr cyclic_prefix cp = cyclic_prefix::NORMAL; +static constexpr unsigned bwp_start_rb = 0; +static constexpr unsigned bwp_size_rb = MAX_RB; +static constexpr unsigned max_nof_ports = 4; +static uint64_t nof_repetitions = 1000; +static uint64_t nof_threads = 1; +static uint64_t batch_size_per_thread = 100; +static std::string selected_profile_name = "all"; +static benchmark_modes benchmark_mode = benchmark_modes::latency; +static std::unique_ptr> worker_pool = nullptr; +static std::unique_ptr> executor = nullptr; + +// Thread shared variables. +static constexpr auto thread_sync_sleep_duration = std::chrono::nanoseconds(100U); +static std::atomic thread_quit = {}; +static std::atomic pending_count = {0}; +static std::atomic finish_count = {0}; + +// Test profile structure, initialized with default profile values. +struct test_profile { + std::string name = "none"; + std::string description = "TBD."; + pucch_configuration pucch_config = pucch_processor::format0_configuration{}; +}; + +// Profile selected during test execution. +static test_profile selected_profile = {}; + +// Available test profiles. +static const auto profile_set = to_array({ + test_profile{ + .name = "Format0", + .description = + "PUCCH Format 0 with frequency hopping, the maximum number of cyclic shifts and four receive ports.", + .pucch_config = pucch_processor::format0_configuration{.context = std::nullopt, + .slot = slot_point(to_numerology_value(scs), 0), + .cp = cp, + .bwp_size_rb = bwp_size_rb, + .bwp_start_rb = bwp_start_rb, + .starting_prb = 0, + .second_hop_prb = 5, + .start_symbol_index = 0, + .nof_symbols = 2, + .initial_cyclic_shift = 0, + .n_id = 0, + .nof_harq_ack = 2, + .sr_opportunity = true, + .ports = {0, 1, 2, 3}}}, + + test_profile{ + .name = "Format1", + .description = + "PUCCH Format 1 with frequency hopping, the maximum HARQ-ACK feedback bits and four receive ports.", + .pucch_config = pucch_processor::format1_configuration{.context = std::nullopt, + .slot = slot_point(to_numerology_value(scs), 0), + .bwp_size_rb = bwp_size_rb, + .bwp_start_rb = bwp_start_rb, + .cp = cp, + .starting_prb = 0, + .second_hop_prb = 5, + .n_id = 0, + .nof_harq_ack = 2, + .ports = {0, 1, 2, 3}, + .initial_cyclic_shift = 0, + .nof_symbols = 14, + .start_symbol_index = 0, + .time_domain_occ = 1}}, + + test_profile{ + .name = "Format2_4bit", + .description = + "PUCCH Format 2 with frequency hopping, the maximum HARQ-ACK feedback bits and four receive ports.", + .pucch_config = pucch_processor::format2_configuration{.context = std::nullopt, + .slot = slot_point(to_numerology_value(scs), 0), + .cp = cp, + .ports = {0, 1, 2, 3}, + .bwp_size_rb = bwp_size_rb, + .bwp_start_rb = bwp_start_rb, + .starting_prb = 0, + .second_hop_prb = 5, + .nof_prb = 16, + .start_symbol_index = 0, + .nof_symbols = 2, + .rnti = 0x1234, + .n_id = 0, + .n_id_0 = 0, + .nof_harq_ack = 0, + .nof_csi_part1 = 4, + .nof_csi_part2 = 0}}, + + test_profile{ + .name = "Format2_10bit", + .description = + "PUCCH Format 2 with frequency hopping, the maximum HARQ-ACK feedback bits and four receive ports.", + .pucch_config = pucch_processor::format2_configuration{.context = std::nullopt, + .slot = slot_point(to_numerology_value(scs), 0), + .cp = cp, + .ports = {0, 1, 2, 3}, + .bwp_size_rb = bwp_size_rb, + .bwp_start_rb = bwp_start_rb, + .starting_prb = 0, + .second_hop_prb = 5, + .nof_prb = 16, + .start_symbol_index = 0, + .nof_symbols = 2, + .rnti = 0x1234, + .n_id = 0, + .n_id_0 = 0, + .nof_harq_ack = 0, + .nof_csi_part1 = 11, + .nof_csi_part2 = 0}}, + + test_profile{ + .name = "Format2_30bit", + .description = + "PUCCH Format 2 with frequency hopping, the maximum HARQ-ACK feedback bits and four receive ports.", + .pucch_config = pucch_processor::format2_configuration{.context = std::nullopt, + .slot = slot_point(to_numerology_value(scs), 0), + .cp = cp, + .ports = {0, 1, 2, 3}, + .bwp_size_rb = bwp_size_rb, + .bwp_start_rb = bwp_start_rb, + .starting_prb = 0, + .second_hop_prb = 5, + .nof_prb = 16, + .start_symbol_index = 0, + .nof_symbols = 2, + .rnti = 0x1234, + .n_id = 0, + .n_id_0 = 0, + .nof_harq_ack = 0, + .nof_csi_part1 = 30, + .nof_csi_part2 = 0}}, +}); + +static void usage(const char* prog) +{ + fmt::print("Usage: {} [-m benchmark mode] [-R repetitions] [-B Batch size per thread] [-T number of threads] [-P " + "profile] [-h]\n", + prog); + fmt::print("\t-m Benchmark mode. [Default {}]\n", to_string(benchmark_mode)); + fmt::print("\t\t {:<20}It does not print any result.\n", to_string(benchmark_modes::silent)); + fmt::print("\t\t {:<20}Prints the overall average execution time.\n", to_string(benchmark_modes::latency)); + fmt::print("\t\t {:<20}Prints the total aggregated throughput.\n", to_string(benchmark_modes::throughput_total)); + fmt::print("\t\t {:<20}Prints the average single thread throughput.\n", + to_string(benchmark_modes::throughput_thread)); + fmt::print("\t\t {:<20}Prints all the previous modes.\n", to_string(benchmark_modes::all)); + fmt::print("\t-R Repetitions [Default {}]\n", nof_repetitions); + fmt::print("\t-B Batch size [Default {}]\n", batch_size_per_thread); + fmt::print("\t-T Number of threads [Default {}, max. {}]\n", nof_threads, max_nof_threads); + fmt::print("\t-P Benchmark profile. [Default {}]\n", selected_profile_name); + for (const test_profile& profile : profile_set) { + fmt::print("\t\t {:<40} {}\n", profile.name, profile.description); + } + + fmt::print("\t-h Show this message\n"); +} + +static int parse_args(int argc, char** argv) +{ + int opt = 0; + while ((opt = getopt(argc, argv, "R:T:B:P:m:h")) != -1) { + switch (opt) { + case 'R': + nof_repetitions = std::strtol(optarg, nullptr, 10); + break; + case 'T': + nof_threads = std::min(max_nof_threads, static_cast(std::strtol(optarg, nullptr, 10))); + break; + case 'B': + batch_size_per_thread = std::strtol(optarg, nullptr, 10); + break; + case 'P': + selected_profile_name = std::string(optarg); + break; + case 'm': + benchmark_mode = to_benchmark_mode(optarg); + if (benchmark_mode == benchmark_modes::invalid) { + fmt::print(stderr, "Invalid benchmark mode '{}'\n", optarg); + usage(argv[0]); + return -1; + } + break; + case 'h': + default: + usage(argv[0]); + exit(0); + } + } + + if (selected_profile_name != "all") { + // Search profile. + bool profile_found = false; + for (const auto& candidate_profile : profile_set) { + if (selected_profile_name == candidate_profile.name) { + selected_profile = candidate_profile; + srslog::fetch_basic_logger("TEST").info("Loading profile: {}", selected_profile.name); + profile_found = true; + break; + } + } + if (!profile_found) { + usage(argv[0]); + srslog::fetch_basic_logger("TEST").error("Invalid profile: {}.", selected_profile_name); + fmt::print(stderr, "Invalid profile: {}.\n", selected_profile_name); + return -1; + } + } + + return 0; +} + +static pucch_processor_factory& get_pucch_processor_factory() +{ + static std::shared_ptr pucch_proc_factory = nullptr; + + if (pucch_proc_factory) { + return *pucch_proc_factory; + } + + // Create factories required by the PUCCH demodulator factory. + std::shared_ptr equalizer_factory = create_channel_equalizer_generic_factory(); + TESTASSERT(equalizer_factory); + + std::shared_ptr demod_factory = create_channel_modulation_sw_factory(); + TESTASSERT(demod_factory); + + std::shared_ptr prg_factory = create_pseudo_random_generator_sw_factory(); + TESTASSERT(prg_factory); + + std::shared_ptr dft_factory = create_dft_processor_factory_fftw_slow(); + TESTASSERT(dft_factory); + + std::shared_ptr precoding_factory = + create_dft_transform_precoder_factory(dft_factory, pucch_constants::FORMAT3_MAX_NPRB + 1); + TESTASSERT(precoding_factory); + + // Create PUCCH demodulator factory. + std::shared_ptr pucch_demod_factory = + create_pucch_demodulator_factory_sw(equalizer_factory, demod_factory, prg_factory, precoding_factory); + TESTASSERT(pucch_demod_factory); + + // Create factories required by the PUCCH channel estimator factory. + std::shared_ptr lpg_factory = create_low_papr_sequence_generator_sw_factory(); + TESTASSERT(lpg_factory); + + std::shared_ptr lpc_factory = + create_low_papr_sequence_collection_sw_factory(lpg_factory); + TESTASSERT(lpc_factory); + + std::shared_ptr ta_estimator_factory = + create_time_alignment_estimator_dft_factory(dft_factory); + TESTASSERT(ta_estimator_factory); + + // Create channel estimator factory. + std::shared_ptr port_chan_estimator_factory = + create_port_channel_estimator_factory_sw(ta_estimator_factory); + TESTASSERT(port_chan_estimator_factory); + + std::shared_ptr estimator_factory = + create_dmrs_pucch_estimator_factory_sw(prg_factory, lpc_factory, port_chan_estimator_factory); + TESTASSERT(estimator_factory); + + // Create PUCCH detector factory. + std::shared_ptr detector_factory = + create_pucch_detector_factory_sw(lpc_factory, prg_factory, equalizer_factory); + TESTASSERT(detector_factory); + + // Create short block detector factory. + std::shared_ptr short_block_det_factory = create_short_block_detector_factory_sw(); + TESTASSERT(short_block_det_factory); + + // Create polar decoder factory. + std::shared_ptr polar_dec_factory = create_polar_factory_sw(); + TESTASSERT(polar_dec_factory); + + // Create CRC calculator factory. + std::shared_ptr crc_calc_factory = create_crc_calculator_factory_sw("auto"); + TESTASSERT(crc_calc_factory); + + // Create UCI decoder factory. + std::shared_ptr uci_dec_factory = + create_uci_decoder_factory_generic(short_block_det_factory, polar_dec_factory, crc_calc_factory); + TESTASSERT(uci_dec_factory); + + // Create PUCCH processor factory. + channel_estimate::channel_estimate_dimensions max_dimensions = {.nof_prb = bwp_size_rb, + .nof_symbols = get_nsymb_per_slot(cp), + .nof_rx_ports = max_nof_ports, + .nof_tx_layers = pucch_constants::MAX_LAYERS}; + pucch_proc_factory = create_pucch_processor_factory_sw( + estimator_factory, detector_factory, pucch_demod_factory, uci_dec_factory, max_dimensions); + TESTASSERT(pucch_proc_factory); + + if (nof_threads > 1) { + pucch_proc_factory = create_pucch_processor_pool_factory(std::move(pucch_proc_factory), nof_threads); + TESTASSERT(pucch_proc_factory); + } + + return *pucch_proc_factory; +} + +// Instantiates the pucch processor and validator. +static std::tuple, std::unique_ptr> create_processor() +{ + pucch_processor_factory& pucch_proc_factory = get_pucch_processor_factory(); + + // Create pucch processor. + std::unique_ptr processor = pucch_proc_factory.create(); + TESTASSERT(processor); + + // Create pucch processor validator. + std::unique_ptr validator = pucch_proc_factory.create_validator(); + TESTASSERT(validator); + + return std::make_tuple(std::move(processor), std::move(validator)); +} + +template +std::optional get_config(const pucch_configuration& test_case) +{ + if (std::holds_alternative(test_case)) { + return std::get(test_case); + } + + return std::nullopt; +} + +static void thread_process(pucch_processor& proc, const pucch_configuration& config, const resource_grid_reader& grid) +{ + while (!thread_quit) { + // If the pending count is equal to or lower than zero, wait for a new start. + while (pending_count.fetch_sub(1) <= 0) { + // Wait for pending to non-negative. + while (pending_count.load() <= 0) { + // Sleep. + std::this_thread::sleep_for(thread_sync_sleep_duration); + + // Quit if signaled. + if (thread_quit) { + return; + } + } + } + + if (auto pucch0 = get_config(config)) { + proc.process(grid, *pucch0); + } else if (auto pucch1 = get_config(config)) { + proc.process(grid, *pucch1); + } else if (auto pucch2 = get_config(config)) { + proc.process(grid, *pucch2); + } else if (auto pucch3 = get_config(config)) { + proc.process(grid, *pucch3); + } else if (auto pucch4 = get_config(config)) { + proc.process(grid, *pucch4); + } + + // Notify finish count. + ++finish_count; + } +} + +// Creates a resource grid. +static std::unique_ptr create_resource_grid(unsigned nof_ports, unsigned nof_symbols, unsigned nof_subc) +{ + std::shared_ptr rg_factory = create_resource_grid_factory(); + TESTASSERT(rg_factory != nullptr, "Invalid resource grid factory."); + + return rg_factory->create(nof_ports, nof_symbols, nof_subc); +} + +int main(int argc, char** argv) +{ + int ret = parse_args(argc, argv); + if (ret < 0) { + return ret; + } + + // Inform of the benchmark configuration. + if (benchmark_mode != benchmark_modes::silent) { + fmt::print("Launching benchmark for {} threads, {} times per thread, and {} repetitions. Using {} profile.\n", + nof_threads, + batch_size_per_thread, + nof_repetitions, + selected_profile_name); + } + + benchmarker perf_meas("pucch processor", nof_repetitions); + + // Pseudo-random generator. + std::mt19937 rgen(0); + + // Create resource grid. + std::unique_ptr grid = create_resource_grid(max_nof_ports, get_nsymb_per_slot(cp), NRE * bwp_size_rb); + TESTASSERT(grid); + + // Standard complex normal distribution with zero mean. + complex_normal_distribution c_normal_dist = {}; + + // Fill the grid with the random RE. + for (unsigned i_rx_port = 0; i_rx_port != max_nof_ports; ++i_rx_port) { + for (unsigned i_symbol = 0, i_symbol_end = get_nsymb_per_slot(cp); i_symbol != i_symbol_end; ++i_symbol) { + // Obtain view of the OFDM symbol. + span re_view = grid->get_writer().get_view(i_rx_port, i_symbol); + + // Generate random RE. + std::generate(re_view.begin(), re_view.end(), [&rgen, &c_normal_dist]() { return c_normal_dist(rgen); }); + } + } + + // Create processor and validator. + std::unique_ptr processor; + std::unique_ptr validator; + std::tie(processor, validator) = create_processor(); + + for (const test_profile& profile : profile_set) { + // Skip profile if the selected is not default and does not match the current profile. + if ((selected_profile_name != "all") && (profile.name != selected_profile_name)) { + continue; + } + // Get the pucch configuration. + const pucch_configuration& config = profile.pucch_config; + + // Make sure the configuration is valid. + if (auto pucch0 = get_config(config)) { + TESTASSERT(validator->is_valid(*pucch0)); + } else if (auto pucch1 = get_config(config)) { + TESTASSERT(validator->is_valid(*pucch1)); + } else if (auto pucch2 = get_config(config)) { + TESTASSERT(validator->is_valid(*pucch2)); + } else if (auto pucch3 = get_config(config)) { + TESTASSERT(validator->is_valid(*pucch3)); + } else if (auto pucch4 = get_config(config)) { + TESTASSERT(validator->is_valid(*pucch4)); + } + + // Reset finish counter. + finish_count = 0; + pending_count = 0; + thread_quit = false; + + // Prepare threads for the current case. + std::vector threads(nof_threads); + for (unsigned thread_id = 0; thread_id != nof_threads; ++thread_id) { + // Select thread. + unique_thread& thread = threads[thread_id]; + + // Create thread. + thread = unique_thread("thread_" + std::to_string(thread_id), [&proc = *processor, &config, &grid] { + thread_process(proc, config, grid.get()->get_reader()); + }); + } + + // Wait for finish thread init. + while (pending_count.load() != -static_cast(nof_threads)) { + std::this_thread::sleep_for(thread_sync_sleep_duration); + } + + // Run the benchmark. + perf_meas.new_measure(profile.name, nof_threads * batch_size_per_thread, []() mutable { + // Notify start. + finish_count = 0; + pending_count = nof_threads * batch_size_per_thread; + + // Wait for finish. + while (finish_count.load() != (nof_threads * batch_size_per_thread)) { + std::this_thread::sleep_for(thread_sync_sleep_duration); + } + }); + + thread_quit = true; + + for (unique_thread& thread : threads) { + thread.join(); + } + } + + // Print latency. + if ((benchmark_mode == benchmark_modes::latency) || (benchmark_mode == benchmark_modes::all)) { + fmt::print("\n--- Average latency ---\n"); + perf_meas.print_percentiles_time("microseconds", 1e-3 / static_cast(batch_size_per_thread)); + } + + // Print total aggregated throughput. + if ((benchmark_mode == benchmark_modes::throughput_total) || (benchmark_mode == benchmark_modes::all)) { + fmt::print("\n--- Total throughput ---\n"); + perf_meas.print_percentiles_throughput("transmissions"); + } + + // Print average throughput per thread. + if ((benchmark_mode == benchmark_modes::throughput_thread) || (benchmark_mode == benchmark_modes::all)) { + fmt::print("\n--- Thread throughput ---\n"); + perf_meas.print_percentiles_throughput("transmissions", 1.0 / static_cast(nof_threads)); + } + + if (worker_pool) { + worker_pool->stop(); + } + + return 0; +} diff --git a/tests/e2e/tests/attach_detach.py b/tests/e2e/tests/attach_detach.py index e59973fe2a..5ca7b040e7 100644 --- a/tests/e2e/tests/attach_detach.py +++ b/tests/e2e/tests/attach_detach.py @@ -22,6 +22,7 @@ Attach / Detach Tests """ import logging +from time import sleep from typing import Optional, Sequence, Tuple, Union from pytest import mark @@ -62,12 +63,12 @@ def test_smoke( common_scs=30, bandwidth=50, sample_rate=None, - iperf_duration=30, bitrate=HIGH_BITRATE, protocol=IPerfProto.UDP, direction=IPerfDir.BIDIRECTIONAL, global_timing_advance=0, time_alignment_calibration=0, + ue_stop_timeout=15, always_download_artifacts=False, ) @@ -123,13 +124,14 @@ def test_zmq( common_scs=common_scs, bandwidth=bandwidth, sample_rate=None, # default from testbed - iperf_duration=30, bitrate=HIGH_BITRATE, protocol=protocol, direction=direction, global_timing_advance=0, time_alignment_calibration=0, always_download_artifacts=True, + ue_stop_timeout=45, + ue_settle_time=45, ) @@ -176,7 +178,6 @@ def test_rf_udp( common_scs=common_scs, bandwidth=bandwidth, sample_rate=None, # default from testbed - iperf_duration=120, protocol=IPerfProto.UDP, bitrate=HIGH_BITRATE, direction=direction, @@ -198,7 +199,6 @@ def _attach_and_detach_multi_ues( common_scs: int, bandwidth: int, sample_rate: Optional[int], - iperf_duration: int, bitrate: int, protocol: IPerfProto, direction: IPerfDir, @@ -208,6 +208,7 @@ def _attach_and_detach_multi_ues( warning_as_errors: bool = True, reattach_count: int = 1, ue_stop_timeout=30, + ue_settle_time=0, ): logging.info("Attach / Detach Test") @@ -232,6 +233,8 @@ def _attach_and_detach_multi_ues( ue_array_to_iperf = ue_array[::2] ue_array_to_attach = ue_array[1::2] + iperf_duration = reattach_count * ((ue_stop_timeout * len(ue_array_to_attach)) + ue_settle_time) + # Starting iperf in half of the UEs iperf_array = [] for ue_stub in ue_array_to_iperf: @@ -253,6 +256,7 @@ def _attach_and_detach_multi_ues( # Stop and attach half of the UEs while the others are connecting and doing iperf for _ in range(reattach_count): ue_stop(ue_array_to_attach, retina_data, ue_stop_timeout=ue_stop_timeout) + sleep(ue_settle_time) ue_attach_info_dict = ue_start_and_attach(ue_array_to_attach, gnb, fivegc) # final stop will be triggered by teardown diff --git a/tests/e2e/tests/handover.py b/tests/e2e/tests/handover.py index 52b7e88b8a..12456d977f 100644 --- a/tests/e2e/tests/handover.py +++ b/tests/e2e/tests/handover.py @@ -74,7 +74,7 @@ def test_smoke_sequentially( common_scs=30, bandwidth=50, noise_spd=0, - sleep_between_movement_steps=2, + sleep_between_movement_steps=1, always_download_artifacts=False, ) diff --git a/tests/e2e/tests/reestablishment.py b/tests/e2e/tests/reestablishment.py index a9e0124cf6..08269b4781 100644 --- a/tests/e2e/tests/reestablishment.py +++ b/tests/e2e/tests/reestablishment.py @@ -59,7 +59,7 @@ def test_smoke_sequentially( retina_manager: RetinaTestManager, retina_data: RetinaTestData, - ue_4: Tuple[UEStub, ...], + ue_2: Tuple[UEStub, ...], fivegc: FiveGCStub, gnb: GNBStub, ): @@ -69,7 +69,7 @@ def test_smoke_sequentially( _reestablishment_sequentially_ping( retina_manager=retina_manager, retina_data=retina_data, - ue_array=ue_4, + ue_array=ue_2, fivegc=fivegc, gnb=gnb, metrics_summary=None, @@ -295,7 +295,7 @@ def test_zmq_reestablishment_parallel( always_download_artifacts=True, noise_spd=noise_spd, log_ip_level="debug", - warning_as_errors=True, + warning_as_errors=False, ) as ue_attach_info_dict: for i in range(number_of_reestablishments): diff --git a/tests/e2e/tests/test_mode/config_ue.yml b/tests/e2e/tests/test_mode/config_ue.yml index cd6b8925ed..b1c33ec517 100644 --- a/tests/e2e/tests/test_mode/config_ue.yml +++ b/tests/e2e/tests/test_mode/config_ue.yml @@ -22,7 +22,7 @@ cu_up: warn_on_drop: False cu_cp: - pdu_session_setup_timeout: 5 + request_pdu_session_timeout: 5 inactivity_timer: 3 rrc: force_reestablishment_fallback: false diff --git a/tests/e2e/tests/viavi/test_declaration.yml b/tests/e2e/tests/viavi/test_declaration.yml index 878124acb7..f64bf024ec 100644 --- a/tests/e2e/tests/viavi/test_declaration.yml +++ b/tests/e2e/tests/viavi/test_declaration.yml @@ -47,7 +47,7 @@ tests: # test/fail criteria expected_dl_bitrate: 1.2e+9 expected_ul_bitrate: 80.0e+6 - expected_nof_kos: 3 + expected_nof_kos: 10 warning_as_errors: true - campaign_filename: *campaign_filename @@ -61,7 +61,7 @@ tests: # test/fail criteria expected_dl_bitrate: 1.2e+9 expected_ul_bitrate: 80.0e+6 - expected_nof_kos: 3 + expected_nof_kos: 10 warning_as_errors: true - campaign_filename: *campaign_filename @@ -103,7 +103,7 @@ tests: # test/fail criteria expected_dl_bitrate: 1.0e+9 expected_ul_bitrate: 1.0e+3 - expected_nof_kos: 3 + expected_nof_kos: 10 warning_as_errors: true - campaign_filename: *campaign_filename @@ -117,7 +117,7 @@ tests: # test/fail criteria expected_dl_bitrate: 1.0e+9 expected_ul_bitrate: 1.0e+3 - expected_nof_kos: 3 + expected_nof_kos: 10 warning_as_errors: true - campaign_filename: *campaign_filename @@ -159,51 +159,51 @@ tests: # test/fail criteria expected_dl_bitrate: 14.0e+3 expected_ul_bitrate: 1.0e+3 - expected_nof_kos: 4 + expected_nof_kos: 10 warning_as_errors: true - campaign_filename: *campaign_filename - test_name: "32UE ideal UDP attach-detach with traffic" + test_name: "32UE ideal UDP attach-detach with traffic High brate" test_timeout: *test_timeout gnb_extra_commands: "log --ngap_level=debug --metrics_level=info metrics --rlc_report_period=1000" - id: "32UE ideal UDP attach-detach with traffic DDDSU" + id: "32UE ideal UDP attach-detach with traffic tdd DDDSU" max_pdschs_per_slot: 1 max_puschs_per_slot: 4 enable_qos_viavi: false # test/fail criteria expected_dl_bitrate: 14.0e+3 expected_ul_bitrate: 1.0e+3 - expected_nof_kos: 4 + expected_nof_kos: 10 warning_as_errors: true enable_dddsu: true - campaign_filename: *campaign_filename - test_name: "32UE ideal UDP attach-detach with traffic" + test_name: "32UE ideal UDP attach-detach with traffic High brate" test_timeout: *test_timeout gnb_extra_commands: "log --ngap_level=debug --metrics_level=info metrics --rlc_report_period=1000" - id: "32UE ideal UDP attach-detach with traffic UL Heavy 7u2d" + id: "32UE ideal UDP attach-detach with traffic tdd UL Heavy 7u2d" max_pdschs_per_slot: 1 max_puschs_per_slot: 4 enable_qos_viavi: false # test/fail criteria expected_dl_bitrate: 14.0e+3 expected_ul_bitrate: 1.0e+3 - expected_nof_kos: 4 + expected_nof_kos: 10 warning_as_errors: true ul_heavy_7u2d: true - campaign_filename: *campaign_filename - test_name: "32UE ideal UDP attach-detach with traffic" + test_name: "32UE ideal UDP attach-detach with traffic High brate" test_timeout: *test_timeout gnb_extra_commands: "log --ngap_level=debug --metrics_level=info metrics --rlc_report_period=1000" - id: "32UE ideal UDP attach-detach with traffic UL Heavy 6u3d" + id: "32UE ideal UDP attach-detach with traffic tdd UL Heavy 6u3d" max_pdschs_per_slot: 1 max_puschs_per_slot: 4 enable_qos_viavi: false # test/fail criteria expected_dl_bitrate: 14.0e+3 expected_ul_bitrate: 1.0e+3 - expected_nof_kos: 4 + expected_nof_kos: 10 warning_as_errors: true ul_heavy_6u3d: true @@ -232,7 +232,7 @@ tests: # test/fail criteria expected_dl_bitrate: 14.0e+3 expected_ul_bitrate: 1.0e+3 - expected_nof_kos: 3 + expected_nof_kos: 10 warning_as_errors: true - campaign_filename: *campaign_filename @@ -246,7 +246,7 @@ tests: # test/fail criteria expected_dl_bitrate: 14.0e+3 expected_ul_bitrate: 1.0e+3 - expected_nof_kos: 3 + expected_nof_kos: 10 warning_as_errors: true - campaign_filename: *campaign_filename @@ -260,7 +260,7 @@ tests: # test/fail criteria expected_dl_bitrate: 1.2e+9 expected_ul_bitrate: 80.0e+6 - expected_nof_kos: 3 + expected_nof_kos: 10 warning_as_errors: false - campaign_filename: *campaign_filename @@ -274,5 +274,5 @@ tests: # test/fail criteria expected_dl_bitrate: 1.2e+9 expected_ul_bitrate: 80.0e+6 - expected_nof_kos: 3 + expected_nof_kos: 10 warning_as_errors: false diff --git a/tests/e2e/tests/viavi/test_declaration_debug.yml b/tests/e2e/tests/viavi/test_declaration_debug.yml index 1f97397127..a5d1cf270b 100644 --- a/tests/e2e/tests/viavi/test_declaration_debug.yml +++ b/tests/e2e/tests/viavi/test_declaration_debug.yml @@ -47,5 +47,5 @@ tests: # test/fail criteria expected_dl_bitrate: 1.2e+9 expected_ul_bitrate: 80.0e+6 - expected_nof_kos: 3 + expected_nof_kos: 10 warning_as_errors: false diff --git a/tests/integrationtests/du_high/mac_test_mode_adapter_test.cpp b/tests/integrationtests/du_high/mac_test_mode_adapter_test.cpp index 24e003d624..21dddaeecc 100644 --- a/tests/integrationtests/du_high/mac_test_mode_adapter_test.cpp +++ b/tests/integrationtests/du_high/mac_test_mode_adapter_test.cpp @@ -158,7 +158,7 @@ static mac_uci_pdu make_random_uci_with_csi(rnti_t test_rnti = to_rnti(0x4601)) class base_mac_test_mode_test { protected: - base_mac_test_mode_test(const test_params& params_) : params(params_), adapter{params.test_ue_cfg, phy} + base_mac_test_mode_test(const test_params& params_) : params(params_), adapter{params.test_ue_cfg, phy, 1} { adapter.connect(std::make_unique(mac_events, adapter.get_phy_notifier())); diff --git a/tests/integrationtests/du_high_cu/cu_du_test.cpp b/tests/integrationtests/du_high_cu/cu_du_test.cpp index 59a4172c0a..5abe3d2c28 100644 --- a/tests/integrationtests/du_high_cu/cu_du_test.cpp +++ b/tests/integrationtests/du_high_cu/cu_du_test.cpp @@ -62,8 +62,8 @@ class cu_du_test : public ::testing::Test srs_cu_cp::cu_cp_configuration cu_cfg = config_helpers::make_default_cu_cp_config(); cu_cfg.services.cu_cp_executor = &workers.exec_mapper->du_control_executor(); // reuse du-high ctrl exec cu_cfg.services.timers = &timers; - cu_cfg.ngaps.push_back( - srs_cu_cp::cu_cp_configuration::ngap_params{&*amf, {{7, {{plmn_identity::test_value(), {{1}}}}}}}); + cu_cfg.ngaps.push_back(srs_cu_cp::cu_cp_configuration::ngap_params{ + &*amf, {{7, {{plmn_identity::test_value(), {{slice_service_type{1}}}}}}}}); // create CU-CP. cu_cp_obj = create_cu_cp(cu_cfg); diff --git a/tests/integrationtests/du_high_cu/du_high_cu_test_simulator.cpp b/tests/integrationtests/du_high_cu/du_high_cu_test_simulator.cpp index 810e890a62..dc0236b246 100644 --- a/tests/integrationtests/du_high_cu/du_high_cu_test_simulator.cpp +++ b/tests/integrationtests/du_high_cu/du_high_cu_test_simulator.cpp @@ -85,8 +85,8 @@ du_high_cu_test_simulator::du_high_cu_test_simulator(const du_high_cu_cp_test_si srs_cu_cp::cu_cp_configuration cu_cfg = config_helpers::make_default_cu_cp_config(); cu_cfg.services.cu_cp_executor = workers.cu_cp_exec; cu_cfg.services.timers = &timers; - cu_cfg.ngaps.push_back( - srs_cu_cp::cu_cp_configuration::ngap_params{&n2_gw, {{7, {{plmn_identity::test_value(), {{1}}}}}}}); + cu_cfg.ngaps.push_back(srs_cu_cp::cu_cp_configuration::ngap_params{ + &n2_gw, {{7, {{plmn_identity::test_value(), {{slice_service_type{1}}}}}}}}); // Instatiate CU-CP. cu_cp_inst = create_cu_cp(cu_cfg); diff --git a/tests/integrationtests/ngap/ngap_integration_test.cpp b/tests/integrationtests/ngap/ngap_integration_test.cpp index 3f9ac2ec27..e517158062 100644 --- a/tests/integrationtests/ngap/ngap_integration_test.cpp +++ b/tests/integrationtests/ngap/ngap_integration_test.cpp @@ -133,15 +133,15 @@ class ngap_integration_test : public ::testing::Test cu_cp_configuration cucfg = config_helpers::make_default_cu_cp_config(); cucfg.services.timers = &timers; cucfg.services.cu_cp_executor = &ctrl_worker; - cucfg.ngaps.push_back( - cu_cp_configuration::ngap_params{adapter.get(), {{7, {{plmn_identity::test_value(), {{1}}}}}}}); + cucfg.ngaps.push_back(cu_cp_configuration::ngap_params{ + adapter.get(), {{7, {{plmn_identity::test_value(), {{slice_service_type{1}}}}}}}}); return cucfg; }()) { - cfg.gnb_id = cu_cp_cfg.node.gnb_id; - cfg.ran_node_name = cu_cp_cfg.node.ran_node_name; - cfg.supported_tas = cu_cp_cfg.ngaps.front().supported_tas; - cfg.pdu_session_setup_timeout = cu_cp_cfg.ue.pdu_session_setup_timeout; + cfg.gnb_id = cu_cp_cfg.node.gnb_id; + cfg.ran_node_name = cu_cp_cfg.node.ran_node_name; + cfg.supported_tas = cu_cp_cfg.ngaps.front().supported_tas; + cfg.request_pdu_session_timeout = cu_cp_cfg.ue.request_pdu_session_timeout; } void SetUp() override diff --git a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test.cpp b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test.cpp index 49b67bb2ab..ef776238c4 100644 --- a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test.cpp +++ b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test.cpp @@ -48,7 +48,6 @@ using namespace srsran; static constexpr subcarrier_spacing scs = subcarrier_spacing::kHz30; static constexpr uint16_t rnti = 0x1234; static constexpr unsigned bwp_start_rb = 0; -static constexpr unsigned nof_layers = 1; static constexpr unsigned nof_ofdm_symbols = 14; static const symbol_slot_mask dmrs_symbol_mask = {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; static constexpr unsigned nof_ldpc_iterations = 10; @@ -68,6 +67,7 @@ static std::string channel_fading_distribution = "uniform- static float sinr_dB = 60.0F; static unsigned nof_corrupted_re_per_ofdm_symbol = 0; static unsigned nof_rx_ports = 2; +static unsigned nof_layers = 1; static unsigned bwp_size_rb = 273; static pusch_mcs_table mcs_table = pusch_mcs_table::qam64; static sch_mcs_index mcs_index = 20; @@ -342,6 +342,7 @@ class pxsch_bler_test channel_fading_distribution, sinr_dB, nof_corrupted_re_per_ofdm_symbol, + nof_layers, nof_rx_ports, MAX_RB * NRE, nof_ofdm_symbols, @@ -514,6 +515,8 @@ static void usage(std::string_view prog) fmt::print("\t-S SINR. [Default {}]\n", sinr_dB); fmt::print("\t-N Number of corrupted RE per OFDM symbol. [Default {}]\n", nof_corrupted_re_per_ofdm_symbol); fmt::print("\t-P Number of receive ports. [Default {}]\n", nof_rx_ports); + fmt::print("\t-L Number of transmit layers. It must not exceed the number of ports. [Default {}]\n", + nof_layers); fmt::print("\t-B Number of allocated PRBs (same as BWP size). [Default {}]\n", bwp_size_rb); fmt::print("\t-M MCS table. [Default {}]\n", mcs_table); fmt::print("\t-m MCS index. [Default {}]\n", mcs_index); @@ -527,7 +530,7 @@ static void usage(std::string_view prog) static void parse_args(int argc, char** argv) { int opt = 0; - while ((opt = getopt(argc, argv, "C:F:S:N:P:R:B:M:m:DT:vh")) != -1) { + while ((opt = getopt(argc, argv, "C:F:S:N:P:L:R:B:M:m:DT:vh")) != -1) { switch (opt) { case 'C': if (optarg != nullptr) { @@ -551,6 +554,9 @@ static void parse_args(int argc, char** argv) case 'P': nof_rx_ports = std::strtol(optarg, nullptr, 10); break; + case 'L': + nof_layers = std::strtol(optarg, nullptr, 10); + break; case 'B': bwp_size_rb = std::strtol(optarg, nullptr, 10); break; diff --git a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.cpp b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.cpp index 3bc5d2146b..57415baab7 100644 --- a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.cpp +++ b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.cpp @@ -91,6 +91,7 @@ channel_emulator::channel_emulator(std::string delay_profile, std::string fading_distribution_, float sinr_dB, unsigned nof_corrupted_re_per_symbol, + unsigned nof_tx_ports, unsigned nof_rx_ports, unsigned nof_subc, unsigned nof_symbols, @@ -98,7 +99,7 @@ channel_emulator::channel_emulator(std::string delay_profile, subcarrier_spacing scs, task_executor& executor_) : nof_ofdm_symbols(nof_symbols), - freq_domain_channel({nof_subc, nof_rx_ports}), + freq_domain_channel({nof_subc, nof_rx_ports, nof_tx_ports}), temp_channel(nof_subc), emulators(max_nof_threads, sinr_dB, nof_corrupted_re_per_symbol, nof_subc), executor(executor_) @@ -156,42 +157,53 @@ channel_emulator::channel_emulator(std::string delay_profile, void channel_emulator::run(resource_grid_writer& rx_grid, const resource_grid_reader& tx_grid) { unsigned nof_rx_ports = freq_domain_channel.get_dimension_size(1); + unsigned nof_tx_ports = freq_domain_channel.get_dimension_size(2); unsigned nof_taps = taps_channel_response.get_dimension_size(1); // Channel emulator. std::atomic count = {0}, completed = {0}; for (unsigned i_rx_port = 0; i_rx_port != nof_rx_ports; ++i_rx_port) { - // Get view of the frequency domain response for this port. - span chan_freq_respone = freq_domain_channel.get_view({i_rx_port}); - - // Generate frequency domain response for the entire slot. - srsvec::zero(chan_freq_respone); - for (unsigned i_tap = 0; i_tap != nof_taps; ++i_tap) { - // Select tap from the fading distribution. - cf_t tap; - switch (fading_distribution) { - case rayleigh: - tap = dist_rayleigh(rgen); - break; - case uniform_phase: - tap = std::polar(1.0F, dist_uniform_phase(rgen)); - break; - case invalid_distribution: - tap = std::numeric_limits::quiet_NaN(); - break; - } + // Generate frequency domain channel response for each transmit port. + for (unsigned i_tx_port = 0; i_tx_port != nof_tx_ports; ++i_tx_port) { + // Get view of the frequency domain response for this port. + span chan_freq_respone = freq_domain_channel.get_view({i_rx_port, i_tx_port}); + + // Generate frequency domain response for the entire slot. + for (unsigned i_tap = 0; i_tap != nof_taps; ++i_tap) { + // Select intermediate tap frequency domain buffer. Use final buffer for the first tap and skip accumulation. + span tap_channel = temp_channel; + if (i_tap == 0) { + tap_channel = chan_freq_respone; + } + + // Select tap from the fading distribution. + cf_t tap; + switch (fading_distribution) { + case rayleigh: + tap = dist_rayleigh(rgen); + break; + case uniform_phase: + tap = std::polar(1.0F, dist_uniform_phase(rgen)); + break; + case invalid_distribution: + tap = std::numeric_limits::quiet_NaN(); + break; + } - // Multiply tap frequency response by a fading distribution tap. - srsvec::sc_prod(taps_channel_response.get_view({i_tap}), tap, temp_channel); + // Multiply tap frequency response by a fading distribution tap. + srsvec::sc_prod(taps_channel_response.get_view({i_tap}), tap, tap_channel); - // Accumulate tap frequency response. - srsvec::add(chan_freq_respone, temp_channel, chan_freq_respone); + // Accumulate tap frequency response. Bypass accumulation for the first tap. + if (i_tap != 0) { + srsvec::add(chan_freq_respone, tap_channel, chan_freq_respone); + } + } } // Run channel for each symbol with the same frequency response. for (unsigned i_symbol = 0; i_symbol != nof_ofdm_symbols; ++i_symbol) { - bool success = executor.execute([this, &rx_grid, &tx_grid, chan_freq_respone, i_rx_port, i_symbol, &completed]() { - emulators.get().run(rx_grid, tx_grid, chan_freq_respone, i_rx_port, i_symbol); + bool success = executor.execute([this, &rx_grid, &tx_grid, i_rx_port, i_symbol, &completed]() { + emulators.get().run(rx_grid, tx_grid, freq_domain_channel, i_rx_port, i_symbol); ++completed; }); report_fatal_error_if_not(success, "Failed to enqueue concurrent channel emulate."); @@ -207,21 +219,36 @@ void channel_emulator::run(resource_grid_writer& rx_grid, const resource_grid_re void channel_emulator::concurrent_channel_emulator::run(resource_grid_writer& rx_grid, const resource_grid_reader& tx_grid, - span freq_response, - unsigned i_port, + const tensor<3, cf_t>& freq_response, + unsigned i_rx_port, unsigned i_symbol) { using namespace std::complex_literals; + unsigned nof_tx_ports = freq_response.get_dimension_size(2); - // Get OFDM symbol. - tx_grid.get(temp_ofdm_symbol, 0, i_symbol, 0); + // For each transmit port. + for (unsigned i_tx_port = 0; i_tx_port != nof_tx_ports; ++i_tx_port) { + // Select temporary buffer. + span single_ofdm_symbol = temp_single_ofdm_symbol; + if (i_tx_port == 0) { + single_ofdm_symbol = temp_ofdm_symbol; + } - // Apply frequency domain fading channel. - srsvec::prod(temp_ofdm_symbol, freq_response, temp_ofdm_symbol); + // Get OFDM symbol. + tx_grid.get(single_ofdm_symbol, i_tx_port, i_symbol, 0); + + // Apply frequency domain fading channel. + srsvec::prod(single_ofdm_symbol, freq_response.get_view({i_rx_port, i_tx_port}), single_ofdm_symbol); + + // Skip accumulating for the first transmit port. + if (i_tx_port != 0) { + srsvec::add(temp_ofdm_symbol, single_ofdm_symbol, temp_ofdm_symbol); + } + } // Apply AWGN. - std::generate(temp_awgn.begin(), temp_awgn.end(), [this]() { return dist_awgn(rgen); }); - srsvec::add(temp_ofdm_symbol, temp_awgn, temp_ofdm_symbol); + std::generate(temp_single_ofdm_symbol.begin(), temp_single_ofdm_symbol.end(), [this]() { return dist_awgn(rgen); }); + srsvec::add(temp_ofdm_symbol, temp_single_ofdm_symbol, temp_ofdm_symbol); // Corrupt REs. std::set corrupted_i_subc; @@ -237,5 +264,5 @@ void channel_emulator::concurrent_channel_emulator::run(resource_grid_writer& } // Write the OFDM symbol back to the grid. - rx_grid.put(i_port, i_symbol, 0, temp_ofdm_symbol); + rx_grid.put(i_rx_port, i_symbol, 0, temp_ofdm_symbol); } diff --git a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.h b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.h index de0a4d9919..590e303991 100644 --- a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.h +++ b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.h @@ -55,6 +55,7 @@ class channel_emulator /// uniform-phase. /// \param[in] sinr_dB Signal-to-Interference-plus-Noise Ratio. /// \param[in] nof_corrupted_re_per_symbol Number of corrupted RE per OFDM symbol. Set to zero for no corrupted RE. + /// \param[in] nof_tx_ports Number of transmit ports. /// \param[in] nof_rx_ports Number of receive ports. /// \param[in] nof_subc Number of resource grid subcarriers. /// \param[in] nof_symbols Number of OFDM symbols per slot. @@ -65,6 +66,7 @@ class channel_emulator std::string fading_distribution, float sinr_dB, unsigned nof_corrupted_re_per_symbol, + unsigned nof_tx_ports, unsigned nof_rx_ports, unsigned nof_subc, unsigned nof_symbols, @@ -89,7 +91,7 @@ class channel_emulator dist_corrupted_re(0, nof_subc - 1), nof_corrupted_re(nof_corrupted_re_), temp_ofdm_symbol(nof_subc), - temp_awgn(nof_subc) + temp_single_ofdm_symbol(nof_subc) { } @@ -101,7 +103,7 @@ class channel_emulator /// \param[in] i_symbol OFDM symbol index within the slot. void run(resource_grid_writer& rx_grid, const resource_grid_reader& tx_grid, - span freq_response, + const tensor<3, cf_t>& freq_response, unsigned i_port, unsigned i_symbol); @@ -118,8 +120,8 @@ class channel_emulator unsigned nof_corrupted_re; /// Temporary OFDM frequency domain symbol. std::vector temp_ofdm_symbol; - /// Temporary generated noise for adding to an OFDM symbol. - std::vector temp_awgn; + /// Temporary single OFDM frequency domain symbol. + std::vector temp_single_ofdm_symbol; }; enum { @@ -136,7 +138,7 @@ class channel_emulator /// Uniform real distribution for phase. std::uniform_real_distribution dist_uniform_phase = std::uniform_real_distribution(-M_PI, M_PI); /// Temporary channel sum. - dynamic_tensor<2, cf_t> freq_domain_channel; + dynamic_tensor<3, cf_t> freq_domain_channel; /// Temporary channel. std::vector temp_channel; /// Frequency response of each of the channel taps. diff --git a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_factories.cpp b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_factories.cpp index 53df32b6e5..c26e7818fe 100644 --- a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_factories.cpp +++ b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_factories.cpp @@ -21,6 +21,7 @@ */ #include "pxsch_bler_test_factories.h" +#include "srsran/phy/upper/channel_processors/pusch/pusch_processor_phy_capabilities.h" #if defined(HWACC_PDSCH_ENABLED) && defined(HWACC_PUSCH_ENABLED) #include "srsran/hal/dpdk/bbdev/bbdev_acc.h" #include "srsran/hal/dpdk/bbdev/bbdev_acc_factory.h" @@ -176,7 +177,7 @@ std::shared_ptr srsran::create_sw_pdsch_processor_facto } #endif // HWACC_PDSCH_ENABLED && HWACC_PUSCH_ENABLED - return create_pdsch_concurrent_processor_factory_sw(crc_calc_factory, + return create_pdsch_concurrent_processor_factory_sw(segmenter_factory, ldpc_encoder_factory, ldpc_rate_matcher_factory, pseudo_random_gen_factory, @@ -194,6 +195,8 @@ std::shared_ptr srsran::create_sw_pusch_processor_facto bool dec_enable_early_stop, const std::string& pxsch_type) { + pusch_processor_phy_capabilities pusch_processor_phy_cap = get_pusch_processor_phy_capabilities(); + std::shared_ptr dft_proc_factory = create_dft_processor_factory_fftw_slow(); report_fatal_error_if_not(dft_proc_factory, "Failed to create factory."); @@ -286,13 +289,15 @@ std::shared_ptr srsran::create_sw_pusch_processor_facto report_fatal_error_if_not(uci_dec_factory, "Failed to create factory."); pusch_processor_factory_sw_configuration pusch_proc_factory_config; - pusch_proc_factory_config.estimator_factory = chan_est_factory; - pusch_proc_factory_config.demodulator_factory = pusch_demod_factory; - pusch_proc_factory_config.demux_factory = demux_factory; - pusch_proc_factory_config.decoder_factory = pusch_dec_factory; - pusch_proc_factory_config.uci_dec_factory = uci_dec_factory; - pusch_proc_factory_config.ch_estimate_dimensions = { - MAX_NOF_PRBS, MAX_NSYMB_PER_SLOT, pusch_constants::MAX_NOF_RX_PORTS, 1}; + pusch_proc_factory_config.estimator_factory = chan_est_factory; + pusch_proc_factory_config.demodulator_factory = pusch_demod_factory; + pusch_proc_factory_config.demux_factory = demux_factory; + pusch_proc_factory_config.decoder_factory = pusch_dec_factory; + pusch_proc_factory_config.uci_dec_factory = uci_dec_factory; + pusch_proc_factory_config.ch_estimate_dimensions = {.nof_prb = MAX_NOF_PRBS, + .nof_symbols = MAX_NSYMB_PER_SLOT, + .nof_rx_ports = pusch_constants::MAX_NOF_RX_PORTS, + .nof_tx_layers = pusch_processor_phy_cap.max_nof_layers}; pusch_proc_factory_config.dec_nof_iterations = nof_ldpc_iterations; pusch_proc_factory_config.dec_enable_early_stop = dec_enable_early_stop; pusch_proc_factory_config.max_nof_concurrent_threads = max_nof_threads; diff --git a/tests/unittests/cu_cp/cu_cp_initial_context_setup_test.cpp b/tests/unittests/cu_cp/cu_cp_initial_context_setup_test.cpp index a774b3a525..a895ea86d2 100644 --- a/tests/unittests/cu_cp/cu_cp_initial_context_setup_test.cpp +++ b/tests/unittests/cu_cp/cu_cp_initial_context_setup_test.cpp @@ -212,7 +212,8 @@ class cu_cp_initial_context_setup_test : public cu_cp_test_environment, public : "Failed to receive E1AP Bearer Context Modification"); // Inject E1AP Bearer Context Modification Response and wait for DL RRC Message (containing RRC Reconfiguration) - get_cu_up(0).push_tx_pdu(generate_bearer_context_modification_response(cu_cp_e1ap_id, cu_up_e1ap_id)); + get_cu_up(0).push_tx_pdu(generate_bearer_context_modification_response( + cu_cp_e1ap_id, cu_up_e1ap_id, {}, {{uint_to_pdu_session_id(1), drb_id_t::drb1}})); report_fatal_error_if_not(this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu), "Failed to receive F1AP DL RRC Message (containing RRC Reconfiguration)"); report_fatal_error_if_not(test_helpers::is_valid_dl_rrc_message_transfer(f1ap_pdu), diff --git a/tests/unittests/cu_cp/cu_cp_intra_du_handover_test.cpp b/tests/unittests/cu_cp/cu_cp_intra_du_handover_test.cpp index 0d100288e0..dd5693ba1a 100644 --- a/tests/unittests/cu_cp/cu_cp_intra_du_handover_test.cpp +++ b/tests/unittests/cu_cp/cu_cp_intra_du_handover_test.cpp @@ -169,6 +169,22 @@ class cu_cp_intra_du_handover_test : public cu_cp_test_environment, public ::tes return true; } + [[nodiscard]] bool timeout_rrc_reconfiguration_and_await_f1ap_ue_context_release_command() + { + // Fail RRC Reconfiguration (UE doesn't respond) and wait for F1AP UE Context Release Command. + if (tick_until( + std::chrono::milliseconds(this->get_cu_cp_cfg().rrc.rrc_procedure_timeout_ms), + [&]() { return false; }, + false)) { + return false; + } + report_fatal_error_if_not(this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu), + "Failed to receive UE Context Release Command"); + report_fatal_error_if_not(test_helpers::is_valid_ue_context_release_command(f1ap_pdu), + "Invalid UE Context Release Command"); + return true; + } + [[nodiscard]] bool send_rrc_reconfiguration_complete() { get_du(du_idx).push_ul_pdu(generate_ul_rrc_message_transfer( @@ -238,6 +254,31 @@ TEST_F(cu_cp_intra_du_handover_test, when_bearer_context_modification_fails_then ASSERT_EQ(report.ues.size(), 1) << "UE should be removed"; } +TEST_F(cu_cp_intra_du_handover_test, when_rrc_reconfiguration_fails_then_ho_fails) +{ + // Inject Measurement Report and await F1AP UE Context Setup Request. + ASSERT_TRUE(send_rrc_measurement_report_and_await_ue_context_setup_request()); + + // Inject UE Context Setup Response and await Bearer Context Modification Request. + ASSERT_TRUE(send_ue_context_setup_response_and_await_bearer_context_modification_request()); + + // Inject Bearer Context Modification Response and await UE Context Modification Request. + ASSERT_TRUE(send_bearer_context_modification_response_and_await_ue_context_modification_request()); + + // Inject UE Context Modification Response. + ASSERT_TRUE(send_ue_context_modification_response()); + + // Let the RRC Reconfiguration timeout and await F1AP UE Context Release Command for target UE. + ASSERT_TRUE(timeout_rrc_reconfiguration_and_await_f1ap_ue_context_release_command()); + + // // Inject F1AP UE Context Release Complete for target UE. + ASSERT_TRUE(send_f1ap_ue_context_release_complete(target_cu_ue_id, target_du_ue_id)); + + // STATUS: Target UE should be removed from DU. + auto report = this->get_cu_cp().get_metrics_handler().request_metrics_report(); + ASSERT_EQ(report.ues.size(), 1) << "Target UE should be removed"; +} + TEST_F(cu_cp_intra_du_handover_test, when_ho_succeeds_then_source_ue_is_removed) { // Inject Measurement Report and await F1AP UE Context Setup Request. @@ -260,5 +301,5 @@ TEST_F(cu_cp_intra_du_handover_test, when_ho_succeeds_then_source_ue_is_removed) // STATUS: Source UE should be removed from DU. auto report = this->get_cu_cp().get_metrics_handler().request_metrics_report(); - ASSERT_EQ(report.ues.size(), 1) << "UE should be removed"; + ASSERT_EQ(report.ues.size(), 1) << "Source UE should be removed"; } diff --git a/tests/unittests/cu_cp/cu_cp_pdu_session_resource_setup_test.cpp b/tests/unittests/cu_cp/cu_cp_pdu_session_resource_setup_test.cpp index b4cec459ed..90b1435b10 100644 --- a/tests/unittests/cu_cp/cu_cp_pdu_session_resource_setup_test.cpp +++ b/tests/unittests/cu_cp/cu_cp_pdu_session_resource_setup_test.cpp @@ -199,7 +199,8 @@ class cu_cp_pdu_session_resource_setup_test : public cu_cp_test_environment, pub const std::map& pdu_sessions_to_add = {}, const std::map& pdu_sessions_to_modify = {{pdu_session_id_t::min, drb_id_t::drb1}}, const std::optional>& expected_srbs_to_add_mod = std::nullopt, - const std::optional>& expected_drbs_to_add_mod = std::nullopt) + const std::optional>& expected_drbs_to_add_mod = std::nullopt, + const std::vector& pdu_sessions_failed_to_modify = {}) { return cu_cp_test_environment::send_bearer_context_modification_response_and_await_rrc_reconfiguration( du_idx, @@ -208,7 +209,8 @@ class cu_cp_pdu_session_resource_setup_test : public cu_cp_test_environment, pub pdu_sessions_to_add, pdu_sessions_to_modify, expected_srbs_to_add_mod, - expected_drbs_to_add_mod); + expected_drbs_to_add_mod, + pdu_sessions_failed_to_modify); } [[nodiscard]] bool timeout_rrc_reconfiguration_and_await_pdu_session_setup_response() @@ -312,6 +314,29 @@ TEST_F(cu_cp_pdu_session_resource_setup_test, when_bearer_context_modification_f ASSERT_TRUE(send_bearer_context_modification_failure_and_await_pdu_session_setup_response()); } +TEST_F(cu_cp_pdu_session_resource_setup_test, + when_bearer_context_modification_response_contains_failed_to_modify_list_then_no_pdu_session_is_setup) +{ + // Inject NGAP PDU Session Resource Setup Request and await Bearer Context Setup Request + ASSERT_TRUE(send_pdu_session_resource_setup_request_and_await_bearer_context_setup_request( + generate_valid_pdu_session_resource_setup_request_message( + ue_ctx->amf_ue_id.value(), ue_ctx->ran_ue_id.value(), {{psi, {{qfi, 9}}}}))); + + // Inject Bearer Context Setup Response and await UE Context Modification Request + ASSERT_TRUE(send_bearer_context_setup_response_and_await_ue_context_modification_request()); + + // Inject UE Context Modification Response and await Bearer Context Modification Request + ASSERT_TRUE(send_ue_context_modification_response_and_await_bearer_context_modification_request()); + + // Inject Bearer Context Modification Response and await DL RRC Message Transfer containing RRC Reconfiguration + ASSERT_TRUE(send_bearer_context_modification_response_and_await_rrc_reconfiguration( + {}, {}, std::vector{srb_id_t::srb2}, {}, {psi})); + + // Inject RRC Reconfiguration Complete and await successful PDU Session Resource Setup Response + ASSERT_TRUE(send_rrc_reconfiguration_complete_and_await_pdu_session_setup_response( + generate_rrc_reconfiguration_complete_pdu(3, 7), {}, {psi})); +} + TEST_F(cu_cp_pdu_session_resource_setup_test, when_rrc_reconfiguration_fails_then_setup_fails) { // Inject NGAP PDU Session Resource Setup Request and await Bearer Context Setup Request @@ -382,7 +407,7 @@ TEST_F(cu_cp_pdu_session_resource_setup_test, when_setup_for_pdu_sessions_with_t // Inject Bearer Context Modification Response and await DL RRC Message Transfer containing RRC Reconfiguration ASSERT_TRUE(send_bearer_context_modification_response_and_await_rrc_reconfiguration( {}, - {{psi, drb_id_t::drb1}, {psi2, drb_id_t::drb2}}, + {{psi, drb_id_t::drb1}}, std::vector{srb_id_t::srb2}, std::vector{drb_id_t::drb1, drb_id_t::drb2})); @@ -412,8 +437,8 @@ TEST_F( ASSERT_TRUE(send_ue_context_modification_response_and_await_bearer_context_modification_request()); // Inject Bearer Context Modification Response and await DL RRC Message Transfer containing RRC Reconfiguration - get_cu_up(cu_up_idx).push_tx_pdu( - generate_bearer_context_modification_response(ue_ctx->cu_cp_e1ap_id.value(), cu_up_e1ap_id)); + get_cu_up(cu_up_idx).push_tx_pdu(generate_bearer_context_modification_response( + ue_ctx->cu_cp_e1ap_id.value(), cu_up_e1ap_id, {}, {{uint_to_pdu_session_id(1), drb_id_t::drb1}})); ASSERT_TRUE(this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu)); ASSERT_TRUE(test_helpers::is_valid_dl_rrc_message_transfer(f1ap_pdu)); { diff --git a/tests/unittests/cu_cp/cu_cp_test_environment.cpp b/tests/unittests/cu_cp/cu_cp_test_environment.cpp index a39015fe12..f59ce05485 100644 --- a/tests/unittests/cu_cp/cu_cp_test_environment.cpp +++ b/tests/unittests/cu_cp/cu_cp_test_environment.cpp @@ -213,7 +213,7 @@ cu_cp_test_environment::cu_cp_test_environment(cu_cp_test_env_params params_) : cu_cp_cfg.f1ap.proc_timeout = std::chrono::milliseconds(10000); // procedure timeouts should only occur intentionally // > UE config - cu_cp_cfg.ue.pdu_session_setup_timeout = + cu_cp_cfg.ue.request_pdu_session_timeout = std::chrono::seconds(10); // procedure timeouts should only occur intentionally // create CU-CP instance. @@ -726,7 +726,7 @@ bool cu_cp_test_environment::send_bearer_context_modification_response_and_await // Inject Bearer Context Modification Response and wait for UE Context Modification Request get_cu_up(cu_up_idx).push_tx_pdu(generate_bearer_context_modification_response( - ue_ctx.cu_cp_e1ap_id.value(), ue_ctx.cu_up_e1ap_id.value(), {{psi, drb_test_params{drb_id, qfi}}})); + ue_ctx.cu_cp_e1ap_id.value(), ue_ctx.cu_up_e1ap_id.value(), {{psi, drb_test_params{drb_id, qfi}}}, {})); bool result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); report_fatal_error_if_not(result, "Failed to receive UE Context Modification Request"); report_fatal_error_if_not(test_helpers::is_valid_ue_context_modification_request(f1ap_pdu), @@ -765,7 +765,8 @@ bool cu_cp_test_environment::send_bearer_context_modification_response_and_await const std::map& pdu_sessions_to_add, const std::map& pdu_sessions_to_modify, const std::optional>& expected_srbs_to_add_mod, - const std::optional>& expected_drbs_to_add_mod) + const std::optional>& expected_drbs_to_add_mod, + const std::vector& pdu_sessions_failed_to_modify) { f1ap_message f1ap_pdu; srsran_assert(not this->get_du(du_idx).try_pop_dl_pdu(f1ap_pdu), "there are still F1AP DL messages to pop from DU"); @@ -773,8 +774,11 @@ bool cu_cp_test_environment::send_bearer_context_modification_response_and_await auto& ue_ctx = attached_ues.at(du_ue_id_to_ran_ue_id_map.at(du_idx).at(du_ue_id)); // Inject E1AP Bearer Context Modification Response and wait for DL RRC Message (containing RRC Reconfiguration) - get_cu_up(cu_up_idx).push_tx_pdu(generate_bearer_context_modification_response( - ue_ctx.cu_cp_e1ap_id.value(), ue_ctx.cu_up_e1ap_id.value(), pdu_sessions_to_add, pdu_sessions_to_modify)); + get_cu_up(cu_up_idx).push_tx_pdu(generate_bearer_context_modification_response(ue_ctx.cu_cp_e1ap_id.value(), + ue_ctx.cu_up_e1ap_id.value(), + pdu_sessions_to_add, + pdu_sessions_to_modify, + pdu_sessions_failed_to_modify)); bool result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); report_fatal_error_if_not(result, "Failed to receive F1AP DL RRC Message (containing RRC Reconfiguration)"); report_fatal_error_if_not(test_helpers::is_valid_dl_rrc_message_transfer(f1ap_pdu), diff --git a/tests/unittests/cu_cp/cu_cp_test_environment.h b/tests/unittests/cu_cp/cu_cp_test_environment.h index f9ddeff29e..8c0a1f2188 100644 --- a/tests/unittests/cu_cp/cu_cp_test_environment.h +++ b/tests/unittests/cu_cp/cu_cp_test_environment.h @@ -45,12 +45,15 @@ struct cu_cp_test_amf_config { }; struct cu_cp_test_env_params { - cu_cp_test_env_params(unsigned max_nof_cu_ups_ = 8, - unsigned max_nof_dus_ = 8, - unsigned max_nof_ues_ = 8192, - unsigned max_nof_drbs_per_ue_ = 8, - const std::vector>& amf_config_ = - {{supported_tracking_area{7, {{plmn_identity::test_value(), {{1}}}}}}}) : + cu_cp_test_env_params( + unsigned max_nof_cu_ups_ = 8, + unsigned max_nof_dus_ = 8, + unsigned max_nof_ues_ = 8192, + unsigned max_nof_drbs_per_ue_ = 8, + const std::vector>& amf_config_ = {{supported_tracking_area{ + 7, + {plmn_item{plmn_identity::test_value(), + std::vector{s_nssai_t{slice_service_type{1}, slice_differentiator{}}}}}}}}) : max_nof_cu_ups(max_nof_cu_ups_), max_nof_dus(max_nof_dus_), max_nof_ues(max_nof_ues_), @@ -222,7 +225,8 @@ class cu_cp_test_environment const std::map& pdu_sessions_to_add = {}, const std::map& pdu_sessions_to_modify = {{pdu_session_id_t::min, drb_id_t::drb1}}, const std::optional>& expected_srbs_to_add_mod = std::nullopt, - const std::optional>& expected_drbs_to_add_mod = std::nullopt); + const std::optional>& expected_drbs_to_add_mod = std::nullopt, + const std::vector& pdu_sessions_failed_to_modify = {}); [[nodiscard]] bool send_rrc_reconfiguration_complete_and_await_pdu_session_setup_response( unsigned du_idx, diff --git a/tests/unittests/cu_cp/cu_cp_test_messages.cpp b/tests/unittests/cu_cp/cu_cp_test_messages.cpp index afbbc60d35..c5d16d919c 100644 --- a/tests/unittests/cu_cp/cu_cp_test_messages.cpp +++ b/tests/unittests/cu_cp/cu_cp_test_messages.cpp @@ -58,7 +58,7 @@ srsran::srs_cu_cp::generate_pdu_session_resource_setup(ue_index_t ue_index, (void)item.pdu_session_nas_pdu.resize(2); item.pdu_session_nas_pdu[0] = 0xaa; item.pdu_session_nas_pdu[1] = 0xbb; - item.s_nssai.sst = 1; + item.s_nssai.sst = slice_service_type{1}; item.pdu_session_aggregate_maximum_bit_rate_dl = 100; item.pdu_session_aggregate_maximum_bit_rate_ul = 100; diff --git a/tests/unittests/cu_cp/du_processor/du_processor_test_helpers.cpp b/tests/unittests/cu_cp/du_processor/du_processor_test_helpers.cpp index f6cc203c23..fae1da11f7 100644 --- a/tests/unittests/cu_cp/du_processor/du_processor_test_helpers.cpp +++ b/tests/unittests/cu_cp/du_processor/du_processor_test_helpers.cpp @@ -118,7 +118,8 @@ du_processor_test::du_processor_test() : cu_cp_configuration cucfg = config_helpers::make_default_cu_cp_config(); cucfg.services.timers = &timers; cucfg.services.cu_cp_executor = &ctrl_worker; - cu_cp_cfg.ngaps.push_back(cu_cp_configuration::ngap_params{nullptr, {{7, {{plmn_identity::test_value(), {{1}}}}}}}); + cu_cp_cfg.ngaps.push_back( + cu_cp_configuration::ngap_params{nullptr, {{7, {{plmn_identity::test_value(), {{slice_service_type{1}}}}}}}}); return cucfg; }()), diff --git a/tests/unittests/cu_cp/test_helpers.h b/tests/unittests/cu_cp/test_helpers.h index 315a6a5515..7549511dee 100644 --- a/tests/unittests/cu_cp/test_helpers.h +++ b/tests/unittests/cu_cp/test_helpers.h @@ -470,7 +470,7 @@ struct dummy_f1ap_ue_context_manager : public f1ap_ue_context_manager { logger.info("Received a new UE context modification request"); // store request so it can be verified in the test code - make_partial_copy(ue_context_modifcation_request, request); + make_partial_copy(ue_context_modification_request, request); return launch_async([res = f1ap_ue_context_modification_response{}, this](coro_context>& ctx) mutable { @@ -507,7 +507,7 @@ struct dummy_f1ap_ue_context_manager : public f1ap_ue_context_manager { bool handle_ue_id_update(ue_index_t ue_index, ue_index_t old_ue_index) override { return true; } - const f1ap_ue_context_modification_request& get_ctxt_mod_request() { return ue_context_modifcation_request; } + const f1ap_ue_context_modification_request& get_ctxt_mod_request() { return ue_context_modification_request; } f1ap_ue_context_release_command last_release_command; @@ -524,7 +524,7 @@ struct dummy_f1ap_ue_context_manager : public f1ap_ue_context_manager { bool ue_context_setup_outcome = false; ue_context_outcome_t ue_context_modification_outcome; - f1ap_ue_context_modification_request ue_context_modifcation_request; + f1ap_ue_context_modification_request ue_context_modification_request; }; struct dummy_cu_up_processor_cu_up_management_notifier : public cu_up_processor_cu_up_management_notifier { diff --git a/tests/unittests/cu_up/pdu_session_manager_test.h b/tests/unittests/cu_up/pdu_session_manager_test.h index 8bded58ca5..8bfe77c919 100644 --- a/tests/unittests/cu_up/pdu_session_manager_test.h +++ b/tests/unittests/cu_up/pdu_session_manager_test.h @@ -139,8 +139,8 @@ generate_pdu_session_res_to_setup_item(pdu_session_id_t psi, drb_id_t drb_id, qo e1ap_pdu_session_res_to_setup_item pdu_session_setup_item; pdu_session_setup_item.pdu_session_id = psi; pdu_session_setup_item.pdu_session_type = "ipv4"; - pdu_session_setup_item.snssai.sst = 1; - pdu_session_setup_item.snssai.sd = 10203; + pdu_session_setup_item.snssai.sst = slice_service_type{1}; + pdu_session_setup_item.snssai.sd = slice_differentiator::create(10203).value(); pdu_session_setup_item.security_ind.integrity_protection_ind = integrity_protection_indication_t::not_needed; pdu_session_setup_item.security_ind.confidentiality_protection_ind = confidentiality_protection_indication_t::not_needed; diff --git a/tests/unittests/du_manager/du_ue/du_bearer_test.cpp b/tests/unittests/du_manager/du_ue/du_bearer_test.cpp index 1f198fa094..9fcaed5f10 100644 --- a/tests/unittests/du_manager/du_ue/du_bearer_test.cpp +++ b/tests/unittests/du_manager/du_ue/du_bearer_test.cpp @@ -50,7 +50,7 @@ class du_ue_bearer_manager_test : public ::testing::Test { du_mng = std::make_unique( std::vector{config_helpers::make_default_du_cell_config()}); - dummy_slice_info = s_nssai_t{.sst = 1}; + dummy_slice_info = s_nssai_t{.sst = slice_service_type{1}}; } void SetUp() override @@ -69,7 +69,7 @@ class du_ue_bearer_manager_test : public ::testing::Test std::vector{config_helpers::make_default_du_cell_config()}); dummy_teid_pool teid_pool; dummy_rlc_rlf_notifier rlf_notifier; - s_nssai_t dummy_slice_info = s_nssai_t{.sst = 1}; + s_nssai_t dummy_slice_info = s_nssai_t{.sst = slice_service_type{1}}; std::unique_ptr create_dummy_drb(drb_id_t drb_id, lcid_t lcid) { diff --git a/tests/unittests/du_manager/procedures/du_ue_ric_configuration_procedure_test.cpp b/tests/unittests/du_manager/procedures/du_ue_ric_configuration_procedure_test.cpp index e72a6663b7..cebfa4d54b 100644 --- a/tests/unittests/du_manager/procedures/du_ue_ric_configuration_procedure_test.cpp +++ b/tests/unittests/du_manager/procedures/du_ue_ric_configuration_procedure_test.cpp @@ -60,16 +60,24 @@ TEST_F(du_ue_ric_config_tester, { std::vector param_list; rrm_policy_ratio_group pol; - pol.max_prb_policy_ratio = 10; + pol.max_prb_policy_ratio = 50; pol.min_prb_policy_ratio = 5; + + unsigned int nof_prbs = get_max_Nprb(params.ran.cells[0].dl_carrier.carrier_bw_mhz, + params.ran.cells[0].scs_common, + band_helper::get_freq_range(params.ran.cells[0].dl_carrier.band)); + int expected_min_prb = static_cast((1.0 * pol.min_prb_policy_ratio.value() / 100) * nof_prbs); + int expected_max_prb = static_cast((1.0 * pol.max_prb_policy_ratio.value() / 100) * nof_prbs); + param_list.emplace_back(control_config_params{std::nullopt, std::nullopt, pol}); start_procedure(du_mac_sched_control_config{(uint64_t)test_ue->f1ap_ue_id, param_list}); ASSERT_TRUE(mac.last_ue_reconf_msg.has_value()) << "MAC should have received new configuration"; ASSERT_EQ(mac.last_ue_reconf_msg->ue_index, test_ue->ue_index); ASSERT_TRUE(mac.last_ue_reconf_msg->sched_cfg.res_alloc_cfg.has_value()); - prb_interval expected_prbs{5, 10}; + prb_interval expected_prbs{expected_min_prb, expected_max_prb}; ASSERT_EQ(mac.last_ue_reconf_msg->sched_cfg.res_alloc_cfg->pdsch_grant_size_limits, expected_prbs); + ASSERT_EQ(mac.last_ue_reconf_msg->sched_cfg.res_alloc_cfg->pusch_grant_size_limits, expected_prbs); ASSERT_FALSE(mac.last_ue_reconf_msg->sched_cfg.cells.has_value()) << "Cells should not have been configured"; ASSERT_FALSE(mac.last_ue_reconf_msg->sched_cfg.lc_config_list.has_value()) << "Logical channels should not have been configured"; diff --git a/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp b/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp index deb044c2c1..f37b9fd58c 100644 --- a/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp +++ b/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp @@ -101,8 +101,8 @@ e1ap_bearer_context_setup_request srsran::srs_cu_cp::generate_bearer_context_set e1ap_pdu_session_res_to_setup_item res_to_setup_item; res_to_setup_item.pdu_session_id = uint_to_pdu_session_id(0); res_to_setup_item.pdu_session_type = "ipv4"; - res_to_setup_item.snssai.sst = 1; - res_to_setup_item.snssai.sd = 10203; + res_to_setup_item.snssai.sst = slice_service_type{1}; + res_to_setup_item.snssai.sd = slice_differentiator::create(10203).value(); res_to_setup_item.security_ind.integrity_protection_ind = integrity_protection_indication_t::not_needed; res_to_setup_item.security_ind.confidentiality_protection_ind = confidentiality_protection_indication_t::not_needed; res_to_setup_item.pdu_session_res_dl_ambr = 330000000; @@ -238,7 +238,8 @@ e1ap_message srsran::srs_cu_cp::generate_bearer_context_modification_response( gnb_cu_cp_ue_e1ap_id_t cu_cp_ue_e1ap_id, gnb_cu_up_ue_e1ap_id_t cu_up_ue_e1ap_id, const std::map& pdu_sessions_to_add, - const std::map& pdu_sessions_to_modify) + const std::map& pdu_sessions_to_modify, + const std::vector& pdu_sessions_failed_to_modify) { e1ap_message bearer_context_modification_response = {}; @@ -304,6 +305,17 @@ e1ap_message srsran::srs_cu_cp::generate_bearer_context_modification_response( ng_ran_bearer_context_mod_resp.pdu_session_res_modified_list.push_back(pdu_session_res_modified_item); } + ng_ran_bearer_context_mod_resp.pdu_session_res_failed_to_modify_list_present = !pdu_sessions_failed_to_modify.empty(); + for (const auto& psi : pdu_sessions_failed_to_modify) { + asn1::e1ap::pdu_session_res_failed_to_modify_item_s pdu_session_res_failed_to_modify_item = {}; + pdu_session_res_failed_to_modify_item.pdu_session_id = pdu_session_id_to_uint(psi); + pdu_session_res_failed_to_modify_item.cause.set_radio_network(); + pdu_session_res_failed_to_modify_item.cause.radio_network() = + asn1::e1ap::cause_radio_network_opts::options::unspecified; + ng_ran_bearer_context_mod_resp.pdu_session_res_failed_to_modify_list.push_back( + pdu_session_res_failed_to_modify_item); + } + return bearer_context_modification_response; } diff --git a/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.h b/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.h index 3170084c85..3a620f9695 100644 --- a/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.h +++ b/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.h @@ -87,7 +87,8 @@ e1ap_message generate_bearer_context_modification_response( gnb_cu_cp_ue_e1ap_id_t cu_cp_ue_e1ap_id, gnb_cu_up_ue_e1ap_id_t cu_up_ue_e1ap_id, const std::map& pdu_sessions_to_add = {}, - const std::map& pdu_sessions_to_modify = {{pdu_session_id_t::min, drb_id_t::drb1}}); + const std::map& pdu_sessions_to_modify = {{pdu_session_id_t::min, drb_id_t::drb1}}, + const std::vector& pdu_sessions_failed_to_modify = {}); /// \brief Generate a dummy Bearer Context Modification Failure. /// \param[in] cu_cp_ue_e1ap_id The CU-CP UE E1AP ID. diff --git a/tests/unittests/e2/CMakeLists.txt b/tests/unittests/e2/CMakeLists.txt index f207515d93..992aa4cc5f 100644 --- a/tests/unittests/e2/CMakeLists.txt +++ b/tests/unittests/e2/CMakeLists.txt @@ -56,6 +56,11 @@ target_link_libraries(e2sm_kpm_meas_provider_test srslog srsran_network srsran_e target_include_directories(e2sm_kpm_meas_provider_test PRIVATE ${CMAKE_SOURCE_DIR}) gtest_discover_tests(e2sm_kpm_meas_provider_test) +add_executable(e2sm_kpm_meas_provider_metrics_test e2sm_kpm_meas_provider_metrics_test.cpp) +target_link_libraries(e2sm_kpm_meas_provider_metrics_test srslog srsran_network srsran_e2 e2ap_asn1 srsran_pcap srsran_gateway srsran_support gtest gtest_main) +target_include_directories(e2sm_kpm_meas_provider_metrics_test PRIVATE ${CMAKE_SOURCE_DIR}) +gtest_discover_tests(e2sm_kpm_meas_provider_metrics_test) + add_executable(e2sm_kpm_test e2sm_kpm_test.cpp) target_link_libraries(e2sm_kpm_test srslog srsran_network srsran_e2 e2ap_asn1 srsran_pcap srsran_gateway srsran_support gtest gtest_main) target_include_directories(e2sm_kpm_test PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/tests/unittests/e2/e2_ric_control_procedure_test.cpp b/tests/unittests/e2/e2_ric_control_procedure_test.cpp index 16c76d76ee..7289d5a92e 100644 --- a/tests/unittests/e2/e2_ric_control_procedure_test.cpp +++ b/tests/unittests/e2/e2_ric_control_procedure_test.cpp @@ -78,12 +78,13 @@ TEST_F(e2_test_setup, ric_control_procedure_packed) { e2->handle_e2_tnl_connection_request(); uint8_t e2ap_ctrl_req[] = { - 0x00, 0x04, 0x00, 0x6e, 0x00, 0x00, 0x05, 0x00, 0x1d, 0x00, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, - 0x02, 0x00, 0x03, 0x00, 0x16, 0x00, 0x09, 0x08, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x05, 0x00, 0x17, 0x00, - 0x46, 0x45, 0x00, 0x00, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x40, 0x00, 0x03, 0x00, 0x02, 0x44, 0x00, 0x00, 0x00, - 0x03, 0x60, 0x00, 0x00, 0x40, 0x00, 0x01, 0x00, 0x05, 0x2a, 0x00, 0x05, 0x30, 0x30, 0x31, 0x30, 0x31, 0x00, 0x06, - 0x44, 0x00, 0x01, 0x00, 0x07, 0x2a, 0x00, 0x01, 0x31, 0x00, 0x08, 0x2a, 0x00, 0x01, 0x30, 0x00, 0x09, 0x28, 0x80, - 0x01, 0x01, 0x00, 0x0a, 0x28, 0x80, 0x01, 0x05, 0x00, 0x0b, 0x28, 0x80, 0x01, 0x64, 0x00, 0x15, 0x00, 0x01, 0x40}; + 0x00, 0x04, 0x00, 0x78, 0x00, 0x00, 0x05, 0x00, 0x1d, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x02, 0x00, 0x03, 0x00, 0x16, 0x00, 0x09, 0x08, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x05, 0x00, + 0x17, 0x00, 0x50, 0x4f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x44, + 0x00, 0x03, 0x00, 0x02, 0x44, 0x00, 0x00, 0x00, 0x04, 0x60, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x05, 0x44, + 0x00, 0x01, 0x00, 0x06, 0x2a, 0x00, 0x03, 0x00, 0xf1, 0x10, 0x00, 0x07, 0x44, 0x00, 0x01, 0x00, 0x08, 0x2a, + 0x00, 0x01, 0x01, 0x00, 0x09, 0x2a, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x28, 0x80, 0x01, 0x01, 0x00, + 0x0b, 0x28, 0x80, 0x01, 0x32, 0x00, 0x0c, 0x28, 0x80, 0x01, 0x64, 0x00, 0x15, 0x00, 0x01, 0x40}; byte_buffer e2ap_ctrl_req_buf = byte_buffer::create(e2ap_ctrl_req, e2ap_ctrl_req + sizeof(e2ap_ctrl_req)).value(); packer->handle_packed_pdu(std::move(e2ap_ctrl_req_buf)); diff --git a/tests/unittests/e2/e2sm_kpm_meas_provider_metrics_test.cpp b/tests/unittests/e2/e2sm_kpm_meas_provider_metrics_test.cpp new file mode 100644 index 0000000000..7c71e2956f --- /dev/null +++ b/tests/unittests/e2/e2sm_kpm_meas_provider_metrics_test.cpp @@ -0,0 +1,325 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "lib/e2/e2sm/e2sm_kpm/e2sm_kpm_cu_meas_provider_impl.h" +#include "lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.h" +#include "tests/unittests/e2/common/e2_test_helpers.h" +#include "srsran/support/srsran_test.h" +#include + +using namespace srsran; +using namespace asn1::e2sm; + +span e2sm_kpm_28_552_metrics = get_e2sm_kpm_28_552_metrics(); +span e2sm_kpm_oran_metrics = get_e2sm_kpm_oran_metrics(); + +bool get_metric_definition(std::string metric_name, e2sm_kpm_metric_t& e2sm_kpm_metric_def) +{ + auto name_matches = [&metric_name](const e2sm_kpm_metric_t& x) { + return (x.name == metric_name.c_str() or x.name == metric_name); + }; + + auto it = std::find_if(e2sm_kpm_28_552_metrics.begin(), e2sm_kpm_28_552_metrics.end(), name_matches); + if (it != e2sm_kpm_28_552_metrics.end()) { + e2sm_kpm_metric_def = *it; + return true; + } + + it = std::find_if(e2sm_kpm_oran_metrics.begin(), e2sm_kpm_oran_metrics.end(), name_matches); + if (it != e2sm_kpm_oran_metrics.end()) { + e2sm_kpm_metric_def = *it; + return true; + } + + return false; +} + +rlc_metrics generate_non_zero_rlc_metrics(uint32_t ue_idx, uint32_t bearer_id) +{ + rlc_metrics rlc_metric; + rlc_metric.metrics_period = std::chrono::milliseconds(1000); + rlc_metric.ue_index = static_cast(ue_idx); + rlc_metric.rb_id = rb_id_t(drb_id_t(bearer_id)); + rlc_metric.rx.mode = rlc_mode::am; + rlc_metric.rx.num_pdus = 5; + rlc_metric.rx.num_pdu_bytes = rlc_metric.rx.num_pdus * 1000; + rlc_metric.rx.num_sdus = 5; + rlc_metric.rx.num_sdu_bytes = rlc_metric.rx.num_sdus * 1000; + rlc_metric.rx.num_lost_pdus = 1; + rlc_metric.rx.num_malformed_pdus = 1; + rlc_metric.rx.sdu_latency_us = 1000; + + rlc_metric.tx.tx_low.mode = rlc_mode::am; + rlc_metric.tx.tx_high.num_sdus = 10; + rlc_metric.tx.tx_high.num_sdu_bytes = rlc_metric.tx.tx_high.num_sdus * 1000; + rlc_metric.tx.tx_high.num_dropped_sdus = 1; + rlc_metric.tx.tx_high.num_discarded_sdus = 1; + rlc_metric.tx.tx_high.num_discard_failures = 1; + rlc_metric.tx.tx_low.sum_sdu_latency_us = 1000; + rlc_metric.tx.tx_low.num_of_pulled_sdus = 1; + rlc_metric.tx.tx_low.num_pdus_no_segmentation = 10; + rlc_metric.tx.tx_low.num_pdu_bytes_no_segmentation = rlc_metric.tx.tx_low.num_pdus_no_segmentation * 1000; + rlc_metric.tx.tx_low.mode_specific.am.num_pdus_with_segmentation = 2; + rlc_metric.tx.tx_low.mode_specific.am.num_pdu_bytes_with_segmentation = + rlc_metric.tx.tx_low.mode_specific.am.num_pdus_with_segmentation * 1000; + return rlc_metric; +} + +scheduler_cell_metrics generate_non_zero_sched_metrics() +{ + scheduler_cell_metrics sched_metric; + sched_metric.nof_prbs = 52; + sched_metric.nof_dl_slots = 14; + sched_metric.nof_ul_slots = 14; + sched_metric.nof_prach_preambles = 10; + + scheduler_ue_metrics ue_metrics = {0}; + ue_metrics.pci = 1; + ue_metrics.rnti = static_cast(0x1000 + 1); + ue_metrics.tot_dl_prbs_used = 1200; + ue_metrics.mean_dl_prbs_used = 12; + ue_metrics.tot_ul_prbs_used = 1200; + ue_metrics.mean_ul_prbs_used = 12; + ue_metrics.ul_delay_ms = 100; + ue_metrics.pusch_snr_db = 10; + for (auto i = 0; i < 10; i++) { + ue_metrics.cqi_stats.update(i); + } + sched_metric.ue_metrics.push_back(ue_metrics); + + return sched_metric; +} + +class dummy_e2_du_metrics_notifier : public e2_du_metrics_notifier, public e2_du_metrics_interface +{ +public: + void report_metrics(const scheduler_cell_metrics& metrics) override + { + if (e2_meas_provider) { + e2_meas_provider->report_metrics(metrics); + } + } + + void report_metrics(const rlc_metrics& metrics) override + { + if (e2_meas_provider) { + e2_meas_provider->report_metrics(metrics); + } + } + + void connect_e2_du_meas_provider(std::unique_ptr meas_provider) override {} + + void connect_e2_du_meas_provider(e2_du_metrics_notifier* meas_provider) { e2_meas_provider = meas_provider; } + +private: + e2_du_metrics_notifier* e2_meas_provider; +}; + +class e2sm_kpm_meas_provider_metrics_test : public ::testing::Test +{ +protected: + void SetUp() override + { + srslog::fetch_basic_logger("TEST").set_level(srslog::basic_levels::debug); + srslog::init(); + f1ap_ue_id_mapper = std::make_unique(); + du_meas_provider = std::make_unique(*f1ap_ue_id_mapper); + metrics = std::make_unique(); + metrics->connect_e2_du_meas_provider(du_meas_provider.get()); + } + + void TearDown() override + { + // Flush logger after each test. + srslog::flush(); + } + + std::unique_ptr metrics; + std::unique_ptr f1ap_ue_id_mapper; + std::unique_ptr du_meas_provider; + srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); +}; + +TEST_F(e2sm_kpm_meas_provider_metrics_test, e2sm_kpm_supported_metrics_are_supported) +{ + e2sm_kpm_metric_level_enum metric_level; + meas_type_c meas_type; + meas_label_s meas_label; + meas_label.no_label_present = true; + meas_label.no_label = meas_label_s::no_label_e_::true_value; + bool cell_scope = false; + std::vector supported_metrics; + bool metric_supported = false; + + // E2-NODE-LEVEL metrics + metric_level = E2_NODE_LEVEL; + supported_metrics = du_meas_provider->get_supported_metric_names(metric_level); + for (auto& metric : supported_metrics) { + meas_type.set_meas_name().from_string(metric); + metric_supported = du_meas_provider->is_metric_supported(meas_type, meas_label, metric_level, cell_scope); + ASSERT_TRUE(metric_supported) << "Metric: " << metric << " Level: " << e2sm_kpm_scope_2_str(metric_level) + << " returned as supported but not supported "; + } + + // UE-LEVEL metrics + metric_level = UE_LEVEL; + supported_metrics = du_meas_provider->get_supported_metric_names(metric_level); + for (auto& metric : supported_metrics) { + meas_type.set_meas_name().from_string(metric); + metric_supported = du_meas_provider->is_metric_supported(meas_type, meas_label, metric_level, cell_scope); + ASSERT_TRUE(metric_supported) << "Metric: " << metric << " Level: " << e2sm_kpm_scope_2_str(metric_level) + << " returned as supported but not supported "; + } +} + +TEST_F(e2sm_kpm_meas_provider_metrics_test, e2sm_kpm_return_e2_level_metric_with_no_measurements) +{ + // metrics that return no_value when no measurements are present. Specifically, they should not return 0 + std::vector no_value_metrics = {"DRB.AirIfDelayUl", + "DRB.PacketSuccessRateUlgNBUu", + "DRB.RlcDelayUl", + "DRB.RlcPacketDropRateDl", + "DRB.RlcSduDelayDl"}; + + // E2-NODE-LEVEL metrics have to be always returned, even if 0 or NAN + e2sm_kpm_metric_level_enum metric_level = E2_NODE_LEVEL; + meas_type_c meas_type; + std::optional cell_global_id = {}; + e2sm_kpm_metric_t e2sm_kpm_metric_definition; + + label_info_list_l label_info_list; + label_info_item_s label_info_item = {}; + label_info_item.meas_label.no_label_present = true; + label_info_item.meas_label.no_label = meas_label_s::no_label_e_::true_value; + label_info_list.push_back(label_info_item); + std::vector meas_records_items; + + std::vector supported_metrics = du_meas_provider->get_supported_metric_names(metric_level); + for (auto& metric : supported_metrics) { + meas_type.set_meas_name().from_string(metric); + + meas_records_items.clear(); + du_meas_provider->get_meas_data(meas_type, label_info_list, {}, cell_global_id, meas_records_items); + + ASSERT_TRUE(meas_records_items.size() == 1) + << "Metric: " << metric << " Level: " << e2sm_kpm_scope_2_str(metric_level) + << " returned no measurements (size=" << meas_records_items.size() << ")"; + + // Check if metric should return no_value when no measurements are present + bool found = std::any_of( + no_value_metrics.begin(), no_value_metrics.end(), [&metric](const std::string& s) { return s == metric; }); + if (found) { + ASSERT_EQ(meas_records_items[0].type(), meas_record_item_c::types::no_value) + << "Metric: " << metric << " Level: " << e2sm_kpm_scope_2_str(metric_level) + << " expected to return no_value when no measurements available."; + continue; + } + if (get_metric_definition(metric, e2sm_kpm_metric_definition)) { + if (e2sm_kpm_metric_definition.data_type == e2sm_kpm_metric_dtype_t::INTEGER) { + ASSERT_EQ(meas_records_items[0].type().value, meas_record_item_c::types::integer) + << "Metric: " << e2sm_kpm_metric_definition.name.c_str() << " should return record of the integer type."; + } else { + // e2sm_kpm_metric_dtype_t::REAL + ASSERT_EQ(meas_records_items[0].type().value, meas_record_item_c::types::real) + << "Metric: " << e2sm_kpm_metric_definition.name.c_str() << " should return record of the real type."; + } + } + + switch (meas_records_items[0].type()) { + case meas_record_item_c::types::integer: + ASSERT_EQ(meas_records_items[0].integer(), 0) + << "Metric: " << metric << " Level: " << e2sm_kpm_scope_2_str(metric_level); + break; + case meas_record_item_c::types::real: + ASSERT_FLOAT_EQ(meas_records_items[0].real().value, 0.0) + << "Metric: " << metric << " Level: " << e2sm_kpm_scope_2_str(metric_level); + break; + default: + printf("%s type: %i\n", metric.c_str(), meas_records_items[0].type().value); + ASSERT_TRUE(false) << "Metric: " << metric << " Level: " << e2sm_kpm_scope_2_str(metric_level) + << " returned a record with wrong type."; + break; + } + } +} + +TEST_F(e2sm_kpm_meas_provider_metrics_test, e2sm_kpm_return_e2_level_metric_with_with_measurements) +{ + // E2-NODE-LEVEL metrics have to be always returned, even if 0 or NAN + e2sm_kpm_metric_level_enum metric_level = E2_NODE_LEVEL; + meas_type_c meas_type; + std::optional cell_global_id = {}; + e2sm_kpm_metric_t e2sm_kpm_metric_definition; + + label_info_list_l label_info_list; + label_info_item_s label_info_item = {}; + label_info_item.meas_label.no_label_present = true; + label_info_item.meas_label.no_label = meas_label_s::no_label_e_::true_value; + label_info_list.push_back(label_info_item); + std::vector meas_records_items; + + // Fill e2sm-kpm measurements provider with RLC and SCHED metrics. + // Generate dummy metrics that will generate non-zero e2sm-kpm metric records. + rlc_metrics rlc_metric = generate_non_zero_rlc_metrics(0, 1); + metrics->report_metrics(rlc_metric); + scheduler_cell_metrics sched_metrics = generate_non_zero_sched_metrics(); + metrics->report_metrics(sched_metrics); + + std::vector supported_metrics = du_meas_provider->get_supported_metric_names(metric_level); + for (auto& metric : supported_metrics) { + meas_type.set_meas_name().from_string(metric); + + meas_records_items.clear(); + du_meas_provider->get_meas_data(meas_type, label_info_list, {}, cell_global_id, meas_records_items); + + ASSERT_TRUE(meas_records_items.size() == 1) + << "Metric: " << metric << " Level: " << e2sm_kpm_scope_2_str(metric_level) + << " returned no measurements (size=" << meas_records_items.size() << ")"; + + if (get_metric_definition(metric, e2sm_kpm_metric_definition)) { + if (e2sm_kpm_metric_definition.data_type == e2sm_kpm_metric_dtype_t::INTEGER) { + ASSERT_EQ(meas_records_items[0].type().value, meas_record_item_c::types::integer) + << "Metric: " << e2sm_kpm_metric_definition.name.c_str() << " should return record of the integer type."; + } else { + // e2sm_kpm_metric_dtype_t::REAL + ASSERT_EQ(meas_records_items[0].type().value, meas_record_item_c::types::real) + << "Metric: " << e2sm_kpm_metric_definition.name.c_str() << " should return record of the real type."; + } + } + + switch (meas_records_items[0].type()) { + case meas_record_item_c::types::integer: + ASSERT_NE(meas_records_items[0].integer(), 0) + << "Metric: " << metric << " Level: " << e2sm_kpm_scope_2_str(metric_level); + break; + case meas_record_item_c::types::real: + ASSERT_NE(meas_records_items[0].real().value, 0.0) + << "Metric: " << metric << " Level: " << e2sm_kpm_scope_2_str(metric_level); + break; + default: + printf("%s type: %i\n", metric.c_str(), meas_records_items[0].type().value); + ASSERT_TRUE(false) << "Metric: " << metric << " Level: " << e2sm_kpm_scope_2_str(metric_level) + << " returned a record with wrong type."; + break; + } + } +} \ No newline at end of file diff --git a/tests/unittests/e2/e2sm_kpm_meas_provider_test.cpp b/tests/unittests/e2/e2sm_kpm_meas_provider_test.cpp index 22101fee47..ca5cc36da1 100644 --- a/tests/unittests/e2/e2sm_kpm_meas_provider_test.cpp +++ b/tests/unittests/e2/e2sm_kpm_meas_provider_test.cpp @@ -304,8 +304,8 @@ TEST_P(e2sm_kpm_du_meas_provider_test, e2sm_kpm_ind_three_drb_rlc_metrics) uint32_t expected_drop_rate = 10; uint32_t expected_ul_success_rate = 80; - uint32_t expected_dl_throughput = 10000 / 1e3 * 8; - uint32_t expected_ul_throughput = 5000 / 1e3 * 8; + float expected_dl_throughput = 10000 / 1e3 * 8; + float expected_ul_throughput = 5000 / 1e3 * 8; std::vector expected_dl_vol; std::vector expected_ul_vol; @@ -410,10 +410,10 @@ TEST_P(e2sm_kpm_du_meas_provider_test, e2sm_kpm_ind_three_drb_rlc_metrics) TESTASSERT_EQ(expected_ul_success_rate, meas_record[3].integer()); } if (nof_records >= 5) { - TESTASSERT_EQ(expected_dl_throughput, meas_record[4].integer()); + TESTASSERT_EQ(expected_dl_throughput, meas_record[4].real().value); } if (nof_records >= 6) { - TESTASSERT_EQ(expected_ul_throughput, meas_record[5].integer()); + TESTASSERT_EQ(expected_ul_throughput, meas_record[5].real().value); } } } diff --git a/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp b/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp index f6c2a97064..344dea8ce7 100644 --- a/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp +++ b/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp @@ -268,8 +268,8 @@ f1ap_ue_context_modification_request srsran::srs_cu_cp::generate_ue_context_modi drbs_to_be_setup_mod_item.qos_info.drb_qos.reflective_qos_attribute_subject_to = true; // s nssai - drbs_to_be_setup_mod_item.qos_info.s_nssai.sst = 1; - drbs_to_be_setup_mod_item.qos_info.s_nssai.sd = 128; + drbs_to_be_setup_mod_item.qos_info.s_nssai.sst = slice_service_type{1}; + drbs_to_be_setup_mod_item.qos_info.s_nssai.sd = slice_differentiator::create(128).value(); // notif ctrl drbs_to_be_setup_mod_item.qos_info.notif_ctrl = drb_notification_control::active; diff --git a/tests/unittests/f1ap/cu_cp/f1ap_cu_test_helpers.cpp b/tests/unittests/f1ap/cu_cp/f1ap_cu_test_helpers.cpp index 2dfeef9653..eb97afa3af 100644 --- a/tests/unittests/f1ap/cu_cp/f1ap_cu_test_helpers.cpp +++ b/tests/unittests/f1ap/cu_cp/f1ap_cu_test_helpers.cpp @@ -159,7 +159,7 @@ srsran::srs_cu_cp::create_ue_context_setup_request(const std::initializer_list is_valid(const pusch_processor::pdu_t& pdu) const override { return default_success_t(); } - bool is_valid(const srs_estimator_configuration& config) const override { return true; } + error_type is_valid(const srs_estimator_configuration& config) const override + { + return default_success_t(); + } }; class resource_grid_pool_dummy : public resource_grid_pool, private shared_resource_grid::pool_interface diff --git a/tests/unittests/ngap/ngap_asn1_packer_test.cpp b/tests/unittests/ngap/ngap_asn1_packer_test.cpp index 1941d641da..cfbfa804f9 100644 --- a/tests/unittests/ngap/ngap_asn1_packer_test.cpp +++ b/tests/unittests/ngap/ngap_asn1_packer_test.cpp @@ -77,7 +77,8 @@ class ngap_asn1_packer_test : public ::testing::Test TEST_F(ngap_asn1_packer_test, when_packing_successful_then_pdu_matches_tv) { // Populate message - ngap_context_t ngap_ctxt = {{411, 22}, "srsgnb01", {{7, {{plmn_identity::test_value(), {{1}}}}}}, {}, 256}; + ngap_context_t ngap_ctxt = { + {411, 22}, "srsgnb01", {{7, {{plmn_identity::test_value(), {{slice_service_type{1}}}}}}}, {}, 256}; ngap_message ngap_msg = {}; ngap_msg.pdu.set_init_msg(); diff --git a/tests/unittests/ngap/ngap_nas_message_test.cpp b/tests/unittests/ngap/ngap_nas_message_test.cpp index 9bfe880fdf..7f9f9df120 100644 --- a/tests/unittests/ngap/ngap_nas_message_test.cpp +++ b/tests/unittests/ngap/ngap_nas_message_test.cpp @@ -101,8 +101,8 @@ TEST_F(ngap_nas_message_routine_test, when_initial_context_setup_request_is_not_ ASSERT_EQ(ngap->get_nof_ues(), 1); // tick timers - // Status: NGAP does not receive new Initial Context Setup Request until pdu_session_setup_timer has ended. - for (unsigned msec_elapsed = 0; msec_elapsed < cu_cp_cfg.ue.pdu_session_setup_timeout.count() * 1000; + // Status: NGAP does not receive new Initial Context Setup Request until request_pdu_session_timer has ended. + for (unsigned msec_elapsed = 0; msec_elapsed < cu_cp_cfg.ue.request_pdu_session_timeout.count() * 1000; ++msec_elapsed) { this->tick(); } diff --git a/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp b/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp index 47b89d3530..e05a09f165 100644 --- a/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp +++ b/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp @@ -123,8 +123,8 @@ TEST_F(ngap_pdu_session_resource_setup_procedure_test, ASSERT_EQ(ngap->get_nof_ues(), 1); // tick timers - // Status: NGAP does not receive new PDU Session Resource Setup Request until pdu_session_setup_timer has ended. - for (unsigned msec_elapsed = 0; msec_elapsed < cu_cp_cfg.ue.pdu_session_setup_timeout.count() * 1000; + // Status: NGAP does not receive new PDU Session Resource Setup Request until request_pdu_session_timer has ended. + for (unsigned msec_elapsed = 0; msec_elapsed < cu_cp_cfg.ue.request_pdu_session_timeout.count() * 1000; ++msec_elapsed) { this->tick(); } diff --git a/tests/unittests/ngap/ngap_test_helpers.cpp b/tests/unittests/ngap/ngap_test_helpers.cpp index 351e5f6dc0..e957fe1ae2 100644 --- a/tests/unittests/ngap/ngap_test_helpers.cpp +++ b/tests/unittests/ngap/ngap_test_helpers.cpp @@ -39,7 +39,12 @@ ngap_test::ngap_test() : cu_cp_configuration cucfg = config_helpers::make_default_cu_cp_config(); cucfg.services.timers = &timers; cucfg.services.cu_cp_executor = &ctrl_worker; - cucfg.ngaps.push_back(cu_cp_configuration::ngap_params{&n2_gw, {{7, {{plmn_identity::test_value(), {{1}}}}}}}); + cucfg.ngaps.push_back(cu_cp_configuration::ngap_params{ + &n2_gw, + {supported_tracking_area{ + 7, + {plmn_item{plmn_identity::test_value(), + std::vector{s_nssai_t{slice_service_type{1}, slice_differentiator{}}}}}}}}); return cucfg; }()) { @@ -48,10 +53,10 @@ ngap_test::ngap_test() : srslog::init(); ngap_configuration ngap_cfg{}; - ngap_cfg.gnb_id = cu_cp_cfg.node.gnb_id; - ngap_cfg.ran_node_name = cu_cp_cfg.node.ran_node_name; - ngap_cfg.supported_tas = cu_cp_cfg.ngaps.front().supported_tas; - ngap_cfg.pdu_session_setup_timeout = cu_cp_cfg.ue.pdu_session_setup_timeout; + ngap_cfg.gnb_id = cu_cp_cfg.node.gnb_id; + ngap_cfg.ran_node_name = cu_cp_cfg.node.ran_node_name; + ngap_cfg.supported_tas = cu_cp_cfg.ngaps.front().supported_tas; + ngap_cfg.request_pdu_session_timeout = cu_cp_cfg.ue.request_pdu_session_timeout; ngap = create_ngap(ngap_cfg, cu_cp_notifier, *cu_cp_cfg.ngaps.front().n2_gw, timers, ctrl_worker); cu_cp_notifier.connect_ngap(ngap->get_ngap_ue_context_removal_handler()); diff --git a/tests/unittests/ngap/ngap_validators_test.cpp b/tests/unittests/ngap/ngap_validators_test.cpp index 8c5c456b8e..6d03fe7af6 100644 --- a/tests/unittests/ngap/ngap_validators_test.cpp +++ b/tests/unittests/ngap/ngap_validators_test.cpp @@ -261,7 +261,7 @@ TEST_F(ngap_validator_test, when_unique_and_duplicate_pdu_session_id_then_pdu_se // Test handling of PduSessionResourceSetupRequest with non-GBR QoS flows but no PDU Session Aggregate Maximum Bit Rate TEST_F( ngap_validator_test, - when_pdu_session_request_contains_non_gbr_qos_flows_but_no_aggregate_maximum_bitrate_then_pdu_session_setup_fails) + when_request_pdu_session_contains_non_gbr_qos_flows_but_no_aggregate_maximum_bitrate_then_pdu_session_setup_fails) { pdu_session_id_t psi = uint_to_pdu_session_id(1); ue_index_t ue_index = uint_to_ue_index(0); diff --git a/tests/unittests/phy/support/resource_grid_test.cpp b/tests/unittests/phy/support/resource_grid_test.cpp index a4d6a4673a..f0882d6a70 100644 --- a/tests/unittests/phy/support/resource_grid_test.cpp +++ b/tests/unittests/phy/support/resource_grid_test.cpp @@ -23,7 +23,6 @@ #include "srsran/phy/support/resource_grid_reader.h" #include "srsran/phy/support/resource_grid_writer.h" #include "srsran/phy/support/support_factories.h" -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/zero.h" #include "srsran/support/srsran_test.h" #include @@ -90,7 +89,7 @@ void test_mask_bitset(unsigned nof_ports, unsigned nof_symbols, unsigned nof_sub // Put elements in grid. unsigned symbol_idx = symbol_dist(rgen); - srsvec::aligned_vec symbols_gold(nof_elements); + std::vector symbols_gold(nof_elements); bounded_bitset mask(nof_subc); // Fill mask and generate symbols. @@ -141,8 +140,8 @@ void test_mask_bitset(unsigned nof_ports, unsigned nof_symbols, unsigned nof_sub } // Get elements using the same mask. - srsvec::aligned_vec symbols(nof_elements); - span symbol_buffer_get = grid->get_reader().get(symbols, port_gold, symbol_idx, 0, mask); + std::vector symbols(nof_elements); + span symbol_buffer_get = grid->get_reader().get(symbols, port_gold, symbol_idx, 0, mask); // Make sure all symbols are used. TESTASSERT(symbol_buffer_get.empty(), "Symbol buffer - not empty."); @@ -172,8 +171,8 @@ void test_consecutive(unsigned nof_ports, unsigned nof_symbols, unsigned nof_sub unsigned port_gold = port_dist(rgen); // Put elements in grid - unsigned symbol_idx = symbol_dist(rgen); - srsvec::aligned_vec symbols_gold(nof_elements); + unsigned symbol_idx = symbol_dist(rgen); + std::vector symbols_gold(nof_elements); // Select initial subcarrier unsigned k_init = subc_dist(rgen); @@ -213,7 +212,7 @@ void test_consecutive(unsigned nof_ports, unsigned nof_symbols, unsigned nof_sub } // Get elements - srsvec::aligned_vec symbols(nof_elements); + std::vector symbols(nof_elements); grid->get_reader().get(symbols, port_gold, symbol_idx, k_init); // Assert symbols diff --git a/tests/unittests/phy/upper/channel_coding/ldpc/ldpc_segmenter_test.cpp b/tests/unittests/phy/upper/channel_coding/ldpc/ldpc_segmenter_test.cpp index 3089da6c82..72f00759f7 100644 --- a/tests/unittests/phy/upper/channel_coding/ldpc/ldpc_segmenter_test.cpp +++ b/tests/unittests/phy/upper/channel_coding/ldpc/ldpc_segmenter_test.cpp @@ -28,6 +28,7 @@ #include "ldpc_segmenter_test_data.h" #include "srsran/phy/upper/channel_coding/channel_coding_factories.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc_segmenter_buffer.h" #include "srsran/srsvec/bit.h" #include @@ -120,17 +121,23 @@ TEST_P(LDPCSegmenterFixture, LDPCSegmenterTest) const std::vector trans_block = test_data.trans_block.read(); const std::vector segments_check = test_data.segments.read(); - static_vector segments; - segmenter_tx->segment(segments, trans_block, seg_cfg); + // Initialize the segmenter. + const ldpc_segmenter_buffer& segment_buffer = segmenter_tx->new_transmission(trans_block, seg_cfg); + static_bit_buffer cb_data; + cb_data.resize(segment_buffer.get_segment_length().value()); - EXPECT_EQ(segments.size(), test_data.nof_segments) << "Wrong number of segments."; + EXPECT_EQ(segment_buffer.get_nof_codeblocks(), test_data.nof_segments) << "Wrong number of segments."; std::vector segment_data(test_data.segment_length); unsigned seg_offset = 0; - for (const auto& seg : segments) { - srsvec::bit_unpack(segment_data, seg.get_data()); - span filler_bits = span(segment_data).last(seg.get_metadata().cb_specific.nof_filler_bits); + for (unsigned i_cb = 0, i_cb_end = segment_buffer.get_nof_codeblocks(); i_cb != i_cb_end; ++i_cb) { + // Copy codeblock data, including TB and/or CB CRC if applicable, as well as filler and zero padding bits. + segment_buffer.read_codeblock(cb_data, trans_block, i_cb); + + srsvec::bit_unpack(segment_data, cb_data); + span filler_bits = + span(segment_data).last(segment_buffer.get_cb_metadata(i_cb).cb_specific.nof_filler_bits); std::fill(filler_bits.begin(), filler_bits.end(), FILLER_BIT); EXPECT_EQ(span(segment_data), @@ -139,7 +146,7 @@ TEST_P(LDPCSegmenterFixture, LDPCSegmenterTest) seg_offset += test_data.segment_length; } - std::vector cw_llrs(segments[0].get_metadata().tb_common.cw_length); + std::vector cw_llrs(segment_buffer.get_cb_metadata(0).tb_common.cw_length); std::generate(cw_llrs.begin(), cw_llrs.end(), [n = static_cast(-127)]() mutable { int8_t r = std::clamp(n, LLR_MIN.to_value_type(), LLR_MAX.to_value_type()); ++n; diff --git a/tests/unittests/phy/upper/channel_coding/polar/polar_chain_test.cpp b/tests/unittests/phy/upper/channel_coding/polar/polar_chain_test.cpp index be555305d2..5f983c66d0 100644 --- a/tests/unittests/phy/upper/channel_coding/polar/polar_chain_test.cpp +++ b/tests/unittests/phy/upper/channel_coding/polar/polar_chain_test.cpp @@ -21,7 +21,6 @@ */ #include "srsran/phy/upper/channel_coding/channel_coding_factories.h" -#include "srsran/srsvec/aligned_vec.h" #include "srsran/support/srsran_test.h" #include #include @@ -165,7 +164,7 @@ int main(int argc, char** argv) TESTASSERT(rate_dematcher); // Create Tx data and fill - srsvec::aligned_vec data_tx(K); + std::vector data_tx(K); for (uint8_t& v : data_tx) { v = dist(rgen); } @@ -174,32 +173,32 @@ int main(int argc, char** argv) code->set(K, E, nMax, bil); // Allocate TX data - srsvec::aligned_vec allocated_tx(code->get_N()); + std::vector allocated_tx(code->get_N()); allocator->allocate(allocated_tx, data_tx, *code); // Encoder TX data - srsvec::aligned_vec encoded_tx(code->get_N()); + std::vector encoded_tx(code->get_N()); encoder->encode(encoded_tx, allocated_tx, code->get_n()); // Rate matching - srsvec::aligned_vec rate_matched_tx(E); + std::vector rate_matched_tx(E); rate_matcher->rate_match(rate_matched_tx, encoded_tx, *code); // Modulate - srsvec::aligned_vec rate_matched_rx(E); + std::vector rate_matched_rx(E); std::transform( rate_matched_tx.begin(), rate_matched_tx.end(), rate_matched_rx.begin(), [](uint8_t b) { return (1 - 2 * b); }); // Undo rate matching - srsvec::aligned_vec encoded_rx(code->get_N()); + std::vector encoded_rx(code->get_N()); rate_dematcher->rate_dematch(encoded_rx, rate_matched_rx, *code); // Decode Rx data - srsvec::aligned_vec allocated_rx(code->get_N()); + std::vector allocated_rx(code->get_N()); decoder->decode(allocated_rx, encoded_rx, *code); // Deallocate RX data - srsvec::aligned_vec data_rx(K); + std::vector data_rx(K); deallocator->deallocate(data_rx, allocated_rx, *code); // Assert decoded message diff --git a/tests/unittests/phy/upper/channel_modulation/modulation_mapper_test.cpp b/tests/unittests/phy/upper/channel_modulation/modulation_mapper_test.cpp index 5a5d94470c..8da0183f21 100644 --- a/tests/unittests/phy/upper/channel_modulation/modulation_mapper_test.cpp +++ b/tests/unittests/phy/upper/channel_modulation/modulation_mapper_test.cpp @@ -22,7 +22,6 @@ #include "modulation_mapper_test_data.h" #include "srsran/phy/upper/channel_modulation/channel_modulation_factories.h" -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/bit.h" #include "fmt/ostream.h" #include @@ -101,13 +100,13 @@ TEST_P(ModulationMapperFixture, ModulationMapperTest) srsvec::bit_pack(packed_data, testvector_data); // Modulate in complex float. - srsran::srsvec::aligned_vec symbols_cf(test_case.nsymbols); + std::vector symbols_cf(test_case.nsymbols); modulator->modulate(symbols_cf, packed_data, test_case.scheme); assert_symbols(symbols_cf, expected_symbols); // Modulate in complex i8. - srsran::srsvec::aligned_vec symbols_ci8(test_case.nsymbols); - float scale_ci8 = modulator->modulate(symbols_ci8, packed_data, test_case.scheme); + std::vector symbols_ci8(test_case.nsymbols); + float scale_ci8 = modulator->modulate(symbols_ci8, packed_data, test_case.scheme); assert_symbols(symbols_ci8, expected_symbols, scale_ci8); } } diff --git a/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_processor_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_processor_vectortest.cpp index 0f6ffdf5cd..195bcc9a02 100644 --- a/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_processor_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_processor_vectortest.cpp @@ -295,7 +295,7 @@ class PdschProcessorFixture : public ::testing::TestWithParam>(*worker_pool); - return create_pdsch_concurrent_processor_factory_sw(crc_calc_factory, + return create_pdsch_concurrent_processor_factory_sw(ldpc_segmenter_tx_factory, ldpc_encoder_factory, ldpc_rate_matcher_factory, prg_factory, diff --git a/tests/unittests/phy/upper/channel_processors/pucch/CMakeLists.txt b/tests/unittests/phy/upper/channel_processors/pucch/CMakeLists.txt index 3ecd06c67e..68da91d331 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch/CMakeLists.txt +++ b/tests/unittests/phy/upper/channel_processors/pucch/CMakeLists.txt @@ -77,6 +77,19 @@ if (USE_PHY_TESTVECTORS) gtest_main) add_test_vector(pucch_demodulator_format3_test pucch_demodulator_format3_test_data.tar.gz "") + add_executable(pucch_demodulator_format4_test pucch_demodulator_format4_test.cpp) + target_link_libraries(pucch_demodulator_format4_test + srsran_channel_equalizer + srsran_channel_processors + srsran_generic_funcs + srsran_phy_support + srsran_transform_precoding + srsran_upper_phy_support + srslog + gtest + gtest_main) + add_test_vector(pucch_demodulator_format4_test pucch_demodulator_format4_test_data.tar.gz "") + add_executable(pucch_detector_test pucch_detector_test.cpp) target_link_libraries(pucch_detector_test srsran_channel_processors srsran_channel_equalizer srslog gtest gtest_main) add_test_vector(pucch_detector_test pucch_detector_test_data.tar.gz "") diff --git a/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format2_test_data.h b/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format2_test_data.h index e049f1f160..b9c9041273 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format2_test_data.h +++ b/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format2_test_data.h @@ -22,7 +22,7 @@ #pragma once -// This file was generated using the following MATLAB class on 23-10-2024 (seed 0): +// This file was generated using the following MATLAB class on 05-11-2024 (seed 0): // + "srsPUCCHDemodulatorFormat2Unittest.m" #include "../../../support/resource_grid_test_doubles.h" diff --git a/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format3_test_data.h b/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format3_test_data.h index 62c0b9f9bd..5185b4c231 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format3_test_data.h +++ b/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format3_test_data.h @@ -22,7 +22,7 @@ #pragma once -// This file was generated using the following MATLAB class on 28-10-2024 (seed 0): +// This file was generated using the following MATLAB class on 05-11-2024 (seed 0): // + "srsPUCCHDemodulatorFormat3Unittest.m" #include "../../../support/resource_grid_test_doubles.h" diff --git a/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format4_test.cpp b/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format4_test.cpp new file mode 100644 index 0000000000..f5859a35cb --- /dev/null +++ b/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format4_test.cpp @@ -0,0 +1,189 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "pucch_demodulator_format4_test_data.h" +#include "srsran/adt/to_array.h" +#include "srsran/phy/support/support_factories.h" +#include "srsran/phy/upper/channel_processors/channel_processor_factories.h" +#include "srsran/phy/upper/channel_processors/pucch/factories.h" +#include "srsran/phy/upper/channel_processors/pucch/pucch_demodulator.h" +#include "srsran/phy/upper/equalization/equalization_factories.h" +#include "srsran/phy/upper/pucch_formats_3_4_helpers.h" +#include "srsran/ran/pucch/pucch_constants.h" +#include "srsran/srsvec/conversion.h" +#include "srsran/support/format/fmt_optional.h" +#include "fmt/ostream.h" +#include + +using namespace srsran; + +using PucchDemodulatorParams = test_case_t; + +namespace srsran { + +// Maximum allowed error. +constexpr log_likelihood_ratio::value_type LLR_MAX_ERROR = 1; + +std::ostream& operator<<(std::ostream& os, test_case_t test_case) +{ + fmt::print(os, + "ports=[{}] first_prb={} second_hop_prb={} start_symbol_index={} nof_symbols={} rnti={} " + "n_id={} additionalDMRS={} pi2_bpsk={} spreading_factor={} occi={}", + span(test_case.context.config.rx_ports), + test_case.context.config.first_prb, + test_case.context.config.second_hop_prb, + test_case.context.config.start_symbol_index, + test_case.context.config.nof_symbols, + test_case.context.config.rnti, + test_case.context.config.n_id, + test_case.context.config.additional_dmrs, + test_case.context.config.pi2_bpsk, + test_case.context.config.occ_length, + test_case.context.config.occ_index); + return os; +} + +std::ostream& operator<<(std::ostream& os, span data) +{ + fmt::print(os, "[{}]", data); + return os; +} + +static bool operator==(span lhs, span rhs) +{ + return std::equal( + lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](log_likelihood_ratio lhs_, log_likelihood_ratio rhs_) { + return log_likelihood_ratio::abs(lhs_ - rhs_) <= LLR_MAX_ERROR; + }); +} + +} // namespace srsran + +class PucchDemodulatorFixture : public ::testing::TestWithParam +{ +protected: + pucch_demodulator::format4_configuration config; + std::vector uci_expected; + channel_estimate channel_est; + resource_grid_reader_spy rg_spy; + + // PUCCH Demodulator. + static std::unique_ptr demodulator; + + PucchDemodulatorFixture() : rg_spy(16, 14, MAX_NOF_PRBS) {} + + static void SetUpTestSuite() + { + if (!demodulator) { + // Create factories required by the PUCCH demodulator factory. + std::shared_ptr equalizer_factory = create_channel_equalizer_generic_factory(); + ASSERT_NE(equalizer_factory, nullptr) << "Cannot create equalizer factory"; + + std::shared_ptr demod_factory = create_channel_modulation_sw_factory(); + ASSERT_NE(demod_factory, nullptr) << "Cannot create channel modulation factory"; + + std::shared_ptr prg_factory = create_pseudo_random_generator_sw_factory(); + ASSERT_NE(prg_factory, nullptr) << "Cannot create pseudo-random generator factory"; + + std::shared_ptr dft_factory = create_dft_processor_factory_fftw_slow(); + ASSERT_NE(dft_factory, nullptr) << "Cannot create DFT processor factory"; + + std::shared_ptr precoding_factory = + create_dft_transform_precoder_factory(dft_factory, pucch_constants::FORMAT4_MAX_NPRB + 1); + ASSERT_NE(precoding_factory, nullptr) << "Cannot create transform precoder factory"; + + // Create PUCCH demodulator factory. + std::shared_ptr pucch_demod_factory = + create_pucch_demodulator_factory_sw(equalizer_factory, demod_factory, prg_factory, precoding_factory); + ASSERT_NE(pucch_demod_factory, nullptr) << "Cannot create PUCCH demodulator factory."; + + // Create PUCCH demodulator. + demodulator = pucch_demod_factory->create(); + ASSERT_NE(demodulator, nullptr) << "Cannot create PUCCH demodulator."; + } + } + + void SetUp() override + { + const test_case_t& test_case = GetParam(); + + // Assert PUCCH demodulator. + ASSERT_NE(demodulator, nullptr) << "Cannot create PUCCH demodulator."; + + // Prepare PUCCH demodulator configuration. + config = test_case.context.config; + + // Prepare expected UCI codeword. + uci_expected = test_case.uci_codeword.read(); + + // Determine resource grid dimensions. + unsigned nof_prb = test_case.context.grid_nof_prb; + unsigned nof_symbols = test_case.context.grid_nof_symbols; + unsigned nof_rx_ports = config.rx_ports.size(); + + // Prepare the resource grid. + symbol_slot_mask dmrs_symb_mask = get_pucch_formats_3_4_dmrs_symbol_mask( + config.nof_symbols, config.second_hop_prb.has_value(), config.additional_dmrs); + unsigned nof_test_symbols = + test_case.context.config.rx_ports.size() * (config.nof_symbols - dmrs_symb_mask.count()) * NRE; + + std::vector grid_entries = test_case.symbols.read(); + ASSERT_EQ(grid_entries.size(), nof_test_symbols) + << "The number of grid entries and the number of PUCCH REs do not match"; + + rg_spy.write(grid_entries); + + // Prepare channel estimates. + channel_estimate::channel_estimate_dimensions ce_dims; + ce_dims.nof_prb = nof_prb; + ce_dims.nof_symbols = nof_symbols; + ce_dims.nof_rx_ports = nof_rx_ports; + ce_dims.nof_tx_layers = 1; + + channel_est.resize(ce_dims); + + // Set estimated channel. + std::vector estimates = test_case.estimates.read(); + srsvec::convert(channel_est.get_path_ch_estimate(0, 0), estimates); + + // Set noise variance. + channel_est.set_noise_variance(test_case.context.noise_var, 0); + } +}; + +std::unique_ptr PucchDemodulatorFixture::demodulator = nullptr; + +TEST_P(PucchDemodulatorFixture, PucchDemodulatorVectorTest) +{ + std::vector uci_data(uci_expected.size()); + + // Perform demodulation. + demodulator->demodulate(uci_data, rg_spy, channel_est, config); + + // Assert UCI codeword matches. + ASSERT_EQ(span(uci_expected), span(uci_data)); +} + +// Creates test suite that combines all possible parameters. +INSTANTIATE_TEST_SUITE_P(PucchDemodulatorVectorTest, + PucchDemodulatorFixture, + ::testing::ValuesIn(pucch_demodulator_format4_test_data)); diff --git a/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format4_test_data.h b/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format4_test_data.h new file mode 100644 index 0000000000..ea95172c16 --- /dev/null +++ b/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format4_test_data.h @@ -0,0 +1,117 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +// This file was generated using the following MATLAB class on 05-11-2024 (seed 0): +// + "srsPUCCHDemodulatorFormat4Unittest.m" + +#include "../../../support/resource_grid_test_doubles.h" +#include "srsran/phy/upper/channel_processors/pucch/pucch_demodulator.h" +#include "srsran/support/file_vector.h" + +namespace srsran { + +struct context_t { + unsigned grid_nof_prb; + unsigned grid_nof_symbols; + float noise_var; + pucch_demodulator::format4_configuration config; +}; + +struct test_case_t { + context_t context; + file_vector symbols; + file_vector estimates; + file_vector uci_codeword; +}; + +static const std::vector pucch_demodulator_format4_test_data = { + // clang-format off + {{266, 14, 0.81, {{0}, 251, {}, 0, 14, 8323, 927, true, false, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols0.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates0.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits0.dat"}}, + {{170, 14, 0.16, {{0}, 149, {}, 0, 14, 17800, 19, true, false, 4, 2}}, {"test_data/pucch_demodulator_format4_test_input_symbols2.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates2.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits2.dat"}}, + {{151, 14, 0.01, {{0}, 132, {}, 0, 14, 6522, 814, true, true, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols4.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates4.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits4.dat"}}, + {{234, 14, 0.81, {{0}, 222, {}, 0, 14, 58321, 723, true, true, 4, 3}}, {"test_data/pucch_demodulator_format4_test_input_symbols6.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates6.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits6.dat"}}, + {{273, 14, 0.09, {{0}, 259, {}, 0, 14, 65108, 82, false, false, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols8.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates8.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits8.dat"}}, + {{181, 14, 0.49, {{0}, 125, {}, 0, 14, 41366, 444, false, false, 4, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols10.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates10.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits10.dat"}}, + {{233, 14, 0.64, {{0}, 69, {}, 0, 14, 64311, 341, false, true, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols12.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates12.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits12.dat"}}, + {{175, 14, 0.25, {{0}, 172, {}, 0, 14, 32009, 961, false, true, 4, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols14.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates14.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits14.dat"}}, + {{263, 14, 0.64, {{0}, 254, 256, 0, 14, 41682, 101, true, false, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols16.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates16.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits16.dat"}}, + {{56, 14, 0.04, {{0}, 38, 51, 0, 14, 63908, 759, true, false, 4, 3}}, {"test_data/pucch_demodulator_format4_test_input_symbols18.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates18.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits18.dat"}}, + {{163, 14, 0.04, {{0}, 105, 123, 0, 14, 34348, 992, true, true, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols20.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates20.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits20.dat"}}, + {{156, 14, 0.25, {{0}, 133, 148, 0, 14, 22469, 270, true, true, 4, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols22.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates22.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits22.dat"}}, + {{175, 14, 0.09, {{0}, 93, 168, 0, 14, 55710, 275, false, false, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols24.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates24.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits24.dat"}}, + {{274, 14, 0.64, {{0}, 204, 240, 0, 14, 57200, 668, false, false, 4, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols26.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates26.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits26.dat"}}, + {{143, 14, 0.16, {{0}, 104, 117, 0, 14, 58967, 519, false, true, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols28.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates28.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits28.dat"}}, + {{173, 14, 0.36, {{0}, 170, 162, 0, 14, 28669, 108, false, true, 4, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols30.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates30.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits30.dat"}}, + {{243, 14, 0.36, {{0}, 88, {}, 1, 13, 30084, 983, true, false, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols32.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates32.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits32.dat"}}, + {{272, 14, 0.01, {{0}, 260, {}, 1, 13, 18233, 614, true, false, 4, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols34.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates34.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits34.dat"}}, + {{23, 14, 0.04, {{0}, 12, {}, 1, 13, 11524, 987, true, true, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols36.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates36.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits36.dat"}}, + {{197, 14, 0.16, {{0}, 155, {}, 1, 13, 54686, 956, true, true, 4, 3}}, {"test_data/pucch_demodulator_format4_test_input_symbols38.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates38.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits38.dat"}}, + {{244, 14, 0.04, {{0}, 242, {}, 1, 13, 39561, 352, false, false, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols40.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates40.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits40.dat"}}, + {{143, 14, 0.16, {{0}, 78, {}, 1, 13, 32422, 648, false, false, 4, 3}}, {"test_data/pucch_demodulator_format4_test_input_symbols42.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates42.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits42.dat"}}, + {{271, 14, 0.49, {{0}, 269, {}, 1, 13, 9465, 594, false, true, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols44.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates44.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits44.dat"}}, + {{242, 14, 0.25, {{0}, 197, {}, 1, 13, 21870, 929, false, true, 4, 3}}, {"test_data/pucch_demodulator_format4_test_input_symbols46.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates46.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits46.dat"}}, + {{234, 14, 0.16, {{0}, 160, 214, 1, 13, 2515, 22, true, false, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols48.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates48.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits48.dat"}}, + {{228, 14, 0.81, {{0}, 208, 216, 1, 13, 53298, 545, true, false, 4, 2}}, {"test_data/pucch_demodulator_format4_test_input_symbols50.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates50.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits50.dat"}}, + {{145, 14, 0.04, {{0}, 141, 140, 1, 13, 45241, 1016, true, true, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols52.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates52.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits52.dat"}}, + {{268, 14, 0.09, {{0}, 263, 267, 1, 13, 47309, 662, true, true, 4, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols54.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates54.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits54.dat"}}, + {{182, 14, 1, {{0}, 166, 131, 1, 13, 36950, 335, false, false, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols56.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates56.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits56.dat"}}, + {{224, 14, 0.09, {{0}, 123, 60, 1, 13, 19208, 713, false, false, 4, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols58.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates58.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits58.dat"}}, + {{158, 14, 1, {{0}, 101, 95, 1, 13, 6522, 71, false, true, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols60.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates60.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits60.dat"}}, + {{213, 14, 0.81, {{0}, 196, 208, 1, 13, 33084, 318, false, true, 4, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols62.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates62.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits62.dat"}}, + {{133, 14, 0.25, {{0}, 128, {}, 5, 5, 36679, 771, true, false, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols64.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates64.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits64.dat"}}, + {{202, 14, 0.49, {{0}, 199, {}, 5, 5, 40171, 77, true, false, 4, 3}}, {"test_data/pucch_demodulator_format4_test_input_symbols66.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates66.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits66.dat"}}, + {{249, 14, 0.81, {{0}, 244, {}, 5, 5, 36556, 305, true, true, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols68.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates68.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits68.dat"}}, + {{213, 14, 0.81, {{0}, 133, {}, 5, 5, 15363, 373, true, true, 4, 2}}, {"test_data/pucch_demodulator_format4_test_input_symbols70.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates70.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits70.dat"}}, + {{233, 14, 0.09, {{0}, 163, {}, 5, 5, 64895, 711, false, false, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols72.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates72.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits72.dat"}}, + {{234, 14, 1, {{0}, 199, {}, 5, 5, 22665, 919, false, false, 4, 3}}, {"test_data/pucch_demodulator_format4_test_input_symbols74.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates74.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits74.dat"}}, + {{256, 14, 0.16, {{0}, 201, {}, 5, 5, 47693, 996, false, true, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols76.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates76.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits76.dat"}}, + {{91, 14, 1, {{0}, 37, {}, 5, 5, 56802, 799, false, true, 4, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols78.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates78.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits78.dat"}}, + {{130, 14, 0.04, {{0}, 71, 82, 5, 5, 54938, 399, true, false, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols80.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates80.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits80.dat"}}, + {{252, 14, 0.16, {{0}, 200, 209, 5, 5, 20453, 722, true, false, 4, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols82.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates82.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits82.dat"}}, + {{153, 14, 0.49, {{0}, 121, 118, 5, 5, 1198, 389, true, true, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols84.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates84.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits84.dat"}}, + {{251, 14, 0.49, {{0}, 218, 114, 5, 5, 46355, 329, true, true, 4, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols86.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates86.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits86.dat"}}, + {{154, 14, 0.64, {{0}, 153, 148, 5, 5, 7574, 990, false, false, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols88.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates88.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits88.dat"}}, + {{207, 14, 0.04, {{0}, 206, 202, 5, 5, 259, 82, false, false, 4, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols90.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates90.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits90.dat"}}, + {{202, 14, 0.36, {{0}, 170, 183, 5, 5, 22927, 430, false, true, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols92.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates92.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits92.dat"}}, + {{131, 14, 0.36, {{0}, 120, 130, 5, 5, 62760, 373, false, true, 4, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols94.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates94.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits94.dat"}}, + {{64, 14, 0.04, {{0}, 49, {}, 10, 4, 1421, 152, true, false, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols96.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates96.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits96.dat"}}, + {{168, 14, 0.36, {{0}, 138, {}, 10, 4, 34828, 303, true, false, 4, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols98.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates98.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits98.dat"}}, + {{267, 14, 0.09, {{0}, 240, {}, 10, 4, 33192, 647, true, true, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols100.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates100.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits100.dat"}}, + {{274, 14, 0.09, {{0}, 240, {}, 10, 4, 37965, 730, true, true, 4, 3}}, {"test_data/pucch_demodulator_format4_test_input_symbols102.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates102.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits102.dat"}}, + {{237, 14, 0.36, {{0}, 231, {}, 10, 4, 62814, 1015, false, false, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols104.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates104.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits104.dat"}}, + {{126, 14, 0.16, {{0}, 99, {}, 10, 4, 19802, 285, false, false, 4, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols106.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates106.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits106.dat"}}, + {{239, 14, 0.25, {{0}, 237, {}, 10, 4, 10629, 895, false, true, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols108.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates108.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits108.dat"}}, + {{187, 14, 0.49, {{0}, 123, {}, 10, 4, 34631, 791, false, true, 4, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols110.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates110.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits110.dat"}}, + {{198, 14, 0.04, {{0}, 160, 170, 10, 4, 57520, 488, true, false, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols112.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates112.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits112.dat"}}, + {{205, 14, 0.81, {{0}, 149, 169, 10, 4, 12721, 295, true, false, 4, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols114.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates114.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits114.dat"}}, + {{171, 14, 0.64, {{0}, 123, 135, 10, 4, 22780, 668, true, true, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols116.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates116.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits116.dat"}}, + {{131, 14, 0.81, {{0}, 126, 128, 10, 4, 51378, 645, true, true, 4, 3}}, {"test_data/pucch_demodulator_format4_test_input_symbols118.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates118.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits118.dat"}}, + {{43, 14, 0.36, {{0}, 21, 26, 10, 4, 22335, 1012, false, false, 2, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols120.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates120.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits120.dat"}}, + {{243, 14, 0.01, {{0}, 242, 237, 10, 4, 48633, 17, false, false, 4, 1}}, {"test_data/pucch_demodulator_format4_test_input_symbols122.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates122.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits122.dat"}}, + {{246, 14, 0.25, {{0}, 208, 154, 10, 4, 60173, 295, false, true, 2, 0}}, {"test_data/pucch_demodulator_format4_test_input_symbols124.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates124.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits124.dat"}}, + {{213, 14, 0.04, {{0}, 131, 111, 10, 4, 58214, 792, false, true, 4, 3}}, {"test_data/pucch_demodulator_format4_test_input_symbols126.dat"}, {"test_data/pucch_demodulator_format4_test_input_estimates126.dat"}, {"test_data/pucch_demodulator_format4_test_output_sch_soft_bits126.dat"}}, + // clang-format on +}; + +} // namespace srsran diff --git a/tests/unittests/phy/upper/sequence_generators/pseudo_random_generator_test.cpp b/tests/unittests/phy/upper/sequence_generators/pseudo_random_generator_test.cpp index 7da929d222..3cf5f007bc 100644 --- a/tests/unittests/phy/upper/sequence_generators/pseudo_random_generator_test.cpp +++ b/tests/unittests/phy/upper/sequence_generators/pseudo_random_generator_test.cpp @@ -21,7 +21,6 @@ */ #include "srsran/phy/upper/sequence_generators/sequence_generator_factories.h" -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/bit.h" #include #include @@ -162,7 +161,7 @@ TEST_P(PseudoRandomGeneratorFixture, PseudoRandomGeneratorXorUnpacked) std::uniform_int_distribution distXorUnpacked(0, 1); // Create data buffer. - srsvec::aligned_vec data(size); + std::vector data(size); // Fill buffer with random data. for (unsigned char& v : data) { @@ -176,7 +175,7 @@ TEST_P(PseudoRandomGeneratorFixture, PseudoRandomGeneratorXorUnpacked) generator->advance(offset); // Apply sequence. - srsvec::aligned_vec data_xor(size); + std::vector data_xor(size); generator->apply_xor(data_xor, data); // Assert. @@ -192,7 +191,7 @@ TEST_P(PseudoRandomGeneratorFixture, PseudoRandomGeneratorXori8) log_likelihood_ratio::max().to_value_type()); // Create data buffer. - srsvec::aligned_vec data(size); + std::vector data(size); // Fill buffer with random data. for (log_likelihood_ratio& v : data) { @@ -206,7 +205,7 @@ TEST_P(PseudoRandomGeneratorFixture, PseudoRandomGeneratorXori8) generator->advance(offset); // Apply sequence. - srsvec::aligned_vec data_xor(size); + std::vector data_xor(size); generator->apply_xor(data_xor, data); // Assert. @@ -246,7 +245,7 @@ TEST_P(PseudoRandomGeneratorFixture, PseudoRandomGeneratorBit) TEST_P(PseudoRandomGeneratorFixture, PseudoRandomGeneratorFloat) { // Create data buffer. - srsvec::aligned_vec sequence(size); + std::vector sequence(size); // Initialize sequence generator. generator->init(c_init); diff --git a/tests/unittests/phy/upper/signal_processors/srs/srs_estimator_test_data.h b/tests/unittests/phy/upper/signal_processors/srs/srs_estimator_test_data.h index 7f209c04b7..1c75bc4143 100644 --- a/tests/unittests/phy/upper/signal_processors/srs/srs_estimator_test_data.h +++ b/tests/unittests/phy/upper/signal_processors/srs/srs_estimator_test_data.h @@ -22,7 +22,7 @@ #pragma once -// This file was generated using the following MATLAB class on 12-04-2024 (seed 0): +// This file was generated using the following MATLAB class on 07-11-2024 (seed 0): // + "srsSRSEstimatorUnittest.m" #include "srsran/phy/upper/signal_processors/srs/srs_estimator_configuration.h" @@ -43,78 +43,78 @@ struct test_case_t { static const std::vector srs_estimator_test_data = { // clang-format off - {{{{0, 130, 8, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 12, 17, 647, 2, srs_resource_configuration::comb_size_enum(2), 1, 1, 66, 1, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.964003, -0.265891), cf_t(-0.995781, 0.091758)}}, 1, 2}, 0, {0.000000313}}}, {"test_data/srs_estimator_test_input0.dat"}}, - {{{{0, 937, 1, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 11, 2, 982, 3, srs_resource_configuration::comb_size_enum(4), 3, 9, 50, 7, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.779357, 0.626580), cf_t(-0.559597, -0.828765)}}, 1, 2}, 0, {-0.000000343}}}, {"test_data/srs_estimator_test_input1.dat"}}, - {{{{0, 283, 7, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 0, 44, 99, 1, srs_resource_configuration::comb_size_enum(2), 1, 5, 25, 9, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.097340, -0.995251), cf_t(0.386322, 0.922364), cf_t(0.280197, -0.959943), cf_t(-0.997933, 0.064268)}}, 2, 2}, 0, {-0.000000057}}}, {"test_data/srs_estimator_test_input2.dat"}}, - {{{{0, 772, 6, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 3, 10, 696, 0, srs_resource_configuration::comb_size_enum(4), 1, 4, 39, 7, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.163803, 0.986493), cf_t(-0.032008, 0.999488), cf_t(0.007961, -0.999968), cf_t(-0.999300, -0.037421)}}, 2, 2}, 0, {0.000000207}}}, {"test_data/srs_estimator_test_input3.dat"}}, - {{{{0, 560, 8, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 1, 58, 152, 1, srs_resource_configuration::comb_size_enum(2), 1, 4, 38, 6, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.888557, 0.458767), cf_t(-0.981336, -0.192301), cf_t(0.915268, -0.402845), cf_t(-0.907949, -0.419080), cf_t(0.943095, 0.332524), cf_t(0.182239, -0.983254), cf_t(0.684977, 0.728565), cf_t(-0.981563, 0.191141)}}, 4, 2}, 0, {-0.000000508}}}, {"test_data/srs_estimator_test_input4.dat"}}, - {{{{0, 813, 3, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 4, 38, 541, 1, srs_resource_configuration::comb_size_enum(4), 2, 8, 30, 1, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.864488, 0.502653), cf_t(0.855377, -0.518005), cf_t(0.458558, -0.888664), cf_t(0.999705, -0.024284), cf_t(0.131708, 0.991289), cf_t(0.575632, 0.817709), cf_t(-0.971121, -0.238589), cf_t(0.881772, 0.471677)}}, 4, 2}, 0, {-0.000000060}}}, {"test_data/srs_estimator_test_input5.dat"}}, - {{{{0, 4, 1, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 10, 5, 836, 1, srs_resource_configuration::comb_size_enum(2), 0, 5, 61, 9, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.415250, 0.909707), cf_t(-0.086618, 0.996242)}}, 1, 2}, 0, {-0.000000369}}}, {"test_data/srs_estimator_test_input6.dat"}}, - {{{{0, 593, 1, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 7, 39, 148, 1, srs_resource_configuration::comb_size_enum(4), 2, 0, 16, 9, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.714536, 0.699599), cf_t(0.403437, 0.915008)}}, 1, 2}, 0, {-0.000000271}}}, {"test_data/srs_estimator_test_input7.dat"}}, - {{{{0, 924, 4, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 12, 15, 502, 1, srs_resource_configuration::comb_size_enum(2), 1, 0, 11, 0, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.592272, -0.805738), cf_t(-0.599184, -0.800612), cf_t(-0.114589, -0.993413), cf_t(-0.952834, 0.303492)}}, 2, 2}, 0, {0.000000049}}}, {"test_data/srs_estimator_test_input8.dat"}}, - {{{{0, 193, 2, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 8, 40, 187, 3, srs_resource_configuration::comb_size_enum(4), 0, 9, 33, 4, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.919884, 0.392191), cf_t(-0.346703, 0.937975), cf_t(-0.944618, 0.328172), cf_t(-0.998571, -0.053436)}}, 2, 2}, 0, {0.000000011}}}, {"test_data/srs_estimator_test_input9.dat"}}, - {{{{0, 659, 8, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 4, 22, 831, 3, srs_resource_configuration::comb_size_enum(2), 1, 7, 39, 5, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.262404, 0.964958), cf_t(-0.983358, 0.181679), cf_t(0.558486, -0.829514), cf_t(0.150711, 0.988578), cf_t(-0.316455, 0.948607), cf_t(0.122290, 0.992494), cf_t(0.340131, 0.940378), cf_t(0.477850, 0.878441)}}, 4, 2}, 0, {-0.000000284}}}, {"test_data/srs_estimator_test_input10.dat"}}, - {{{{0, 945, 4, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 5, 62, 189, 1, srs_resource_configuration::comb_size_enum(4), 0, 4, 40, 9, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.076653, 0.997058), cf_t(-0.241284, -0.970455), cf_t(0.739979, 0.672630), cf_t(-0.418821, 0.908069), cf_t(-0.798388, -0.602143), cf_t(0.176590, 0.984285), cf_t(-0.289087, 0.957303), cf_t(-0.888618, 0.458649)}}, 4, 2}, 0, {0.000000008}}}, {"test_data/srs_estimator_test_input11.dat"}}, - {{{{0, 820, 0, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 0, 14, 951, 1, srs_resource_configuration::comb_size_enum(2), 1, 4, 24, 5, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.997150, -0.075440), cf_t(0.972018, 0.234904)}}, 1, 2}, 0, {0.000000401}}}, {"test_data/srs_estimator_test_input12.dat"}}, - {{{{0, 101, 9, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 2, 8, 343, 2, srs_resource_configuration::comb_size_enum(4), 0, 5, 52, 7, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.217916, -0.975968), cf_t(0.822535, -0.568714)}}, 1, 2}, 0, {0.000000407}}}, {"test_data/srs_estimator_test_input13.dat"}}, - {{{{0, 202, 3, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 0, 11, 761, 0, srs_resource_configuration::comb_size_enum(2), 1, 5, 11, 6, 0, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.991042, -0.133553), cf_t(-0.999996, -0.002963), cf_t(-0.232257, -0.972654), cf_t(-0.983546, 0.180660)}}, 2, 2}, 0, {-0.000000459}}}, {"test_data/srs_estimator_test_input14.dat"}}, - {{{{0, 73, 6, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 5, 52, 99, 2, srs_resource_configuration::comb_size_enum(4), 0, 6, 66, 8, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.592900, -0.805276), cf_t(-0.958159, 0.286237), cf_t(0.310992, -0.950413), cf_t(-0.911123, 0.412136)}}, 2, 2}, 0, {0.000000339}}}, {"test_data/srs_estimator_test_input15.dat"}}, - {{{{0, 177, 0, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 4, 3, 851, 1, srs_resource_configuration::comb_size_enum(2), 1, 7, 42, 8, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.260745, 0.965408), cf_t(0.995269, 0.097155), cf_t(0.497267, 0.867597), cf_t(-0.695505, 0.718521), cf_t(-0.909196, 0.416369), cf_t(0.994991, -0.099963), cf_t(0.785448, 0.618928), cf_t(0.320239, 0.947337)}}, 4, 2}, 0, {-0.000000011}}}, {"test_data/srs_estimator_test_input16.dat"}}, - {{{{0, 942, 3, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 0, 27, 755, 2, srs_resource_configuration::comb_size_enum(4), 3, 11, 20, 2, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.302444, -0.953167), cf_t(-0.969934, -0.243370), cf_t(-0.500755, -0.865589), cf_t(0.693588, 0.720372), cf_t(-0.501783, -0.864994), cf_t(-0.320316, -0.947311), cf_t(0.436367, 0.899769), cf_t(0.999983, -0.005778)}}, 4, 2}, 0, {-0.000000343}}}, {"test_data/srs_estimator_test_input17.dat"}}, - {{{{0, 903, 0, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 9, 29, 195, 3, srs_resource_configuration::comb_size_enum(2), 0, 7, 25, 4, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.362722, 0.931897), cf_t(-0.900099, 0.435686), cf_t(-0.993627, 0.112719), cf_t(0.726333, 0.687343)}}, 1, 4}, 0, {0.000000093}}}, {"test_data/srs_estimator_test_input18.dat"}}, - {{{{0, 596, 2, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 3, 16, 297, 3, srs_resource_configuration::comb_size_enum(4), 3, 4, 39, 6, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.779372, 0.626561), cf_t(0.831673, -0.555266), cf_t(0.727478, -0.686132), cf_t(0.413006, -0.910728)}}, 1, 4}, 0, {-0.000000249}}}, {"test_data/srs_estimator_test_input19.dat"}}, - {{{{0, 435, 5, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 4, 2, 165, 0, srs_resource_configuration::comb_size_enum(2), 0, 7, 27, 7, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.425673, -0.904877), cf_t(-0.454806, 0.890591), cf_t(-0.197500, -0.980303), cf_t(0.787726, 0.616026), cf_t(0.980662, -0.195711), cf_t(-0.766660, -0.642054), cf_t(-0.980682, -0.195607), cf_t(0.179984, -0.983670)}}, 2, 4}, 0, {-0.000000080}}}, {"test_data/srs_estimator_test_input20.dat"}}, - {{{{0, 157, 0, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 3, 29, 450, 3, srs_resource_configuration::comb_size_enum(4), 2, 7, 65, 5, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.058356, 0.998296), cf_t(-0.337907, -0.941179), cf_t(-0.447696, -0.894186), cf_t(0.910125, 0.414334), cf_t(-0.242993, 0.970028), cf_t(-0.030093, 0.999547), cf_t(-0.471767, -0.881723), cf_t(0.162389, 0.986727)}}, 2, 4}, 0, {0.000000175}}}, {"test_data/srs_estimator_test_input21.dat"}}, - {{{{0, 799, 8, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 9, 24, 6, 3, srs_resource_configuration::comb_size_enum(2), 0, 5, 31, 6, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.126329, -0.991988), cf_t(0.974860, 0.222818), cf_t(0.573868, 0.818948), cf_t(-0.072652, -0.997357), cf_t(-0.439780, 0.898105), cf_t(0.449088, 0.893487), cf_t(-0.541779, 0.840521), cf_t(0.044912, 0.998991), cf_t(0.216544, -0.976273), cf_t(-0.176520, -0.984297), cf_t(-0.780865, -0.624699), cf_t(0.868396, -0.495872), cf_t(-0.983849, 0.178998), cf_t(-0.986156, 0.165823), cf_t(0.357907, 0.933757), cf_t(-0.119481, 0.992836)}}, 4, 4}, 0, {0.000000277}}}, {"test_data/srs_estimator_test_input22.dat"}}, - {{{{0, 93, 1, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 8, 27, 699, 2, srs_resource_configuration::comb_size_enum(4), 2, 7, 64, 6, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.255166, 0.966897), cf_t(-0.781200, -0.624281), cf_t(0.127113, -0.991888), cf_t(0.546021, -0.837771), cf_t(-0.253059, -0.967451), cf_t(-0.951324, 0.308194), cf_t(-0.588893, 0.808211), cf_t(0.497732, -0.867331), cf_t(0.086408, 0.996260), cf_t(-0.966561, 0.256438), cf_t(-0.525123, -0.851026), cf_t(-0.040459, 0.999181), cf_t(0.731560, 0.681777), cf_t(-0.525470, -0.850812), cf_t(-0.864425, 0.502762), cf_t(-0.756472, -0.654026)}}, 4, 4}, 0, {0.000000086}}}, {"test_data/srs_estimator_test_input23.dat"}}, - {{{{0, 271, 5, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 4, 41, 122, 1, srs_resource_configuration::comb_size_enum(2), 1, 7, 36, 10, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.180918, -0.983498), cf_t(-0.990028, -0.140872), cf_t(0.999218, -0.039545), cf_t(0.195542, 0.980695)}}, 1, 4}, 0, {-0.000000411}}}, {"test_data/srs_estimator_test_input24.dat"}}, - {{{{0, 414, 1, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 5, 40, 374, 3, srs_resource_configuration::comb_size_enum(4), 3, 2, 9, 8, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.331241, -0.943546), cf_t(0.831225, 0.555936), cf_t(-0.987288, -0.158944), cf_t(-0.981880, -0.189505)}}, 1, 4}, 0, {0.000000376}}}, {"test_data/srs_estimator_test_input25.dat"}}, - {{{{0, 687, 4, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 9, 1, 532, 1, srs_resource_configuration::comb_size_enum(2), 0, 2, 55, 4, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.904617, 0.426226), cf_t(-0.797004, 0.603974), cf_t(0.761511, -0.648153), cf_t(0.359428, -0.933173), cf_t(-0.775230, 0.631679), cf_t(0.031895, -0.999491), cf_t(0.119811, -0.992797), cf_t(-0.717669, 0.696384)}}, 2, 4}, 0, {-0.000000296}}}, {"test_data/srs_estimator_test_input26.dat"}}, - {{{{0, 335, 7, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 8, 49, 449, 0, srs_resource_configuration::comb_size_enum(4), 3, 6, 60, 9, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.850908, -0.525314), cf_t(-0.008132, -0.999967), cf_t(0.563370, 0.826205), cf_t(0.457256, -0.889335), cf_t(0.309837, 0.950790), cf_t(0.248465, -0.968641), cf_t(-0.833922, 0.551883), cf_t(-0.417371, 0.908736)}}, 2, 4}, 0, {0.000000035}}}, {"test_data/srs_estimator_test_input27.dat"}}, - {{{{0, 139, 0, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 8, 31, 507, 0, srs_resource_configuration::comb_size_enum(2), 0, 6, 63, 2, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.328863, -0.944377), cf_t(0.997574, -0.069614), cf_t(0.998007, -0.063103), cf_t(0.138768, 0.990325), cf_t(-0.867724, -0.497046), cf_t(0.999995, 0.003282), cf_t(-0.984914, -0.173044), cf_t(-0.999928, 0.011974), cf_t(0.399437, -0.916761), cf_t(0.663376, -0.748286), cf_t(-0.991735, 0.128304), cf_t(0.812154, -0.583443), cf_t(0.724713, -0.689050), cf_t(-0.760135, -0.649766), cf_t(0.317059, -0.948406), cf_t(-0.891971, -0.452093)}}, 4, 4}, 0, {0.000000360}}}, {"test_data/srs_estimator_test_input28.dat"}}, - {{{{0, 252, 7, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 8, 42, 85, 2, srs_resource_configuration::comb_size_enum(4), 3, 9, 39, 6, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.900263, -0.435346), cf_t(0.650454, -0.759545), cf_t(-0.946509, -0.322677), cf_t(-0.649025, 0.760767), cf_t(-0.876033, -0.482251), cf_t(-0.995136, 0.098508), cf_t(-0.685081, -0.728467), cf_t(0.951960, 0.306223), cf_t(0.994312, 0.106505), cf_t(0.561333, -0.827590), cf_t(0.979866, 0.199655), cf_t(-0.997853, 0.065487), cf_t(0.725261, 0.688474), cf_t(0.252309, 0.967647), cf_t(-0.751301, -0.659960), cf_t(0.353414, 0.935467)}}, 4, 4}, 0, {-0.000000393}}}, {"test_data/srs_estimator_test_input29.dat"}}, - {{{{0, 193, 2, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 0, 34, 650, 2, srs_resource_configuration::comb_size_enum(2), 0, 5, 8, 3, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.998165, 0.060550), cf_t(0.602920, -0.797801), cf_t(0.702325, -0.711856), cf_t(-0.127168, 0.991881)}}, 1, 4}, 0, {-0.000000304}}}, {"test_data/srs_estimator_test_input30.dat"}}, - {{{{0, 427, 5, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 2, 6, 970, 0, srs_resource_configuration::comb_size_enum(4), 0, 6, 3, 0, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.908015, -0.418937), cf_t(-0.133671, -0.991026), cf_t(-0.076319, -0.997083), cf_t(0.921690, 0.387928)}}, 1, 4}, 0, {0.000000375}}}, {"test_data/srs_estimator_test_input31.dat"}}, - {{{{0, 879, 9, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 8, 25, 525, 0, srs_resource_configuration::comb_size_enum(2), 0, 3, 20, 1, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.497838, 0.867270), cf_t(0.547481, -0.836818), cf_t(-0.978669, 0.205443), cf_t(-0.931997, -0.362466), cf_t(-0.596905, -0.802312), cf_t(0.608429, -0.793608), cf_t(0.987463, 0.157850), cf_t(-0.576953, 0.816777)}}, 2, 4}, 0, {-0.000000056}}}, {"test_data/srs_estimator_test_input32.dat"}}, - {{{{0, 678, 0, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 3, 63, 920, 2, srs_resource_configuration::comb_size_enum(4), 2, 3, 28, 1, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.975697, 0.219125), cf_t(0.436455, 0.899726), cf_t(0.087583, -0.996157), cf_t(-0.635655, 0.771973), cf_t(0.415542, -0.909574), cf_t(0.937199, 0.348796), cf_t(0.808198, 0.588911), cf_t(-0.990560, -0.137079)}}, 2, 4}, 0, {-0.000000171}}}, {"test_data/srs_estimator_test_input33.dat"}}, - {{{{0, 926, 1, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 7, 6, 479, 2, srs_resource_configuration::comb_size_enum(2), 1, 2, 40, 10, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.308640, 0.951179), cf_t(0.900903, 0.434021), cf_t(0.996519, 0.083364), cf_t(-0.352692, 0.935739), cf_t(0.665435, 0.746455), cf_t(0.047191, 0.998886), cf_t(0.798519, -0.601970), cf_t(-0.962127, 0.272603), cf_t(0.232825, 0.972519), cf_t(0.943503, 0.331364), cf_t(0.328917, 0.944359), cf_t(0.802807, 0.596239), cf_t(0.789930, -0.613197), cf_t(-0.933705, 0.358043), cf_t(0.832792, 0.553586), cf_t(0.999580, -0.028963)}}, 4, 4}, 0, {-0.000000175}}}, {"test_data/srs_estimator_test_input34.dat"}}, - {{{{0, 305, 2, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 0, 40, 517, 0, srs_resource_configuration::comb_size_enum(4), 0, 10, 36, 8, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.773890, 0.633320), cf_t(-0.023161, -0.999732), cf_t(-0.796018, -0.605273), cf_t(0.195873, -0.980629), cf_t(0.458513, -0.888688), cf_t(0.997892, 0.064901), cf_t(-0.986581, -0.163272), cf_t(-0.236358, 0.971666), cf_t(-0.525697, 0.850672), cf_t(0.954026, 0.299724), cf_t(-0.127144, -0.991884), cf_t(-0.353287, -0.935515), cf_t(-0.272789, 0.962074), cf_t(-0.493186, -0.869924), cf_t(-0.265367, -0.964148), cf_t(-0.937275, -0.348591)}}, 4, 4}, 0, {-0.000000108}}}, {"test_data/srs_estimator_test_input35.dat"}}, - {{{{1, 345, 0, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 8, 7, 759, 0, srs_resource_configuration::comb_size_enum(2), 1, 7, 50, 7, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.866489, -0.499197), cf_t(-0.062588, -0.998039)}}, 1, 2}, 0, {-0.000000138}}}, {"test_data/srs_estimator_test_input36.dat"}}, - {{{{1, 887, 7, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 1, 43, 375, 2, srs_resource_configuration::comb_size_enum(4), 3, 2, 5, 4, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.137379, -0.990519), cf_t(0.274919, 0.961467)}}, 1, 2}, 0, {-0.000000058}}}, {"test_data/srs_estimator_test_input37.dat"}}, - {{{{1, 657, 5, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 6, 6, 155, 1, srs_resource_configuration::comb_size_enum(2), 0, 1, 27, 8, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.790747, 0.612143), cf_t(0.214634, -0.976695), cf_t(0.761287, 0.648415), cf_t(-0.258234, 0.966082)}}, 2, 2}, 0, {0.000000054}}}, {"test_data/srs_estimator_test_input38.dat"}}, - {{{{1, 711, 9, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 10, 7, 443, 3, srs_resource_configuration::comb_size_enum(4), 0, 9, 33, 7, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.118837, -0.992914), cf_t(-0.143630, 0.989631), cf_t(-0.794016, 0.607897), cf_t(0.972758, 0.231824)}}, 2, 2}, 0, {0.000000090}}}, {"test_data/srs_estimator_test_input39.dat"}}, - {{{{1, 624, 4, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 0, 44, 323, 0, srs_resource_configuration::comb_size_enum(2), 0, 0, 28, 8, 0, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.559101, -0.829099), cf_t(-0.980835, -0.194839), cf_t(-0.676415, -0.736521), cf_t(0.664589, 0.747209), cf_t(-0.169313, -0.985562), cf_t(0.775226, 0.631684), cf_t(0.700412, 0.713739), cf_t(0.814178, 0.580616)}}, 4, 2}, 0, {-0.000000186}}}, {"test_data/srs_estimator_test_input40.dat"}}, - {{{{1, 325, 1, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 4, 57, 222, 2, srs_resource_configuration::comb_size_enum(4), 2, 2, 5, 2, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.856881, -0.515515), cf_t(-0.934801, -0.355173), cf_t(0.502518, 0.864567), cf_t(0.997128, -0.075736), cf_t(-0.268626, -0.963245), cf_t(-0.388070, 0.921630), cf_t(-0.718138, -0.695900), cf_t(0.479373, 0.877611)}}, 4, 2}, 0, {-0.000000126}}}, {"test_data/srs_estimator_test_input41.dat"}}, - {{{{1, 700, 3, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 5, 43, 1006, 1, srs_resource_configuration::comb_size_enum(2), 1, 7, 22, 3, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.304382, 0.952550), cf_t(-0.955964, 0.293485)}}, 1, 2}, 0, {-0.000000040}}}, {"test_data/srs_estimator_test_input42.dat"}}, - {{{{1, 760, 3, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 5, 1, 439, 1, srs_resource_configuration::comb_size_enum(4), 1, 11, 63, 1, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.965195, 0.261531), cf_t(0.059790, 0.998211)}}, 1, 2}, 0, {0.000000137}}}, {"test_data/srs_estimator_test_input43.dat"}}, - {{{{1, 761, 7, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 1, 13, 697, 0, srs_resource_configuration::comb_size_enum(2), 1, 1, 45, 5, 0, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.787798, -0.615934), cf_t(-0.292825, -0.956166), cf_t(-0.994593, -0.103851), cf_t(0.569387, 0.822070)}}, 2, 2}, 0, {0.000000236}}}, {"test_data/srs_estimator_test_input44.dat"}}, - {{{{1, 37, 5, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 10, 33, 766, 1, srs_resource_configuration::comb_size_enum(4), 2, 4, 12, 1, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.033839, 0.999427), cf_t(0.887198, -0.461389), cf_t(0.991687, 0.128672), cf_t(-0.568821, -0.822461)}}, 2, 2}, 0, {0.000000225}}}, {"test_data/srs_estimator_test_input45.dat"}}, - {{{{1, 813, 1, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 7, 32, 450, 2, srs_resource_configuration::comb_size_enum(2), 1, 0, 58, 9, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.679083, -0.734061), cf_t(0.999823, -0.018828), cf_t(-0.575257, -0.817973), cf_t(-0.759366, 0.650663), cf_t(-0.613273, 0.789871), cf_t(0.161574, 0.986861), cf_t(-0.790191, -0.612861), cf_t(0.626775, 0.779200)}}, 4, 2}, 0, {-0.000000247}}}, {"test_data/srs_estimator_test_input46.dat"}}, - {{{{1, 743, 4, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 4, 36, 861, 0, srs_resource_configuration::comb_size_enum(4), 3, 11, 15, 8, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.700697, 0.713459), cf_t(-0.636860, -0.770980), cf_t(0.960204, 0.279299), cf_t(-0.574685, 0.818375), cf_t(0.852639, 0.522500), cf_t(0.422269, 0.906471), cf_t(-0.167759, -0.985828), cf_t(-0.532550, -0.846398)}}, 4, 2}, 0, {-0.000000060}}}, {"test_data/srs_estimator_test_input47.dat"}}, - {{{{1, 932, 6, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 8, 24, 763, 2, srs_resource_configuration::comb_size_enum(2), 1, 3, 16, 8, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.954188, 0.299208), cf_t(0.139577, 0.990211)}}, 1, 2}, 0, {0.000000159}}}, {"test_data/srs_estimator_test_input48.dat"}}, - {{{{1, 548, 9, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 0, 4, 821, 3, srs_resource_configuration::comb_size_enum(4), 0, 9, 36, 10, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.751603, -0.659616), cf_t(0.805326, -0.592832)}}, 1, 2}, 0, {0.000000066}}}, {"test_data/srs_estimator_test_input49.dat"}}, - {{{{1, 186, 1, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 0, 49, 109, 1, srs_resource_configuration::comb_size_enum(2), 1, 3, 46, 10, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.984732, -0.174075), cf_t(-0.799163, -0.601114), cf_t(-0.849650, 0.527346), cf_t(0.003268, -0.999995)}}, 2, 2}, 0, {0.000000044}}}, {"test_data/srs_estimator_test_input50.dat"}}, - {{{{1, 524, 5, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 0, 22, 736, 3, srs_resource_configuration::comb_size_enum(4), 1, 5, 28, 10, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.201359, 0.979517), cf_t(-0.361775, 0.932265), cf_t(0.704193, 0.710009), cf_t(-0.149577, -0.988750)}}, 2, 2}, 0, {0.000000147}}}, {"test_data/srs_estimator_test_input51.dat"}}, - {{{{1, 863, 6, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 10, 24, 789, 2, srs_resource_configuration::comb_size_enum(2), 1, 3, 45, 0, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.990017, 0.140950), cf_t(0.085066, 0.996375), cf_t(0.479789, -0.877384), cf_t(0.916448, -0.400154), cf_t(-0.712787, -0.701381), cf_t(0.442061, 0.896985), cf_t(0.106122, -0.994353), cf_t(0.778900, 0.627148)}}, 4, 2}, 0, {-0.000000166}}}, {"test_data/srs_estimator_test_input52.dat"}}, - {{{{1, 197, 0, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 9, 35, 101, 3, srs_resource_configuration::comb_size_enum(4), 1, 4, 14, 0, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.997966, -0.063747), cf_t(-0.689460, -0.724324), cf_t(-0.773925, 0.633277), cf_t(-0.999968, -0.008061), cf_t(0.831868, -0.554973), cf_t(0.803315, 0.595555), cf_t(0.941694, 0.336470), cf_t(-0.909379, 0.415969)}}, 4, 2}, 0, {0.000000259}}}, {"test_data/srs_estimator_test_input53.dat"}}, - {{{{1, 915, 8, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 1, 62, 399, 2, srs_resource_configuration::comb_size_enum(2), 0, 4, 51, 6, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.857206, 0.514974), cf_t(-0.998844, 0.048079), cf_t(-0.340255, -0.940333), cf_t(0.985361, -0.170481)}}, 1, 4}, 0, {-0.000000090}}}, {"test_data/srs_estimator_test_input54.dat"}}, - {{{{1, 977, 8, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 0, 18, 365, 0, srs_resource_configuration::comb_size_enum(4), 2, 7, 44, 7, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.955692, 0.294369), cf_t(-0.581591, 0.813481), cf_t(-0.953626, 0.300995), cf_t(0.057114, 0.998368)}}, 1, 4}, 0, {0.000000112}}}, {"test_data/srs_estimator_test_input55.dat"}}, - {{{{1, 748, 8, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 1, 37, 856, 1, srs_resource_configuration::comb_size_enum(2), 1, 5, 59, 1, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.603640, 0.797257), cf_t(0.985600, -0.169091), cf_t(-0.949970, 0.312340), cf_t(0.372754, 0.927930), cf_t(0.973861, -0.227146), cf_t(-0.497530, -0.867447), cf_t(0.964892, 0.262647), cf_t(-0.856102, -0.516806)}}, 2, 4}, 0, {0.000000091}}}, {"test_data/srs_estimator_test_input56.dat"}}, - {{{{1, 830, 3, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 0, 41, 85, 0, srs_resource_configuration::comb_size_enum(4), 1, 3, 17, 10, 0, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.490896, 0.871218), cf_t(0.746581, -0.665294), cf_t(0.576372, 0.817188), cf_t(0.829621, 0.558328), cf_t(-0.577612, 0.816311), cf_t(0.904936, -0.425548), cf_t(0.721796, 0.692106), cf_t(-0.805382, 0.592756)}}, 2, 4}, 0, {-0.000000236}}}, {"test_data/srs_estimator_test_input57.dat"}}, - {{{{1, 813, 3, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 7, 37, 702, 0, srs_resource_configuration::comb_size_enum(2), 1, 3, 36, 9, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.995429, -0.095508), cf_t(-0.983015, 0.183524), cf_t(-0.999702, -0.024425), cf_t(-0.988245, 0.152878), cf_t(-0.213983, -0.976837), cf_t(-0.928117, -0.372289), cf_t(-0.603883, -0.797073), cf_t(-0.649253, 0.760572), cf_t(0.530349, -0.847779), cf_t(-0.119668, 0.992814), cf_t(-0.354918, 0.934897), cf_t(0.237191, -0.971463), cf_t(-0.913359, 0.407154), cf_t(-0.006167, -0.999981), cf_t(0.643578, 0.765381), cf_t(0.189207, -0.981937)}}, 4, 4}, 0, {0.000000088}}}, {"test_data/srs_estimator_test_input58.dat"}}, - {{{{1, 573, 1, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 4, 18, 961, 3, srs_resource_configuration::comb_size_enum(4), 3, 10, 64, 10, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.952678, -0.303982), cf_t(-0.944096, 0.329672), cf_t(0.922999, -0.384803), cf_t(-0.833438, -0.552613), cf_t(-0.135383, -0.990793), cf_t(-0.606423, -0.795142), cf_t(0.479179, -0.877717), cf_t(0.696150, -0.717896), cf_t(-0.885937, -0.463806), cf_t(-0.991139, -0.132828), cf_t(0.583127, -0.812381), cf_t(0.913975, -0.405771), cf_t(0.986831, 0.161753), cf_t(-0.695067, 0.718945), cf_t(-0.696067, 0.717977), cf_t(-0.490187, -0.871617)}}, 4, 4}, 0, {-0.000000153}}}, {"test_data/srs_estimator_test_input59.dat"}}, - {{{{1, 416, 6, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 8, 18, 956, 2, srs_resource_configuration::comb_size_enum(2), 1, 4, 18, 9, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.207893, -0.978152), cf_t(-0.208225, 0.978081), cf_t(0.794749, -0.606938), cf_t(0.462807, -0.886459)}}, 1, 4}, 0, {-0.000000057}}}, {"test_data/srs_estimator_test_input60.dat"}}, - {{{{1, 854, 4, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 7, 29, 588, 2, srs_resource_configuration::comb_size_enum(4), 3, 0, 45, 3, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.926287, 0.376820), cf_t(-0.924648, 0.380824), cf_t(0.741587, 0.670857), cf_t(0.395312, -0.918547)}}, 1, 4}, 0, {-0.000000091}}}, {"test_data/srs_estimator_test_input61.dat"}}, - {{{{1, 384, 2, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 7, 8, 575, 0, srs_resource_configuration::comb_size_enum(2), 0, 3, 20, 9, 0, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.997308, 0.073328), cf_t(-0.679303, -0.733858), cf_t(-0.968731, -0.248112), cf_t(0.634128, -0.773228), cf_t(0.825762, 0.564019), cf_t(0.986911, -0.161263), cf_t(0.605359, 0.795953), cf_t(-0.902572, -0.430540)}}, 2, 4}, 0, {0.000000259}}}, {"test_data/srs_estimator_test_input62.dat"}}, - {{{{1, 338, 5, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 5, 56, 503, 0, srs_resource_configuration::comb_size_enum(4), 1, 4, 41, 0, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.418036, -0.908430), cf_t(-0.053898, 0.998546), cf_t(0.755220, -0.655471), cf_t(0.801062, -0.598581), cf_t(0.907779, -0.419449), cf_t(-0.832822, -0.553541), cf_t(0.363536, 0.931580), cf_t(-0.999709, -0.024126)}}, 2, 4}, 0, {0.000000059}}}, {"test_data/srs_estimator_test_input63.dat"}}, - {{{{1, 206, 8, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 5, 39, 438, 2, srs_resource_configuration::comb_size_enum(2), 1, 6, 37, 10, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.554288, 0.832325), cf_t(0.515896, -0.856651), cf_t(-0.755858, 0.654736), cf_t(-0.346102, -0.938197), cf_t(-0.924943, -0.380105), cf_t(-0.116682, -0.993169), cf_t(0.159876, -0.987137), cf_t(0.941334, -0.337476), cf_t(-0.339901, -0.940461), cf_t(-0.637574, 0.770389), cf_t(-0.098667, -0.995121), cf_t(0.213435, -0.976957), cf_t(-0.895121, 0.445823), cf_t(-0.958901, 0.283740), cf_t(-0.905569, 0.424199), cf_t(-0.275539, -0.961290)}}, 4, 4}, 0, {-0.000000203}}}, {"test_data/srs_estimator_test_input64.dat"}}, - {{{{1, 470, 3, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 0, 1, 234, 3, srs_resource_configuration::comb_size_enum(4), 0, 6, 14, 9, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.900458, -0.434943), cf_t(0.938785, 0.344502), cf_t(-0.918192, 0.396135), cf_t(0.655961, -0.754795), cf_t(0.719484, 0.694509), cf_t(0.937989, 0.346665), cf_t(0.493938, -0.869497), cf_t(0.817434, 0.576022), cf_t(-0.475320, -0.879813), cf_t(0.575002, 0.818152), cf_t(-0.740095, -0.672502), cf_t(0.837707, -0.546120), cf_t(-0.810545, -0.585677), cf_t(0.992410, 0.122971), cf_t(-0.992012, -0.126140), cf_t(0.778396, 0.627773)}}, 4, 4}, 0, {0.000000009}}}, {"test_data/srs_estimator_test_input65.dat"}}, - {{{{1, 4, 1, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 8, 63, 869, 2, srs_resource_configuration::comb_size_enum(2), 0, 6, 39, 10, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.080884, -0.996724), cf_t(0.867188, 0.497982), cf_t(-0.527332, -0.849659), cf_t(-0.994315, -0.106480)}}, 1, 4}, 0, {-0.000000171}}}, {"test_data/srs_estimator_test_input66.dat"}}, - {{{{1, 451, 9, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 10, 53, 671, 2, srs_resource_configuration::comb_size_enum(4), 2, 4, 16, 4, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.879545, -0.475815), cf_t(0.670159, -0.742218), cf_t(-0.833304, 0.552815), cf_t(0.759936, 0.649998)}}, 1, 4}, 0, {-0.000000029}}}, {"test_data/srs_estimator_test_input67.dat"}}, - {{{{1, 853, 3, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 4, 8, 399, 1, srs_resource_configuration::comb_size_enum(2), 0, 3, 20, 3, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.890602, 0.454783), cf_t(0.040369, 0.999185), cf_t(0.732369, 0.680908), cf_t(0.218573, -0.975821), cf_t(-0.999520, 0.030990), cf_t(0.893589, 0.448886), cf_t(-0.270490, -0.962723), cf_t(-0.785836, 0.618435)}}, 2, 4}, 0, {-0.000000259}}}, {"test_data/srs_estimator_test_input68.dat"}}, - {{{{1, 193, 2, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 1, 8, 274, 2, srs_resource_configuration::comb_size_enum(4), 3, 2, 32, 1, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.711585, 0.702600), cf_t(-0.921034, 0.389482), cf_t(-0.988858, -0.148859), cf_t(0.460400, 0.887712), cf_t(-0.093311, 0.995637), cf_t(0.986576, 0.163301), cf_t(0.909174, 0.416417), cf_t(0.959728, -0.280932)}}, 2, 4}, 0, {-0.000000036}}}, {"test_data/srs_estimator_test_input69.dat"}}, - {{{{1, 7, 9, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 7, 35, 722, 0, srs_resource_configuration::comb_size_enum(2), 1, 4, 60, 7, 0, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.619720, -0.784823), cf_t(0.844985, -0.534791), cf_t(0.603248, -0.797553), cf_t(-0.979316, -0.202335), cf_t(-0.817910, 0.575347), cf_t(0.841278, -0.540602), cf_t(-0.935217, 0.354075), cf_t(-0.208952, -0.977926), cf_t(-0.414485, 0.910056), cf_t(-0.838918, -0.544258), cf_t(0.824798, -0.565428), cf_t(0.429744, 0.902951), cf_t(-0.775950, -0.630794), cf_t(-0.495848, 0.868409), cf_t(0.978348, 0.206966), cf_t(-0.517308, 0.855799)}}, 4, 4}, 0, {-0.000000163}}}, {"test_data/srs_estimator_test_input70.dat"}}, - {{{{1, 561, 3, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 0, 15, 565, 0, srs_resource_configuration::comb_size_enum(4), 0, 11, 55, 3, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.136161, -0.990687), cf_t(0.999972, 0.007530), cf_t(-0.963619, -0.267278), cf_t(0.072483, -0.997370), cf_t(0.449440, 0.893310), cf_t(-0.405318, 0.914176), cf_t(-0.927533, 0.373742), cf_t(0.077883, -0.996962), cf_t(-0.639218, 0.769025), cf_t(-0.311305, -0.950310), cf_t(-0.233001, 0.972477), cf_t(-0.887975, -0.459892), cf_t(0.375183, 0.926951), cf_t(-0.705972, -0.708240), cf_t(-0.999946, -0.010424), cf_t(-0.014684, -0.999892)}}, 4, 4}, 0, {0.000000076}}}, {"test_data/srs_estimator_test_input71.dat"}}, + {{{{0, 130, 8, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 12, 17, 647, 2, srs_resource_configuration::comb_size_enum(2), 1, 1, 66, 1, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.681653, -0.188013), cf_t(-0.704124, 0.064883)}}, 1, 2}, 0, 0, {0.000000313}}}, {"test_data/srs_estimator_test_input0.dat"}}, + {{{{0, 937, 1, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 11, 2, 982, 3, srs_resource_configuration::comb_size_enum(4), 3, 9, 50, 7, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.551089, 0.443059), cf_t(-0.395695, -0.586025)}}, 1, 2}, 0, 0, {-0.000000343}}}, {"test_data/srs_estimator_test_input1.dat"}}, + {{{{0, 283, 7, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 0, 44, 99, 1, srs_resource_configuration::comb_size_enum(2), 1, 5, 25, 9, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.068830, -0.703749), cf_t(0.273171, 0.652210), cf_t(0.198129, -0.678782), cf_t(-0.705645, 0.045444)}}, 2, 2}, 0, 0, {-0.000000057}}}, {"test_data/srs_estimator_test_input2.dat"}}, + {{{{0, 772, 6, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 3, 10, 696, 0, srs_resource_configuration::comb_size_enum(4), 1, 4, 39, 7, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.115826, 0.697556), cf_t(-0.022633, 0.706744), cf_t(0.005629, -0.707084), cf_t(-0.706612, -0.026460)}}, 2, 2}, 0, 0, {0.000000207}}}, {"test_data/srs_estimator_test_input3.dat"}}, + {{{{0, 560, 8, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 1, 58, 152, 1, srs_resource_configuration::comb_size_enum(2), 1, 4, 38, 6, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.628305, 0.324397), cf_t(-0.693909, -0.135978), cf_t(0.647192, -0.284854), cf_t(-0.642017, -0.296334), cf_t(0.666869, 0.235130), cf_t(0.128862, -0.695266), cf_t(0.484352, 0.515173), cf_t(-0.694070, 0.135157)}}, 4, 2}, 0, 0, {-0.000000508}}}, {"test_data/srs_estimator_test_input4.dat"}}, + {{{{0, 813, 3, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 4, 38, 541, 1, srs_resource_configuration::comb_size_enum(4), 2, 8, 30, 1, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.611285, 0.355430), cf_t(0.604843, -0.366285), cf_t(0.324250, -0.628381), cf_t(0.706898, -0.017171), cf_t(0.093131, 0.700947), cf_t(0.407033, 0.578207), cf_t(-0.686686, -0.168708), cf_t(0.623507, 0.333526)}}, 4, 2}, 0, 0, {-0.000000060}}}, {"test_data/srs_estimator_test_input5.dat"}}, + {{{{0, 4, 1, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 10, 5, 836, 1, srs_resource_configuration::comb_size_enum(2), 0, 5, 61, 9, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.293626, 0.643260), cf_t(-0.061248, 0.704449)}}, 1, 2}, 0, 0, {-0.000000369}}}, {"test_data/srs_estimator_test_input6.dat"}}, + {{{{0, 593, 1, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 7, 39, 148, 1, srs_resource_configuration::comb_size_enum(4), 2, 0, 16, 9, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.505253, 0.494691), cf_t(0.285273, 0.647008)}}, 1, 2}, 0, 0, {-0.000000271}}}, {"test_data/srs_estimator_test_input7.dat"}}, + {{{{0, 924, 4, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 12, 15, 502, 1, srs_resource_configuration::comb_size_enum(2), 1, 0, 11, 0, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.418800, -0.569743), cf_t(-0.423687, -0.566118), cf_t(-0.081027, -0.702449), cf_t(-0.673755, 0.214601)}}, 2, 2}, 0, 0, {0.000000049}}}, {"test_data/srs_estimator_test_input8.dat"}}, + {{{{0, 193, 2, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 8, 40, 187, 3, srs_resource_configuration::comb_size_enum(4), 0, 9, 33, 4, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.650456, 0.277321), cf_t(-0.245156, 0.663248), cf_t(-0.667946, 0.232053), cf_t(-0.706097, -0.037785)}}, 2, 2}, 0, 0, {0.000000011}}}, {"test_data/srs_estimator_test_input9.dat"}}, + {{{{0, 659, 8, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 4, 22, 831, 3, srs_resource_configuration::comb_size_enum(2), 1, 7, 39, 5, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.185548, 0.682328), cf_t(-0.695339, 0.128467), cf_t(0.394909, -0.586555), cf_t(0.106569, 0.699030), cf_t(-0.223768, 0.670767), cf_t(0.086472, 0.701800), cf_t(0.240509, 0.664948), cf_t(0.337891, 0.621152)}}, 4, 2}, 0, 0, {-0.000000284}}}, {"test_data/srs_estimator_test_input10.dat"}}, + {{{{0, 945, 4, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 5, 62, 189, 1, srs_resource_configuration::comb_size_enum(4), 0, 4, 40, 9, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.054202, 0.705026), cf_t(-0.170613, -0.686215), cf_t(0.523244, 0.475621), cf_t(-0.296151, 0.642102), cf_t(-0.564546, -0.425779), cf_t(0.124868, 0.695994), cf_t(-0.204416, 0.676915), cf_t(-0.628347, 0.324314)}}, 4, 2}, 0, 0, {0.000000008}}}, {"test_data/srs_estimator_test_input11.dat"}}, + {{{{0, 820, 0, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 0, 14, 951, 1, srs_resource_configuration::comb_size_enum(2), 1, 4, 24, 5, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.705092, -0.053344), cf_t(0.687321, 0.166103)}}, 1, 2}, 0, 0, {0.000000401}}}, {"test_data/srs_estimator_test_input12.dat"}}, + {{{{0, 101, 9, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 2, 8, 343, 2, srs_resource_configuration::comb_size_enum(4), 0, 5, 52, 7, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.154090, -0.690113), cf_t(0.581620, -0.402142)}}, 1, 2}, 0, 0, {0.000000407}}}, {"test_data/srs_estimator_test_input13.dat"}}, + {{{{0, 202, 3, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 0, 11, 761, 0, srs_resource_configuration::comb_size_enum(2), 1, 5, 11, 6, 0, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.700772, -0.094436), cf_t(-0.707104, -0.002095), cf_t(-0.164231, -0.687771), cf_t(-0.695472, 0.127746)}}, 2, 2}, 0, 0, {-0.000000459}}}, {"test_data/srs_estimator_test_input14.dat"}}, + {{{{0, 73, 6, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 5, 52, 99, 2, srs_resource_configuration::comb_size_enum(4), 0, 6, 66, 8, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.419244, -0.569416), cf_t(-0.677521, 0.202400), cf_t(0.219904, -0.672043), cf_t(-0.644261, 0.291424)}}, 2, 2}, 0, 0, {0.000000339}}}, {"test_data/srs_estimator_test_input15.dat"}}, + {{{{0, 177, 0, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 4, 3, 851, 1, srs_resource_configuration::comb_size_enum(2), 1, 7, 42, 8, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.184375, 0.682646), cf_t(0.703762, 0.068699), cf_t(0.351621, 0.613484), cf_t(-0.491797, 0.508071), cf_t(-0.642898, 0.294418), cf_t(0.703565, -0.070685), cf_t(0.555395, 0.437648), cf_t(0.226443, 0.669868)}}, 4, 2}, 0, 0, {-0.000000011}}}, {"test_data/srs_estimator_test_input16.dat"}}, + {{{{0, 942, 3, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 0, 27, 755, 2, srs_resource_configuration::comb_size_enum(4), 3, 11, 20, 2, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.213860, -0.673991), cf_t(-0.685847, -0.172089), cf_t(-0.354087, -0.612064), cf_t(0.490441, 0.509380), cf_t(-0.354814, -0.611643), cf_t(-0.226497, -0.669850), cf_t(0.308558, 0.636233), cf_t(0.707095, -0.004086)}}, 4, 2}, 0, 0, {-0.000000343}}}, {"test_data/srs_estimator_test_input17.dat"}}, + {{{{0, 903, 0, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 9, 29, 195, 3, srs_resource_configuration::comb_size_enum(2), 0, 7, 25, 4, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.256483, 0.658951), cf_t(-0.636466, 0.308076), cf_t(-0.702600, 0.079704), cf_t(0.513595, 0.486025)}}, 1, 4}, 0, 0, {0.000000093}}}, {"test_data/srs_estimator_test_input18.dat"}}, + {{{{0, 596, 2, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 3, 16, 297, 3, srs_resource_configuration::comb_size_enum(4), 3, 4, 39, 6, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.389686, 0.313280), cf_t(0.415836, -0.277633), cf_t(0.363739, -0.343066), cf_t(0.206503, -0.455364)}}, 1, 4}, 0, 0, {-0.000000249}}}, {"test_data/srs_estimator_test_input19.dat"}}, + {{{{0, 435, 5, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 4, 2, 165, 0, srs_resource_configuration::comb_size_enum(2), 0, 7, 27, 7, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.300996, -0.639845), cf_t(-0.321596, 0.629743), cf_t(-0.139653, -0.693179), cf_t(0.557006, 0.435596), cf_t(0.693433, -0.138388), cf_t(-0.542110, -0.454000), cf_t(-0.693447, -0.138315), cf_t(0.127268, -0.695559)}}, 2, 4}, 0, 0, {-0.000000080}}}, {"test_data/srs_estimator_test_input20.dat"}}, + {{{{0, 157, 0, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 3, 29, 450, 3, srs_resource_configuration::comb_size_enum(4), 2, 7, 65, 5, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.041264, 0.705902), cf_t(-0.238936, -0.665514), cf_t(-0.316569, -0.632285), cf_t(0.643555, 0.292979), cf_t(-0.171822, 0.685913), cf_t(-0.021279, 0.706787), cf_t(-0.333590, -0.623472), cf_t(0.114826, 0.697721)}}, 2, 4}, 0, 0, {0.000000175}}}, {"test_data/srs_estimator_test_input21.dat"}}, + {{{{0, 799, 8, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 9, 24, 6, 3, srs_resource_configuration::comb_size_enum(2), 0, 5, 31, 6, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.089328, -0.701442), cf_t(0.689330, 0.157556), cf_t(0.405786, 0.579084), cf_t(-0.051373, -0.705238), cf_t(-0.310971, 0.635056), cf_t(0.317553, 0.631791), cf_t(-0.383096, 0.594338), cf_t(0.031758, 0.706393), cf_t(0.153120, -0.690329), cf_t(-0.124818, -0.696003), cf_t(-0.552155, -0.441729), cf_t(0.614049, -0.350634), cf_t(-0.695687, 0.126571), cf_t(-0.697317, 0.117255), cf_t(0.253078, 0.660266), cf_t(-0.084486, 0.702041)}}, 4, 4}, 0, 0, {0.000000277}}}, {"test_data/srs_estimator_test_input22.dat"}}, + {{{{0, 93, 1, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 8, 27, 699, 2, srs_resource_configuration::comb_size_enum(4), 2, 7, 64, 6, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.180430, 0.683700), cf_t(-0.552392, -0.441433), cf_t(0.089882, -0.701371), cf_t(0.386095, -0.592394), cf_t(-0.178940, -0.684091), cf_t(-0.672687, 0.217926), cf_t(-0.416410, 0.571492), cf_t(0.351950, -0.613296), cf_t(0.061100, 0.704462), cf_t(-0.683462, 0.181329), cf_t(-0.371318, -0.601766), cf_t(-0.028609, 0.706528), cf_t(0.517291, 0.482089), cf_t(-0.371563, -0.601615), cf_t(-0.611241, 0.355506), cf_t(-0.534906, -0.462466)}}, 4, 4}, 0, 0, {0.000000086}}}, {"test_data/srs_estimator_test_input23.dat"}}, + {{{{0, 271, 5, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 4, 41, 122, 1, srs_resource_configuration::comb_size_enum(2), 1, 7, 36, 10, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.127928, -0.695438), cf_t(-0.700055, -0.099612), cf_t(0.706554, -0.027962), cf_t(0.138269, 0.693456)}}, 1, 4}, 0, 0, {-0.000000411}}}, {"test_data/srs_estimator_test_input24.dat"}}, + {{{{0, 414, 1, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 5, 40, 374, 3, srs_resource_configuration::comb_size_enum(4), 3, 2, 9, 8, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.165621, -0.471773), cf_t(0.415613, 0.277968), cf_t(-0.493644, -0.079472), cf_t(-0.490940, -0.094753)}}, 1, 4}, 0, 0, {0.000000376}}}, {"test_data/srs_estimator_test_input25.dat"}}, + {{{{0, 687, 4, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 9, 1, 532, 1, srs_resource_configuration::comb_size_enum(2), 0, 2, 55, 4, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.452308, 0.213113), cf_t(-0.398502, 0.301987), cf_t(0.380755, -0.324076), cf_t(0.179714, -0.466586), cf_t(-0.387615, 0.315840), cf_t(0.015947, -0.499746), cf_t(0.059905, -0.496398), cf_t(-0.358835, 0.348192)}}, 2, 4}, 0, 0, {-0.000000296}}}, {"test_data/srs_estimator_test_input26.dat"}}, + {{{{0, 335, 7, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 8, 49, 449, 0, srs_resource_configuration::comb_size_enum(4), 3, 6, 60, 9, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.601683, -0.371453), cf_t(-0.005750, -0.707083), cf_t(0.398363, 0.584215), cf_t(0.323329, -0.628855), cf_t(0.219088, 0.672310), cf_t(0.175691, -0.684933), cf_t(-0.589672, 0.390240), cf_t(-0.295126, 0.642573)}}, 2, 4}, 0, 0, {0.000000035}}}, {"test_data/srs_estimator_test_input27.dat"}}, + {{{{0, 139, 0, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 8, 31, 507, 0, srs_resource_configuration::comb_size_enum(2), 0, 6, 63, 2, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.232542, -0.667776), cf_t(0.705391, -0.049225), cf_t(0.705698, -0.044620), cf_t(0.098124, 0.700265), cf_t(-0.613574, -0.351464), cf_t(0.707103, 0.002321), cf_t(-0.696439, -0.122360), cf_t(-0.707056, 0.008467), cf_t(0.282445, -0.648248), cf_t(0.469078, -0.529118), cf_t(-0.701262, 0.090724), cf_t(0.574279, -0.412557), cf_t(0.512450, -0.487232), cf_t(-0.537496, -0.459454), cf_t(0.224194, -0.670624), cf_t(-0.630719, -0.319678)}}, 4, 4}, 0, 0, {0.000000360}}}, {"test_data/srs_estimator_test_input28.dat"}}, + {{{{0, 252, 7, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 8, 42, 85, 2, srs_resource_configuration::comb_size_enum(4), 3, 9, 39, 6, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.636582, -0.307836), cf_t(0.459941, -0.537080), cf_t(-0.669283, -0.228167), cf_t(-0.458930, 0.537944), cf_t(-0.619449, -0.341003), cf_t(-0.703668, 0.069656), cf_t(-0.484425, -0.515104), cf_t(0.673137, 0.216532), cf_t(0.703085, 0.075310), cf_t(0.396922, -0.585194), cf_t(0.692870, 0.141177), cf_t(-0.705589, 0.046306), cf_t(0.512837, 0.486825), cf_t(0.178409, 0.684230), cf_t(-0.531250, -0.466662), cf_t(0.249901, 0.661475)}}, 4, 4}, 0, 0, {-0.000000393}}}, {"test_data/srs_estimator_test_input29.dat"}}, + {{{{0, 193, 2, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 0, 34, 650, 2, srs_resource_configuration::comb_size_enum(2), 0, 5, 8, 3, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.705809, 0.042815), cf_t(0.426329, -0.564131), cf_t(0.496619, -0.503358), cf_t(-0.089921, 0.701366)}}, 1, 4}, 0, 0, {-0.000000304}}}, {"test_data/srs_estimator_test_input30.dat"}}, + {{{{0, 427, 5, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 2, 6, 970, 0, srs_resource_configuration::comb_size_enum(4), 0, 6, 3, 0, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.642064, -0.296233), cf_t(-0.094520, -0.700761), cf_t(-0.053966, -0.705044), cf_t(0.651733, 0.274306)}}, 1, 4}, 0, 0, {0.000000375}}}, {"test_data/srs_estimator_test_input31.dat"}}, + {{{{0, 879, 9, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 8, 25, 525, 0, srs_resource_configuration::comb_size_enum(2), 0, 3, 20, 1, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.248919, 0.433635), cf_t(0.273741, -0.418409), cf_t(-0.489335, 0.102722), cf_t(-0.465999, -0.181233), cf_t(-0.298453, -0.401156), cf_t(0.304214, -0.396804), cf_t(0.493732, 0.078925), cf_t(-0.288476, 0.408389)}}, 2, 4}, 0, 0, {-0.000000056}}}, {"test_data/srs_estimator_test_input32.dat"}}, + {{{{0, 678, 0, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 3, 63, 920, 2, srs_resource_configuration::comb_size_enum(4), 2, 3, 28, 1, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.487848, 0.109562), cf_t(0.218227, 0.449863), cf_t(0.043791, -0.498079), cf_t(-0.317827, 0.385987), cf_t(0.207771, -0.454787), cf_t(0.468599, 0.174398), cf_t(0.404099, 0.294455), cf_t(-0.495280, -0.068539)}}, 2, 4}, 0, 0, {-0.000000171}}}, {"test_data/srs_estimator_test_input33.dat"}}, + {{{{0, 926, 1, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 7, 6, 479, 2, srs_resource_configuration::comb_size_enum(2), 1, 2, 40, 10, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.154320, 0.475589), cf_t(0.450451, 0.217010), cf_t(0.498260, 0.041682), cf_t(-0.176346, 0.467870), cf_t(0.332718, 0.373228), cf_t(0.023595, 0.499443), cf_t(0.399259, -0.300985), cf_t(-0.481063, 0.136302), cf_t(0.116412, 0.486259), cf_t(0.471752, 0.165682), cf_t(0.164458, 0.472179), cf_t(0.401404, 0.298119), cf_t(0.394965, -0.306598), cf_t(-0.466853, 0.179022), cf_t(0.416396, 0.276793), cf_t(0.499790, -0.014482)}}, 4, 4}, 0, 0, {-0.000000175}}}, {"test_data/srs_estimator_test_input34.dat"}}, + {{{{0, 305, 2, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 0, 40, 517, 0, srs_resource_configuration::comb_size_enum(4), 0, 10, 36, 8, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.547223, 0.447825), cf_t(-0.016378, -0.706917), cf_t(-0.562870, -0.427992), cf_t(0.138503, -0.693410), cf_t(0.324218, -0.628397), cf_t(0.705616, 0.045892), cf_t(-0.697618, -0.115451), cf_t(-0.167131, 0.687072), cf_t(-0.371724, 0.601516), cf_t(0.674598, 0.211937), cf_t(-0.089905, -0.701368), cf_t(-0.249812, -0.661509), cf_t(-0.192891, 0.680289), cf_t(-0.348735, -0.615129), cf_t(-0.187643, -0.681755), cf_t(-0.662754, -0.246491)}}, 4, 4}, 0, 0, {-0.000000108}}}, {"test_data/srs_estimator_test_input35.dat"}}, + {{{{1, 345, 0, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 8, 7, 759, 0, srs_resource_configuration::comb_size_enum(2), 1, 7, 50, 7, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.612700, -0.352985), cf_t(-0.044256, -0.705720)}}, 1, 2}, 0, 0, {-0.000000138}}}, {"test_data/srs_estimator_test_input36.dat"}}, + {{{{1, 887, 7, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 1, 43, 375, 2, srs_resource_configuration::comb_size_enum(4), 3, 2, 5, 4, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.097142, -0.700402), cf_t(0.194397, 0.679860)}}, 1, 2}, 0, 0, {-0.000000058}}}, {"test_data/srs_estimator_test_input37.dat"}}, + {{{{1, 657, 5, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 6, 6, 155, 1, srs_resource_configuration::comb_size_enum(2), 0, 1, 27, 8, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.559142, 0.432851), cf_t(0.151769, -0.690627), cf_t(0.538311, 0.458499), cf_t(-0.182599, 0.683123)}}, 2, 2}, 0, 0, {0.000000054}}}, {"test_data/srs_estimator_test_input38.dat"}}, + {{{{1, 711, 9, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 10, 7, 443, 3, srs_resource_configuration::comb_size_enum(4), 0, 9, 33, 7, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.084030, -0.702096), cf_t(-0.101562, 0.699775), cf_t(-0.561454, 0.429848), cf_t(0.687844, 0.163924)}}, 2, 2}, 0, 0, {0.000000090}}}, {"test_data/srs_estimator_test_input39.dat"}}, + {{{{1, 624, 4, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 0, 44, 323, 0, srs_resource_configuration::comb_size_enum(2), 0, 0, 28, 8, 0, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.395344, -0.586262), cf_t(-0.693555, -0.137772), cf_t(-0.478297, -0.520799), cf_t(0.469935, 0.528357), cf_t(-0.119723, -0.696898), cf_t(0.548168, 0.446668), cf_t(0.495266, 0.504690), cf_t(0.575711, 0.410557)}}, 4, 2}, 0, 0, {-0.000000186}}}, {"test_data/srs_estimator_test_input40.dat"}}, + {{{{1, 325, 1, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(1), 4, 57, 222, 2, srs_resource_configuration::comb_size_enum(4), 2, 2, 5, 2, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.605906, -0.364524), cf_t(-0.661004, -0.251145), cf_t(0.355334, 0.611341), cf_t(0.705076, -0.053553), cf_t(-0.189947, -0.681117), cf_t(-0.274407, 0.651691), cf_t(-0.507800, -0.492076), cf_t(0.338968, 0.620565)}}, 4, 2}, 0, 0, {-0.000000126}}}, {"test_data/srs_estimator_test_input41.dat"}}, + {{{{1, 700, 3, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 5, 43, 1006, 1, srs_resource_configuration::comb_size_enum(2), 1, 7, 22, 3, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.215231, 0.673555), cf_t(-0.675969, 0.207525)}}, 1, 2}, 0, 0, {-0.000000040}}}, {"test_data/srs_estimator_test_input42.dat"}}, + {{{{1, 760, 3, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 5, 1, 439, 1, srs_resource_configuration::comb_size_enum(4), 1, 11, 63, 1, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.682496, 0.184930), cf_t(0.042278, 0.705842)}}, 1, 2}, 0, 0, {0.000000137}}}, {"test_data/srs_estimator_test_input43.dat"}}, + {{{{1, 761, 7, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 1, 13, 697, 0, srs_resource_configuration::comb_size_enum(2), 1, 1, 45, 5, 0, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.557057, -0.435531), cf_t(-0.207059, -0.676111), cf_t(-0.703283, -0.073434), cf_t(0.402617, 0.581291)}}, 2, 2}, 0, 0, {0.000000236}}}, {"test_data/srs_estimator_test_input44.dat"}}, + {{{{1, 37, 5, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 10, 33, 766, 1, srs_resource_configuration::comb_size_enum(4), 2, 4, 12, 1, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.023928, 0.706702), cf_t(0.627344, -0.326251), cf_t(0.701229, 0.090985), cf_t(-0.402217, -0.581568)}}, 2, 2}, 0, 0, {0.000000225}}}, {"test_data/srs_estimator_test_input45.dat"}}, + {{{{1, 813, 1, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 7, 32, 450, 2, srs_resource_configuration::comb_size_enum(2), 1, 0, 58, 9, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.480184, -0.519060), cf_t(0.706981, -0.013313), cf_t(-0.406768, -0.578394), cf_t(-0.536953, 0.460089), cf_t(-0.433649, 0.558523), cf_t(0.114250, 0.697816), cf_t(-0.558749, -0.433358), cf_t(0.443197, 0.550978)}}, 4, 2}, 0, 0, {-0.000000247}}}, {"test_data/srs_estimator_test_input46.dat"}}, + {{{{1, 743, 4, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(2), 4, 36, 861, 0, srs_resource_configuration::comb_size_enum(4), 3, 11, 15, 8, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.495468, 0.504492), cf_t(-0.450328, -0.545165), cf_t(0.678967, 0.197495), cf_t(-0.406363, 0.578678), cf_t(0.602907, 0.369464), cf_t(0.298589, 0.640971), cf_t(-0.118624, -0.697086), cf_t(-0.376570, -0.598494)}}, 4, 2}, 0, 0, {-0.000000060}}}, {"test_data/srs_estimator_test_input47.dat"}}, + {{{{1, 932, 6, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 8, 24, 763, 2, srs_resource_configuration::comb_size_enum(2), 1, 3, 16, 8, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.674713, 0.211572), cf_t(0.098696, 0.700185)}}, 1, 2}, 0, 0, {0.000000159}}}, {"test_data/srs_estimator_test_input48.dat"}}, + {{{{1, 548, 9, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 0, 4, 821, 3, srs_resource_configuration::comb_size_enum(4), 0, 9, 36, 10, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.531463, -0.466419), cf_t(0.569452, -0.419196)}}, 1, 2}, 0, 0, {0.000000066}}}, {"test_data/srs_estimator_test_input49.dat"}}, + {{{{1, 186, 1, 0}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 0, 49, 109, 1, srs_resource_configuration::comb_size_enum(2), 1, 3, 46, 10, 1, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.696311, -0.123090), cf_t(-0.565094, -0.425052), cf_t(-0.600794, 0.372890), cf_t(0.002311, -0.707103)}}, 2, 2}, 0, 0, {0.000000044}}}, {"test_data/srs_estimator_test_input50.dat"}}, + {{{{1, 524, 5, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 0, 22, 736, 3, srs_resource_configuration::comb_size_enum(4), 1, 5, 28, 10, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.142382, 0.692623), cf_t(-0.255814, 0.659211), cf_t(0.497939, 0.502052), cf_t(-0.105767, -0.699152)}}, 2, 2}, 0, 0, {0.000000147}}}, {"test_data/srs_estimator_test_input51.dat"}}, + {{{{1, 863, 6, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 10, 24, 789, 2, srs_resource_configuration::comb_size_enum(2), 1, 3, 45, 0, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.700048, 0.099667), cf_t(0.060151, 0.704544), cf_t(0.339262, -0.620404), cf_t(0.648026, -0.282952), cf_t(-0.504016, -0.495951), cf_t(0.312584, 0.634264), cf_t(0.075039, -0.703114), cf_t(0.550766, 0.443460)}}, 4, 2}, 0, 0, {-0.000000166}}}, {"test_data/srs_estimator_test_input52.dat"}}, + {{{{1, 197, 0, 1}, {srs_resource_configuration::one_two_four_enum(2), srs_resource_configuration::one_two_four_enum(4), 9, 35, 101, 3, srs_resource_configuration::comb_size_enum(4), 1, 4, 14, 0, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.705669, -0.045076), cf_t(-0.487522, -0.512174), cf_t(-0.547248, 0.447794), cf_t(-0.707084, -0.005700), cf_t(0.588220, -0.392425), cf_t(0.568029, 0.421121), cf_t(0.665878, 0.237921), cf_t(-0.643028, 0.294135)}}, 4, 2}, 0, 0, {0.000000259}}}, {"test_data/srs_estimator_test_input53.dat"}}, + {{{{1, 915, 8, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 1, 62, 399, 2, srs_resource_configuration::comb_size_enum(2), 0, 4, 51, 6, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.606136, 0.364142), cf_t(-0.706289, 0.033997), cf_t(-0.240597, -0.664916), cf_t(0.696755, -0.120548)}}, 1, 4}, 0, 0, {-0.000000090}}}, {"test_data/srs_estimator_test_input54.dat"}}, + {{{{1, 977, 8, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 0, 18, 365, 0, srs_resource_configuration::comb_size_enum(4), 2, 7, 44, 7, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.675776, 0.208150), cf_t(-0.411247, 0.575218), cf_t(-0.674315, 0.212836), cf_t(0.040386, 0.705953)}}, 1, 4}, 0, 0, {0.000000112}}}, {"test_data/srs_estimator_test_input55.dat"}}, + {{{{1, 748, 8, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 1, 37, 856, 1, srs_resource_configuration::comb_size_enum(2), 1, 5, 59, 1, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.426838, 0.563746), cf_t(0.696925, -0.119566), cf_t(-0.671730, 0.220858), cf_t(0.263577, 0.656146), cf_t(0.688624, -0.160616), cf_t(-0.351807, -0.613378), cf_t(0.682282, 0.185720), cf_t(-0.605356, -0.365437)}}, 2, 4}, 0, 0, {0.000000091}}}, {"test_data/srs_estimator_test_input56.dat"}}, + {{{{1, 830, 3, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 0, 41, 85, 0, srs_resource_configuration::comb_size_enum(4), 1, 3, 17, 10, 0, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.245448, 0.435609), cf_t(0.373291, -0.332647), cf_t(0.288186, 0.408594), cf_t(0.414810, 0.279164), cf_t(-0.288806, 0.408156), cf_t(0.452468, -0.212774), cf_t(0.360898, 0.346053), cf_t(-0.402691, 0.296378)}}, 2, 4}, 0, 0, {-0.000000236}}}, {"test_data/srs_estimator_test_input57.dat"}}, + {{{{1, 813, 3, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 7, 37, 702, 0, srs_resource_configuration::comb_size_enum(2), 1, 3, 36, 9, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.497714, -0.047754), cf_t(-0.491508, 0.091762), cf_t(-0.499851, -0.012213), cf_t(-0.494123, 0.076439), cf_t(-0.106992, -0.488419), cf_t(-0.464059, -0.186144), cf_t(-0.301942, -0.398536), cf_t(-0.324627, 0.380286), cf_t(0.265175, -0.423890), cf_t(-0.059834, 0.496407), cf_t(-0.177459, 0.467449), cf_t(0.118596, -0.485731), cf_t(-0.456680, 0.203577), cf_t(-0.003084, -0.499990), cf_t(0.321789, 0.382690), cf_t(0.094603, -0.490969)}}, 4, 4}, 0, 0, {0.000000088}}}, {"test_data/srs_estimator_test_input58.dat"}}, + {{{{1, 573, 1, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(1), 4, 18, 961, 3, srs_resource_configuration::comb_size_enum(4), 3, 10, 64, 10, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.673645, -0.214948), cf_t(-0.667576, 0.233113), cf_t(0.652659, -0.272097), cf_t(-0.589330, -0.390756), cf_t(-0.095730, -0.700597), cf_t(-0.428806, -0.562251), cf_t(0.338831, -0.620640), cf_t(0.492252, -0.507629), cf_t(-0.626452, -0.327960), cf_t(-0.700841, -0.093924), cf_t(0.412333, -0.574440), cf_t(0.646278, -0.286923), cf_t(0.697795, 0.114377), cf_t(-0.491487, 0.508371), cf_t(-0.492194, 0.507686), cf_t(-0.346614, -0.616327)}}, 4, 4}, 0, 0, {-0.000000153}}}, {"test_data/srs_estimator_test_input59.dat"}}, + {{{{1, 416, 6, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 8, 18, 956, 2, srs_resource_configuration::comb_size_enum(2), 1, 4, 18, 9, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.147002, -0.691658), cf_t(-0.147238, 0.691608), cf_t(0.561973, -0.429170), cf_t(0.327254, -0.626821)}}, 1, 4}, 0, 0, {-0.000000057}}}, {"test_data/srs_estimator_test_input60.dat"}}, + {{{{1, 854, 4, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 7, 29, 588, 2, srs_resource_configuration::comb_size_enum(4), 3, 0, 45, 3, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.463143, 0.188410), cf_t(-0.462324, 0.190412), cf_t(0.370793, 0.335429), cf_t(0.197656, -0.459274)}}, 1, 4}, 0, 0, {-0.000000091}}}, {"test_data/srs_estimator_test_input61.dat"}}, + {{{{1, 384, 2, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 7, 8, 575, 0, srs_resource_configuration::comb_size_enum(2), 0, 3, 20, 9, 0, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.498654, 0.036664), cf_t(-0.339651, -0.366929), cf_t(-0.484366, -0.124056), cf_t(0.317064, -0.386614), cf_t(0.412881, 0.282009), cf_t(0.493456, -0.080631), cf_t(0.302679, 0.397976), cf_t(-0.451286, -0.215270)}}, 2, 4}, 0, 0, {0.000000259}}}, {"test_data/srs_estimator_test_input62.dat"}}, + {{{{1, 338, 5, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 5, 56, 503, 0, srs_resource_configuration::comb_size_enum(4), 1, 4, 41, 0, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(0.209018, -0.454215), cf_t(-0.026949, 0.499273), cf_t(0.377610, -0.327736), cf_t(0.400531, -0.299291), cf_t(0.453889, -0.209725), cf_t(-0.416411, -0.276770), cf_t(0.181768, 0.465790), cf_t(-0.499854, -0.012063)}}, 2, 4}, 0, 0, {0.000000059}}}, {"test_data/srs_estimator_test_input63.dat"}}, + {{{{1, 206, 8, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 5, 39, 438, 2, srs_resource_configuration::comb_size_enum(2), 1, 6, 37, 10, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.391941, 0.588542), cf_t(0.364793, -0.605744), cf_t(-0.534472, 0.462968), cf_t(-0.244731, -0.663405), cf_t(-0.654034, -0.268775), cf_t(-0.082507, -0.702277), cf_t(0.113049, -0.698011), cf_t(0.665624, -0.238631), cf_t(-0.240346, -0.665007), cf_t(-0.450833, 0.544747), cf_t(-0.069768, -0.703656), cf_t(0.150921, -0.690813), cf_t(-0.632946, 0.315244), cf_t(-0.678046, 0.200634), cf_t(-0.640334, 0.299954), cf_t(-0.194835, -0.679735)}}, 4, 4}, 0, 0, {-0.000000203}}}, {"test_data/srs_estimator_test_input64.dat"}}, + {{{{1, 470, 3, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(2), 0, 1, 234, 3, srs_resource_configuration::comb_size_enum(4), 0, 6, 14, 9, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.636720, -0.307551), cf_t(0.663822, 0.243600), cf_t(-0.649260, 0.280110), cf_t(0.463834, -0.533720), cf_t(0.508752, 0.491092), cf_t(0.663258, 0.245129), cf_t(0.349267, -0.614827), cf_t(0.578013, 0.407309), cf_t(-0.336102, -0.622122), cf_t(0.406588, 0.578521), cf_t(-0.523326, -0.475531), cf_t(0.592348, -0.386165), cf_t(-0.573142, -0.414136), cf_t(0.701740, 0.086953), cf_t(-0.701459, -0.089194), cf_t(0.550409, 0.443903)}}, 4, 4}, 0, 0, {0.000000009}}}, {"test_data/srs_estimator_test_input65.dat"}}, + {{{{1, 4, 1, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 8, 63, 869, 2, srs_resource_configuration::comb_size_enum(2), 0, 6, 39, 10, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(0.057193, -0.704790), cf_t(0.613194, 0.352126), cf_t(-0.372880, -0.600800), cf_t(-0.703087, -0.075293)}}, 1, 4}, 0, 0, {-0.000000171}}}, {"test_data/srs_estimator_test_input66.dat"}}, + {{{{1, 451, 9, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 10, 53, 671, 2, srs_resource_configuration::comb_size_enum(4), 2, 4, 16, 4, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0}}, {{{{cf_t(-0.439773, -0.237908), cf_t(0.335080, -0.371109), cf_t(-0.416652, 0.276408), cf_t(0.379968, 0.324999)}}, 1, 4}, 0, 0, {-0.000000029}}}, {"test_data/srs_estimator_test_input67.dat"}}, + {{{{1, 853, 3, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 4, 8, 399, 1, srs_resource_configuration::comb_size_enum(2), 0, 3, 20, 3, 2, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.445301, 0.227391), cf_t(0.020184, 0.499592), cf_t(0.366185, 0.340454), cf_t(0.109286, -0.487910), cf_t(-0.499760, 0.015495), cf_t(0.446794, 0.224443), cf_t(-0.135245, -0.481361), cf_t(-0.392918, 0.309218)}}, 2, 4}, 0, 0, {-0.000000259}}}, {"test_data/srs_estimator_test_input68.dat"}}, + {{{{1, 193, 2, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 1, 8, 274, 2, srs_resource_configuration::comb_size_enum(4), 3, 2, 32, 1, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1}}, {{{{cf_t(-0.355792, 0.351300), cf_t(-0.460517, 0.194741), cf_t(-0.494429, -0.074430), cf_t(0.230200, 0.443856), cf_t(-0.046656, 0.497818), cf_t(0.493288, 0.081651), cf_t(0.454587, 0.208209), cf_t(0.479864, -0.140466)}}, 2, 4}, 0, 0, {-0.000000036}}}, {"test_data/srs_estimator_test_input69.dat"}}, + {{{{1, 7, 9, 1}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 7, 35, 722, 0, srs_resource_configuration::comb_size_enum(2), 1, 4, 60, 7, 0, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(0.438208, -0.554954), cf_t(0.597494, -0.378154), cf_t(0.426561, -0.563955), cf_t(-0.692481, -0.143072), cf_t(-0.578349, 0.406832), cf_t(0.594874, -0.382264), cf_t(-0.661298, 0.250369), cf_t(-0.147752, -0.691498), cf_t(-0.293085, 0.643507), cf_t(-0.593204, -0.384849), cf_t(0.583220, -0.399818), cf_t(0.303875, 0.638482), cf_t(-0.548680, -0.446039), cf_t(-0.350618, 0.614058), cf_t(0.691797, 0.146347), cf_t(-0.365792, 0.605141)}}, 4, 4}, 0, 0, {-0.000000163}}}, {"test_data/srs_estimator_test_input70.dat"}}, + {{{{1, 561, 3, 0}, {srs_resource_configuration::one_two_four_enum(4), srs_resource_configuration::one_two_four_enum(4), 0, 15, 565, 0, srs_resource_configuration::comb_size_enum(4), 0, 11, 55, 3, 3, srs_resource_configuration::group_or_sequence_hopping_enum::neither, {}}, {0, 1, 2, 3}}, {{{{cf_t(-0.096280, -0.700521), cf_t(0.707087, 0.005324), cf_t(-0.681382, -0.188994), cf_t(0.051253, -0.705247), cf_t(0.317802, 0.631666), cf_t(-0.286603, 0.646420), cf_t(-0.655865, 0.264276), cf_t(0.055072, -0.704959), cf_t(-0.451996, 0.543783), cf_t(-0.220126, -0.671971), cf_t(-0.164757, 0.687645), cf_t(-0.627893, -0.325193), cf_t(0.265294, 0.655453), cf_t(-0.499198, -0.500801), cf_t(-0.707068, -0.007371), cf_t(-0.010383, -0.707031)}}, 4, 4}, 0, 0, {0.000000076}}}, {"test_data/srs_estimator_test_input71.dat"}}, // clang-format on }; diff --git a/tests/unittests/phy/upper/signal_processors/srs/srs_estimator_vectortest.cpp b/tests/unittests/phy/upper/signal_processors/srs/srs_estimator_vectortest.cpp index d83c1d14a6..8ebd65c885 100644 --- a/tests/unittests/phy/upper/signal_processors/srs/srs_estimator_vectortest.cpp +++ b/tests/unittests/phy/upper/signal_processors/srs/srs_estimator_vectortest.cpp @@ -100,6 +100,9 @@ TEST_P(srsEstimatorFixture, FromVector) srs_estimator_result result = estimator->estimate(grid, config); ASSERT_EQ(test_case.context.result.channel_matrix.normalize(), result.channel_matrix.normalize()); + ASSERT_TRUE(result.epre_dB.has_value()); + ASSERT_NEAR( + convert_dB_to_power(test_case.context.result.epre_dB.value()), convert_dB_to_power(result.epre_dB.value()), 5e-3); ASSERT_NEAR(test_case.context.result.time_alignment.time_alignment, result.time_alignment.time_alignment, 1e-7); } diff --git a/tests/unittests/ran/pusch/pusch_tpmi_select_test.cpp b/tests/unittests/ran/pusch/pusch_tpmi_select_test.cpp index 83daef45eb..92e53c63b7 100644 --- a/tests/unittests/ran/pusch/pusch_tpmi_select_test.cpp +++ b/tests/unittests/ran/pusch/pusch_tpmi_select_test.cpp @@ -131,8 +131,8 @@ TEST_P(PuschTpmiSelectFixture, VectorTest) } // Get UL-SCH information parameters. - pusch_tpmi_select_info info = - get_tpmi_select_info(test_case.channel_matrix, test_case.noise_variance, test_case.codebook_subset); + pusch_tpmi_select_info info = get_tpmi_select_info( + test_case.channel_matrix, test_case.noise_variance, max_nof_layers, test_case.codebook_subset); // Compare with expected. ASSERT_EQ(info, test_case.info); diff --git a/tests/unittests/rlc/rlc_rx_am_test.cpp b/tests/unittests/rlc/rlc_rx_am_test.cpp index b54e7365f6..14f85d018a 100644 --- a/tests/unittests/rlc/rlc_rx_am_test.cpp +++ b/tests/unittests/rlc/rlc_rx_am_test.cpp @@ -23,6 +23,7 @@ #include "lib/rlc/rlc_rx_am_entity.h" #include "tests/test_doubles/pdcp/pdcp_pdu_generator.h" #include "srsran/support/executors/manual_task_worker.h" +#include "srsran/support/test_utils.h" #include #include #include @@ -81,6 +82,21 @@ class rlc_rx_am_test_frame : public rlc_rx_upper_layer_data_notifier, void report_metrics(const rlc_metrics& metrics) override {} }; +srsran::log_sink_spy& test_spy = []() -> srsran::log_sink_spy& { + if (!srslog::install_custom_sink( + srsran::log_sink_spy::name(), + std::unique_ptr(new srsran::log_sink_spy(srslog::get_default_log_formatter())))) { + report_fatal_error("Unable to create logger spy"); + } + auto* spy = static_cast(srslog::find_sink(srsran::log_sink_spy::name())); + if (spy == nullptr) { + report_fatal_error("Unable to create logger spy"); + } + + srslog::fetch_basic_logger("RLC", *spy, true); + return *spy; +}(); + /// Fixture class for RLC AM Rx tests. /// It requires TEST_P() and INSTANTIATE_TEST_SUITE_P() to create/spawn tests for each config class rlc_rx_am_test : public ::testing::Test, public ::testing::WithParamInterface @@ -92,6 +108,9 @@ class rlc_rx_am_test : public ::testing::Test, public ::testing::WithParamInterf srslog::init(); logger.set_level(srslog::basic_levels::debug); + // reset log spy + test_spy.reset_counters(); + // init RLC logger srslog::fetch_basic_logger("RLC", false).set_level(srslog::basic_levels::debug); srslog::fetch_basic_logger("RLC", false).set_hex_dump_max_size(100); @@ -412,6 +431,14 @@ class rlc_rx_am_test : public ::testing::Test, public ::testing::WithParamInterf tester->sdu_queue.pop(); } + void tick_all(uint32_t ticks) + { + for (uint i = 0; i < ticks; ++i) { + timers.tick(); + ue_worker.run_pending_tasks(); + } + } + void tick() { timers.tick(); @@ -436,6 +463,9 @@ class rlc_rx_am_test_with_limit : public rlc_rx_am_test TEST_P(rlc_rx_am_test, create_new_entity) { EXPECT_NE(rlc, nullptr); + // No warnings or error during construction + EXPECT_EQ(test_spy.get_warning_counter(), 0); + EXPECT_EQ(test_spy.get_error_counter(), 0); } /// Verify the status report from a freshly created instance @@ -656,7 +686,7 @@ TEST_P(rlc_rx_am_test, rx_polling_bit_sn_outside_rx_window) /// Verify proper handling of polling bit for PDU duplicates inside the Rx window: The status-required state shall still /// change but the duplicated SDU shall be discarded -TEST_P(rlc_rx_am_test, rx_polling_bit_sdu_duplicate) +TEST_P(rlc_rx_am_test, rx_sdu_duplicate_with_one_polling_bit) { EXPECT_FALSE(rlc->status_report_required()); @@ -692,12 +722,65 @@ TEST_P(rlc_rx_am_test, rx_polling_bit_sdu_duplicate) // Check if polling bit was considered, despite duplicate SN EXPECT_TRUE(rlc->status_report_required()); + auto& status = rlc->get_status_pdu(); + EXPECT_EQ(status.ack_sn, 0); EXPECT_EQ(tester->status_trigger_counter, 1); // Check if duplicate SDU was properly ignored ASSERT_EQ(tester->sdu_queue.size(), 0); } +/// Verify proper handling of polling bit for PDU duplicates inside the Rx window: The status-required state shall still +/// change but the duplicated SDU shall be discarded +TEST_P(rlc_rx_am_test, rx_sdu_duplicate_two_polling_bits) +{ + EXPECT_FALSE(rlc->status_report_required()); + + uint32_t sn_state = 0; // one SDU inside rx window and duplicate will be outside. + uint32_t sdu_size = 4; + + // Create SDU and PDU with full SDU + std::list> pdu_list = {}; + byte_buffer sdu; + ASSERT_NO_FATAL_FAILURE(create_pdus(pdu_list, sdu, sn_state, sdu_size, sdu_size, sn_state)); + sn_state++; + + // Set polling bit of PDUs + *(pdu_list.front().begin()) |= 0b01000000; // set P = 1; + + // Push into RLC + byte_buffer_slice pdu = byte_buffer_slice::create(pdu_list.front()).value(); + rlc->handle_pdu(std::move(pdu)); + + // Check if polling bit has not changed + EXPECT_TRUE(rlc->status_report_required()); + auto& status1 = rlc->get_status_pdu(); + EXPECT_EQ(status1.ack_sn, 1); + EXPECT_EQ(tester->status_trigger_counter, 1); + + // Check if SDU was properly unpacked and forwarded + ASSERT_EQ(tester->sdu_queue.size(), 1); + EXPECT_EQ(tester->sdu_queue.front().length(), sdu_size); + EXPECT_EQ(tester->sdu_queue.front(), sdu); + tester->sdu_queue.pop(); + + // Let t-StatusProhibit expire + tick_all(config.t_status_prohibit + 1); + + // Push into RLC + pdu = byte_buffer_slice::create(pdu_list.front()).value(); + rlc->handle_pdu(std::move(pdu)); + + // Check if polling bit was considered, despite duplicate SN + EXPECT_TRUE(rlc->status_report_required()); + auto& status2 = rlc->get_status_pdu(); + EXPECT_EQ(status2.ack_sn, 1); // Check the status report is not stale. + EXPECT_EQ(tester->status_trigger_counter, 2); + + // Check if duplicate SDU was properly ignored + ASSERT_EQ(tester->sdu_queue.size(), 0); +} + /// Verify handling of PDUs with SDU segment duplicates: /// - Receive SDU in segments, but loose one segment. /// - Receive all segments again, without any further loss: diff --git a/tests/unittests/rlc/rlc_tx_am_test.cpp b/tests/unittests/rlc/rlc_tx_am_test.cpp index ee379aca2b..82bae32276 100644 --- a/tests/unittests/rlc/rlc_tx_am_test.cpp +++ b/tests/unittests/rlc/rlc_tx_am_test.cpp @@ -24,6 +24,7 @@ #include "tests/test_doubles/pdcp/pdcp_pdu_generator.h" #include "srsran/ran/pdsch/pdsch_constants.h" #include "srsran/support/executors/manual_task_worker.h" +#include "srsran/support/rtsan.h" #include #include @@ -55,6 +56,7 @@ class rlc_tx_am_test_frame : public rlc_tx_upper_layer_data_notifier, // rlc_tx_upper_layer_data_notifier interface void on_transmitted_sdu(uint32_t max_tx_pdcp_sn, uint32_t desired_buf_size) override { + SRSRAN_RTSAN_SCOPED_DISABLER(d); // store in list highest_transmitted_pdcp_sn_list.push_back(max_tx_pdcp_sn); desired_buf_size_list.push_back(desired_buf_size); @@ -62,18 +64,21 @@ class rlc_tx_am_test_frame : public rlc_tx_upper_layer_data_notifier, void on_delivered_sdu(uint32_t max_deliv_pdcp_sn) override { + SRSRAN_RTSAN_SCOPED_DISABLER(d); // store in list highest_delivered_pdcp_sn_list.push_back(max_deliv_pdcp_sn); } void on_retransmitted_sdu(uint32_t max_retx_pdcp_sn) override { + SRSRAN_RTSAN_SCOPED_DISABLER(d); // store in list highest_retransmitted_pdcp_sn_list.push_back(max_retx_pdcp_sn); } void on_delivered_retransmitted_sdu(uint32_t max_deliv_retx_pdcp_sn) override { + SRSRAN_RTSAN_SCOPED_DISABLER(d); // store in list highest_delivered_retransmitted_pdcp_sn_list.push_back(max_deliv_retx_pdcp_sn); } diff --git a/tests/unittests/scheduler/slicing/slice_scheduler_test.cpp b/tests/unittests/scheduler/slicing/slice_scheduler_test.cpp index f4be00e47f..ef9654c7c1 100644 --- a/tests/unittests/scheduler/slicing/slice_scheduler_test.cpp +++ b/tests/unittests/scheduler/slicing/slice_scheduler_test.cpp @@ -280,8 +280,9 @@ class rb_ratio_slice_scheduler_test : public slice_scheduler_test, public ::test static constexpr ran_slice_id_t drb2_slice_id{3}; rb_ratio_slice_scheduler_test() : - slice_scheduler_test({{{plmn_identity::test_value(), s_nssai_t{1}}, MIN_SLICE_RB, MAX_SLICE_RB}, - {{plmn_identity::test_value(), s_nssai_t{2}}, MIN_SLICE_RB, MAX_SLICE_RB}}) + slice_scheduler_test( + {{{plmn_identity::test_value(), s_nssai_t{slice_service_type{1}}}, MIN_SLICE_RB, MAX_SLICE_RB}, + {{plmn_identity::test_value(), s_nssai_t{slice_service_type{2}}}, MIN_SLICE_RB, MAX_SLICE_RB}}) { } @@ -295,7 +296,7 @@ class rb_ratio_slice_scheduler_test : public slice_scheduler_test, public ::test req.starts_in_fallback = false; if (lc_cfgs.size() == 0) { (*req.cfg.lc_config_list)[drb1_idx].rrm_policy.plmn_id = plmn_identity::test_value(); - (*req.cfg.lc_config_list)[drb1_idx].rrm_policy.s_nssai = s_nssai_t{1}; + (*req.cfg.lc_config_list)[drb1_idx].rrm_policy.s_nssai = s_nssai_t{slice_service_type{1}}; } else { *req.cfg.lc_config_list = lc_cfgs; } diff --git a/tests/unittests/srsvec/CMakeLists.txt b/tests/unittests/srsvec/CMakeLists.txt index fc2bce855b..03af31f7ac 100644 --- a/tests/unittests/srsvec/CMakeLists.txt +++ b/tests/unittests/srsvec/CMakeLists.txt @@ -22,10 +22,6 @@ add_executable(srsvec_add_test srsvec_add_test.cpp) target_link_libraries(srsvec_add_test srsvec srslog) add_test(srsvec_add_test srsvec_add_test) -add_executable(srsvec_aligned_test srsvec_aligned_test.cpp) -target_link_libraries(srsvec_aligned_test srsvec srslog) -add_test(srsvec_aligned_test srsvec_aligned_test) - add_executable(srsvec_bit_test srsvec_bit_test.cpp) target_link_libraries(srsvec_bit_test srsvec srslog gtest gtest_main) add_test(srsvec_bit_test srsvec_bit_test) diff --git a/tests/unittests/srsvec/srsvec_add_test.cpp b/tests/unittests/srsvec/srsvec_add_test.cpp index bb4c36752b..51851072de 100644 --- a/tests/unittests/srsvec/srsvec_add_test.cpp +++ b/tests/unittests/srsvec/srsvec_add_test.cpp @@ -21,7 +21,6 @@ */ #include "srsran/srsvec/add.h" -#include "srsran/srsvec/aligned_vec.h" #include "srsran/support/srsran_test.h" #include @@ -35,17 +34,17 @@ void test_cf_add(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); for (cf_t& v : x) { v = cf_t(dist(rgen), dist(rgen)); } - srsvec::aligned_vec y(N); + std::vector y(N); for (cf_t& v : y) { v = cf_t(dist(rgen), dist(rgen)); } - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::add(x, y, z); @@ -60,17 +59,17 @@ void test_float_add(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); for (float& v : x) { v = dist(rgen); } - srsvec::aligned_vec y(N); + std::vector y(N); for (float& v : y) { v = dist(rgen); } - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::add(x, y, z); @@ -85,17 +84,17 @@ void test_i16_add(std::size_t N) { std::uniform_int_distribution dist(INT16_MIN / 2, INT16_MAX / 2); - srsvec::aligned_vec x(N); + std::vector x(N); for (int16_t& v : x) { v = dist(rgen); } - srsvec::aligned_vec y(N); + std::vector y(N); for (int16_t& v : y) { v = dist(rgen); } - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::add(x, y, z); @@ -110,17 +109,17 @@ void test_i8_add(std::size_t N) { std::uniform_int_distribution dist(INT8_MIN / 2, INT8_MAX / 2); - srsvec::aligned_vec x(N); + std::vector x(N); for (int8_t& v : x) { v = dist(rgen); } - srsvec::aligned_vec y(N); + std::vector y(N); for (int8_t& v : y) { v = dist(rgen); } - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::add(x, y, z); diff --git a/tests/unittests/srsvec/srsvec_aligned_test.cpp b/tests/unittests/srsvec/srsvec_aligned_test.cpp deleted file mode 100644 index 6c3e7bc421..0000000000 --- a/tests/unittests/srsvec/srsvec_aligned_test.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsran/srsvec/aligned_vec.h" -#include "srsran/srsvec/copy.h" -#include "srsran/srsvec/zero.h" -#include "srsran/support/srsran_test.h" -#include - -using namespace srsran; - -template -void test_resize(std::size_t N) -{ - // Allocate - srsvec::aligned_vec x(N); - - // Resize - x.resize(2 * N); -} - -template -void test_zero(std::size_t N) -{ - // Allocate - srsvec::aligned_vec x(N); - - // Zero - srsvec::zero(x); - - // Verify copy - for (size_t i = 0; i != N; i++) { - TESTASSERT_EQ(x[i], 0); - } -} - -template -void test_copy(std::size_t N) -{ - // Allocate - srsvec::aligned_vec x(N); - - // Fill vector - for (std::size_t i = 0; i != N; i++) { - x[i] = i; - } - - // Allocate and resize - srsvec::aligned_vec y(N); - - // Copy - srsvec::copy(y, x); - - // Verify copy - for (size_t i = 0; i != N; i++) { - TESTASSERT_EQ(x[i], y[i]); - } -} - -int main() -{ - std::vector sizes = {1, 5, 7, 19, 23, 257}; - - for (std::size_t N : sizes) { - test_resize(N); - test_resize(N); - test_resize(N); - - test_zero(N); - test_zero(N); - test_zero(N); - - test_copy(N); - test_copy(N); - test_copy(N); - } -} \ No newline at end of file diff --git a/tests/unittests/srsvec/srsvec_binary_test.cpp b/tests/unittests/srsvec/srsvec_binary_test.cpp index a752094f60..43b79b5e3a 100644 --- a/tests/unittests/srsvec/srsvec_binary_test.cpp +++ b/tests/unittests/srsvec/srsvec_binary_test.cpp @@ -20,7 +20,6 @@ * */ -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/binary.h" #include "srsran/support/srsran_test.h" #include @@ -34,17 +33,17 @@ void test_binary_xor(std::size_t N) { std::uniform_int_distribution dist(0, RANGE); - srsvec::aligned_vec x(N); + std::vector x(N); for (T& v : x) { v = dist(rgen); } - srsvec::aligned_vec y(N); + std::vector y(N); for (T& v : y) { v = dist(rgen); } - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::binary_xor(x, y, z); @@ -59,17 +58,17 @@ void test_binary_and(std::size_t N) { std::uniform_int_distribution dist(0, RANGE); - srsvec::aligned_vec x(N); + std::vector x(N); for (T& v : x) { v = dist(rgen); } - srsvec::aligned_vec y(N); + std::vector y(N); for (T& v : y) { v = dist(rgen); } - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::binary_and(x, y, z); @@ -84,17 +83,17 @@ void test_binary_or(std::size_t N) { std::uniform_int_distribution dist(0, RANGE); - srsvec::aligned_vec x(N); + std::vector x(N); for (T& v : x) { v = dist(rgen); } - srsvec::aligned_vec y(N); + std::vector y(N); for (T& v : y) { v = dist(rgen); } - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::binary_or(x, y, z); diff --git a/tests/unittests/srsvec/srsvec_bit_test.cpp b/tests/unittests/srsvec/srsvec_bit_test.cpp index c6d10d12b3..55c24e4e0a 100644 --- a/tests/unittests/srsvec/srsvec_bit_test.cpp +++ b/tests/unittests/srsvec/srsvec_bit_test.cpp @@ -20,7 +20,6 @@ * */ -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/bit.h" #include #include @@ -58,7 +57,7 @@ TEST_P(SrsvecBitFixture, SrsvecBitTestUnpack) unsigned value = dist(rgen); // Create destination - srsvec::aligned_vec unpacked(size); + std::vector unpacked(size); // Unpack span bit_buf = srsvec::bit_unpack(unpacked, value, size); @@ -85,10 +84,10 @@ TEST_P(SrsvecBitFixture, SrsvecBitTestUnpackVector) } // Create destination - srsvec::aligned_vec unpacked(nbits); + std::vector unpacked(nbits); // Generate expected values. - srsvec::aligned_vec expected(nbits); + std::vector expected(nbits); std::generate(expected.begin(), expected.end(), [&, index = 0]() mutable { return packed.extract(index++, 1); }); // Unpack @@ -103,7 +102,7 @@ TEST_P(SrsvecBitFixture, SrsvecBitTestPack) std::uniform_int_distribution dist(0, 1U); // Create unpacked data - srsvec::aligned_vec unpacked(size); + std::vector unpacked(size); for (uint8_t& value : unpacked) { value = dist(rgen); } @@ -128,7 +127,7 @@ TEST_P(SrsvecBitFixture, SrsvecBitTestPackVector) std::uniform_int_distribution dist(0, 1U); // Create unpacked data - srsvec::aligned_vec unpacked(nbits); + std::vector unpacked(nbits); for (uint8_t& value : unpacked) { value = dist(rgen); } @@ -151,7 +150,7 @@ TEST_P(SrsvecBitFixture, SrsvecBitTestPackFullVector) std::uniform_int_distribution dist(0, 1U); // Create unpacked data - srsvec::aligned_vec unpacked(size); + std::vector unpacked(size); for (uint8_t& value : unpacked) { value = dist(rgen); } @@ -169,7 +168,7 @@ TEST_P(SrsvecBitFixture, SrsvecBitTestPackOffsetVector) std::uniform_int_distribution dist(0, 1U); // Create unpacked data - srsvec::aligned_vec unpacked(nbits); + std::vector unpacked(nbits); for (uint8_t& value : unpacked) { value = dist(rgen); } @@ -277,10 +276,10 @@ TEST_P(SrsvecBitFixture, SrsvecBitTestUnpackVectorWithRemainder) } // Create destination - srsvec::aligned_vec unpacked(15); + std::vector unpacked(15); // Generate expected values. - srsvec::aligned_vec expected(15); + std::vector expected(15); std::generate(expected.begin(), expected.end(), [&, index = 0]() mutable { return packed.extract(index++, 1); }); // Unpack @@ -302,10 +301,10 @@ TEST_P(SrsvecBitFixture, SrsvecBitTestUnpackVectorOffset) } // Create destination - srsvec::aligned_vec unpacked(nbits - offset); + std::vector unpacked(nbits - offset); // Generate expected values. - srsvec::aligned_vec expected(nbits - offset); + std::vector expected(nbits - offset); std::generate(expected.begin(), expected.end(), [&, index = offset]() mutable { return packed.extract(index++, 1); }); // Unpack diff --git a/tests/unittests/srsvec/srsvec_clipping_test.cpp b/tests/unittests/srsvec/srsvec_clipping_test.cpp index 9ad97f26eb..ec3d3df3b8 100644 --- a/tests/unittests/srsvec/srsvec_clipping_test.cpp +++ b/tests/unittests/srsvec/srsvec_clipping_test.cpp @@ -23,7 +23,6 @@ /// \file /// \brief Unit test for the clipping functions in the \c srsvec vector library. -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/clip.h" #include "srsran/support/srsran_test.h" #include @@ -33,9 +32,9 @@ static const float ASSERT_MAX_ERROR = 1e-6; using namespace srsran; -static srsvec::aligned_vec input; -static srsvec::aligned_vec output; -static srsvec::aligned_vec output_gold; +static std::vector input; +static std::vector output; +static std::vector output_gold; static void setup(std::size_t nof_samples, const float max_amplitude) { diff --git a/tests/unittests/srsvec/srsvec_compare_test.cpp b/tests/unittests/srsvec/srsvec_compare_test.cpp index 3c9338e7c0..2793c99d23 100644 --- a/tests/unittests/srsvec/srsvec_compare_test.cpp +++ b/tests/unittests/srsvec/srsvec_compare_test.cpp @@ -20,7 +20,6 @@ * */ -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/compare.h" #include "srsran/support/math/math_utils.h" #include "srsran/support/srsran_test.h" @@ -34,14 +33,14 @@ static void test_max_abs_ccc(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); for (cf_t& v : x) { v = {dist(rgen), dist(rgen)}; } std::pair result = srsvec::max_abs_element(x); - cf_t* expected_it = std::max_element(x.begin(), x.end(), [](cf_t a, cf_t b) { return abs_sq(a) < abs_sq(b); }); + auto expected_it = std::max_element(x.begin(), x.end(), [](cf_t a, cf_t b) { return abs_sq(a) < abs_sq(b); }); unsigned expected_max_index = static_cast(expected_it - x.begin()); float expected_max_value = abs_sq(*expected_it); @@ -53,7 +52,7 @@ static void test_max_abs_ccc_same(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); std::fill(x.begin(), x.end(), 0); std::pair result = srsvec::max_abs_element(x); @@ -66,14 +65,14 @@ static void test_max_f(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); for (float& v : x) { v = dist(rgen); } std::pair result = srsvec::max_element(x); - float* expected_it = std::max_element(x.begin(), x.end()); + auto expected_it = std::max_element(x.begin(), x.end()); unsigned expected_max_index = static_cast(expected_it - x.begin()); float expected_max_value = *expected_it; @@ -83,7 +82,7 @@ static void test_max_f(std::size_t N) static void test_max_f_same(std::size_t N) { - srsvec::aligned_vec x(N); + std::vector x(N); std::fill(x.begin(), x.end(), 0); std::pair result = srsvec::max_element(x); @@ -97,7 +96,7 @@ static void test_count_if_part_abs_greater_than(std::size_t N) float threshold = 0.5; std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); std::generate(x.begin(), x.end(), [&dist]() { return cf_t{dist(rgen), dist(rgen)}; }); unsigned result = srsvec::count_if_part_abs_greater_than(x, threshold); diff --git a/tests/unittests/srsvec/srsvec_convert_test.cpp b/tests/unittests/srsvec/srsvec_convert_test.cpp index f29190fa4b..6bdcb7075d 100644 --- a/tests/unittests/srsvec/srsvec_convert_test.cpp +++ b/tests/unittests/srsvec/srsvec_convert_test.cpp @@ -20,7 +20,6 @@ * */ -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/conversion.h" #include "srsran/support/srsran_test.h" #include @@ -66,12 +65,12 @@ TEST_P(SrsvecConvertFixture, SrsvecConvertTestComplexInt16) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(size); + std::vector x(size); for (cf_t& v : x) { v = cf_t(dist(rgen), dist(rgen)); } - srsvec::aligned_vec z(2 * size); + std::vector z(2 * size); float scale = 1000.0F; @@ -89,12 +88,12 @@ TEST_P(SrsvecConvertFixture, SrsvecConvertTestInt16Complex) { std::uniform_int_distribution dist(INT16_MIN, INT16_MAX); - srsvec::aligned_vec x(2 * size); + std::vector x(2 * size); for (int16_t& v : x) { v = dist(rgen); } - srsvec::aligned_vec z(size); + std::vector z(size); float scale = 1000.0F; @@ -111,12 +110,12 @@ TEST_P(SrsvecConvertFixture, SrsvecConvertTestFloatInt16) { std::uniform_real_distribution dist(-1, 1); - srsvec::aligned_vec x(size); + std::vector x(size); for (float& v : x) { v = dist(rgen); } - srsvec::aligned_vec z(size); + std::vector z(size); float scale = 1000.0F; @@ -132,12 +131,12 @@ TEST_P(SrsvecConvertFixture, SrsvecConvertTestInt16Float) { std::uniform_int_distribution dist(INT16_MIN, INT16_MAX); - srsvec::aligned_vec x(size); + std::vector x(size); for (int16_t& v : x) { v = dist(rgen); } - srsvec::aligned_vec z(size); + std::vector z(size); float scale = 1000.0F; @@ -155,11 +154,11 @@ TEST_P(SrsvecConvertFixture, SrsvecConvertTestComplexComplex16Random) static constexpr float range = std::numeric_limits::max() / 2; std::uniform_real_distribution dist(-range, range); - srsvec::aligned_vec in(size); + std::vector in(size); std::generate(in.begin(), in.end(), [&dist]() { return cf_t(dist(rgen), dist(rgen)); }); - srsvec::aligned_vec out(size); - srsvec::aligned_vec data_cbf16(size); + std::vector out(size); + std::vector data_cbf16(size); // Convert from single precission to BF16. srsvec::convert(data_cbf16, in); @@ -190,14 +189,14 @@ TEST_P(SrsvecConvertFixture, SrsvecConvertTestComplexComplex16Special) static const std::vector values = {nan, infinity, neg_infinity, one_round_down, one_round_up}; static const std::vector expected_values = {0x7fc0, 0x7f80, 0xff80, 0x3f80, 0x3f82}; - srsvec::aligned_vec in(size); + std::vector in(size); std::generate(in.begin(), in.end(), [n = 0]() mutable { float re = values[(n++) % values.size()]; float im = values[(n++) % values.size()]; return cf_t(re, im); }); - srsvec::aligned_vec data_cbf16(size); + std::vector data_cbf16(size); // Convert from single precission to BF16. srsvec::convert(data_cbf16, in); @@ -215,11 +214,11 @@ TEST_P(SrsvecConvertFixture, SrsvecConvertTestFloatFloat16Random) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec in(size); + std::vector in(size); std::generate(in.begin(), in.end(), [&dist]() { return dist(rgen); }); // Convert from single precision to brain float. - srsvec::aligned_vec data_bf16(size); + std::vector data_bf16(size); srsvec::convert(data_bf16, in); // Assert conversion to BF16. @@ -228,7 +227,7 @@ TEST_P(SrsvecConvertFixture, SrsvecConvertTestFloatFloat16Random) } // Convert back to single precision float. - srsvec::aligned_vec out(size); + std::vector out(size); srsvec::convert(out, data_bf16); // Assert conversion from BF16. @@ -244,15 +243,15 @@ TEST_P(SrsvecConvertFixture, SrsvecConvertTestInt16Float16Random) float int16_scale = (1 << 15) - 1; - srsvec::aligned_vec in(size); + std::vector in(size); std::generate(in.begin(), in.end(), [&dist]() { return dist(rgen); }); // Convert from single precision to int16. - srsvec::aligned_vec in_int16(size); + std::vector in_int16(size); srsvec::convert(in, int16_scale, in_int16); // Convert from int16 to brain float. - srsvec::aligned_vec data_bf16(size); + std::vector data_bf16(size); srsvec::convert(data_bf16, in_int16, int16_scale); // Assert conversion to BF16. @@ -261,7 +260,7 @@ TEST_P(SrsvecConvertFixture, SrsvecConvertTestInt16Float16Random) } // Convert from brain float back to int16. - srsvec::aligned_vec out_int16(size); + std::vector out_int16(size); srsvec::convert(out_int16, data_bf16, int16_scale); // Assert conversion from BF16. @@ -270,7 +269,7 @@ TEST_P(SrsvecConvertFixture, SrsvecConvertTestInt16Float16Random) } // Convert int16 to float and compare with original data. - srsvec::aligned_vec out(size); + std::vector out(size); srsvec::convert(out_int16, int16_scale, out); for (size_t i = 0; i != size; ++i) { @@ -288,14 +287,14 @@ TEST_P(SrsvecConvertFixture, SrsvecConvertTestScaledInt16ComplexFloat16Random) const unsigned size_i16 = size * 2; - srsvec::aligned_vec in(size_i16); - srsvec::aligned_vec gain(size_i16); + std::vector in(size_i16); + std::vector gain(size_i16); std::generate(in.begin(), in.end(), [&dist_i]() { return dist_i(rgen); }); std::generate(gain.begin(), gain.end(), [&dist_f]() { return int16_gain * float(dist_f(rgen)); }); // Convert from int16 to brain float. - srsvec::aligned_vec data_cbf16(size); + std::vector data_cbf16(size); srsvec::convert(data_cbf16, in, gain); // Assert conversion to cbf16. diff --git a/tests/unittests/srsvec/srsvec_division_test.cpp b/tests/unittests/srsvec/srsvec_division_test.cpp index ab584b607c..ae58b1591c 100644 --- a/tests/unittests/srsvec/srsvec_division_test.cpp +++ b/tests/unittests/srsvec/srsvec_division_test.cpp @@ -20,7 +20,6 @@ * */ -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/division.h" #include "srsran/support/srsran_test.h" #include @@ -34,17 +33,17 @@ void test_divide_fff(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec num(N); + std::vector num(N); for (float& v : num) { v = dist(rgen); } - srsvec::aligned_vec den(N); + std::vector den(N); for (float& v : den) { v = dist(rgen); } - srsvec::aligned_vec result(N); + std::vector result(N); srsvec::divide(result, num, den); diff --git a/tests/unittests/srsvec/srsvec_dot_prod_test.cpp b/tests/unittests/srsvec/srsvec_dot_prod_test.cpp index 59f5c81484..ebf5040eb2 100644 --- a/tests/unittests/srsvec/srsvec_dot_prod_test.cpp +++ b/tests/unittests/srsvec/srsvec_dot_prod_test.cpp @@ -20,7 +20,6 @@ * */ -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/dot_prod.h" #include "srsran/support/srsran_test.h" #include @@ -34,12 +33,12 @@ static void test_dot_prod_ccc(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); for (cf_t& v : x) { v = {dist(rgen), dist(rgen)}; } - srsvec::aligned_vec y(N); + std::vector y(N); for (cf_t& v : y) { v = {dist(rgen), dist(rgen)}; } @@ -63,7 +62,7 @@ static void test_avg_power_cf(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); for (cf_t& v : x) { v = {dist(rgen), dist(rgen)}; } @@ -89,7 +88,7 @@ static void test_avg_power_cbf16(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); for (cbf16_t& v : x) { v = cbf16_t(dist(rgen), dist(rgen)); } diff --git a/tests/unittests/srsvec/srsvec_modulus_square_test.cpp b/tests/unittests/srsvec/srsvec_modulus_square_test.cpp index 3462135514..cb814edf9d 100644 --- a/tests/unittests/srsvec/srsvec_modulus_square_test.cpp +++ b/tests/unittests/srsvec/srsvec_modulus_square_test.cpp @@ -20,7 +20,6 @@ * */ -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/modulus_square.h" #include "srsran/support/math/math_utils.h" #include "srsran/support/srsran_test.h" @@ -35,12 +34,12 @@ void test_modulus_square(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec input(N); + std::vector input(N); for (cf_t& v : input) { v = {dist(rgen), dist(rgen)}; } - srsvec::aligned_vec result(N); + std::vector result(N); srsvec::modulus_square(result, input); diff --git a/tests/unittests/srsvec/srsvec_prod_test.cpp b/tests/unittests/srsvec/srsvec_prod_test.cpp index 4ac2b09328..6de10f45e1 100644 --- a/tests/unittests/srsvec/srsvec_prod_test.cpp +++ b/tests/unittests/srsvec/srsvec_prod_test.cpp @@ -20,7 +20,6 @@ * */ -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/prod.h" #include "srsran/support/srsran_test.h" #include @@ -34,17 +33,17 @@ void test_prod_ccc(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); for (cf_t& v : x) { v = {dist(rgen), dist(rgen)}; } - srsvec::aligned_vec y(N); + std::vector y(N); for (cf_t& v : y) { v = {dist(rgen), dist(rgen)}; } - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::prod(x, y, z); @@ -59,17 +58,17 @@ void test_prod_fff(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); for (float& v : x) { v = dist(rgen); } - srsvec::aligned_vec y(N); + std::vector y(N); for (float& v : y) { v = dist(rgen); } - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::prod(x, y, z); diff --git a/tests/unittests/srsvec/srsvec_sc_prod_test.cpp b/tests/unittests/srsvec/srsvec_sc_prod_test.cpp index ed4a917d8a..0c24cb5fd3 100644 --- a/tests/unittests/srsvec/srsvec_sc_prod_test.cpp +++ b/tests/unittests/srsvec/srsvec_sc_prod_test.cpp @@ -20,7 +20,6 @@ * */ -#include "srsran/srsvec/aligned_vec.h" #include "srsran/srsvec/sc_prod.h" #include "srsran/support/srsran_test.h" #include @@ -34,14 +33,14 @@ void test_sc_prod_ccc(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); for (cf_t& v : x) { v = {dist(rgen), dist(rgen)}; } cf_t h = {dist(rgen), dist(rgen)}; - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::sc_prod(x, h, z); @@ -56,16 +55,16 @@ void test_sc_prod_ccc_bf16(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); std::generate(x.begin(), x.end(), [&dist]() { return to_cbf16(cf_t{dist(rgen), dist(rgen)}); }); cf_t h = {dist(rgen), dist(rgen)}; - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::sc_prod(x, h, z); - srsvec::aligned_vec expected(N); + std::vector expected(N); std::transform(x.begin(), x.end(), expected.begin(), [&h](cbf16_t value) { return h * to_cf(value); }); for (size_t i = 0; i != N; i++) { @@ -79,14 +78,14 @@ void test_sc_prod_cfc(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); for (cf_t& v : x) { v = {dist(rgen), dist(rgen)}; } float h = dist(rgen); - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::sc_prod(x, h, z); @@ -101,14 +100,14 @@ void test_sc_prod_fff(std::size_t N) { std::uniform_real_distribution dist(-1.0, 1.0); - srsvec::aligned_vec x(N); + std::vector x(N); for (float& v : x) { v = dist(rgen); } float h = dist(rgen); - srsvec::aligned_vec z(N); + std::vector z(N); srsvec::sc_prod(x, h, z); diff --git a/tests/unittests/support/tracing/resource_usage_test.cpp b/tests/unittests/support/tracing/resource_usage_test.cpp index 97c7a1c166..86428c16af 100644 --- a/tests/unittests/support/tracing/resource_usage_test.cpp +++ b/tests/unittests/support/tracing/resource_usage_test.cpp @@ -41,7 +41,7 @@ TEST(resource_usage_test, resource_usage_diff_detects_sleep) usleep(10); resource_usage::snapshot point2 = resource_usage::now().value(); resource_usage::diff d = point2 - point1; - ASSERT_GE(d.vol_ctxt_switch_count, 1); + ASSERT_GE(d.vol_ctxt_switch_count + d.invol_ctxt_switch_count, 1); } TEST(resource_usage_test, format_resource_usage_diff) @@ -54,4 +54,4 @@ TEST(resource_usage_test, format_resource_usage_diff) std::string str = fmt::format("{}", d); ASSERT_GT(str.size(), 0); fmt::print("> result: {}\n", str); -} \ No newline at end of file +}