diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000..5495f55944 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,119 @@ +name: Docker Builder +on: + push: + branches: + - main + - test + release: + types: [published] + +jobs: + docker: + runs-on: ubuntu-latest + strategy: + matrix: + include: + # --> split72 + # AMD AVX2 + - SUFFIX: release_avx2 + SPLIT: "split72" + EXTRA_CMAKE_ARGS: -DAUTO_DETECT_ISA=Off + ARCH: x86-64-v3 + TAG: amd64-avx2 + PLATFORM: amd64 + LIB: dpdk + LIB_VERSION: "23.11" + - SUFFIX: release_with_debug_avx2 + SPLIT: "split72" + EXTRA_CMAKE_ARGS: -DAUTO_DETECT_ISA=Off -DFORCE_DEBUG_INFO=On + ARCH: x86-64-v3 + TAG: amd64-avx2 + PLATFORM: amd64 + LIB: dpdk + LIB_VERSION: "23.11" + # AMD AVX512 + - SUFFIX: release_avx512 + SPLIT: "split72" + EXTRA_CMAKE_ARGS: -DAUTO_DETECT_ISA=Off + ARCH: x86-64-v4 + TAG: amd64-avx2-avx512 + PLATFORM: amd64 + LIB: dpdk + LIB_VERSION: "23.11" + - SUFFIX: release_with_debug_avx512 + SPLIT: "split72" + EXTRA_CMAKE_ARGS: -DAUTO_DETECT_ISA=Off -DFORCE_DEBUG_INFO=On + ARCH: x86-64-v4 + TAG: amd64-avx2-avx512 + PLATFORM: amd64 + LIB: dpdk + LIB_VERSION: "23.11" + # --> split8 + # AMD AVX2 + - SUFFIX: release_avx2 + SPLIT: "split8" + EXTRA_CMAKE_ARGS: -DAUTO_DETECT_ISA=Off + ARCH: x86-64-v3 + TAG: amd64-avx2 + PLATFORM: amd64 + LIB: uhd + LIB_VERSION: "4.6.0.0" + - SUFFIX: release_with_debug_avx2 + SPLIT: "split8" + EXTRA_CMAKE_ARGS: -DAUTO_DETECT_ISA=Off -DFORCE_DEBUG_INFO=On + ARCH: x86-64-v3 + TAG: amd64-avx2 + PLATFORM: amd64 + LIB: uhd + LIB_VERSION: "4.6.0.0" + env: + NAME: srsran_${{ matrix.SPLIT }}_${{ matrix.SUFFIX }} + environment: dockerhub + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Determine tags based on branch + id: tags + run: | + BRANCH_NAME="${GITHUB_REF#refs/heads/}" + DATE_TAG="${GITHUB_SHA:0:10}-$(date +'%Y-%m-%d')" + RELEASE_NAME="${{ github.event.release.name }}" + + if [ -n "$RELEASE_NAME" ]; then + tags="${{ env.NAME }}:${DATE_TAG},${{ env.NAME }}:${RELEASE_NAME}" + else + if [ "$BRANCH_NAME" == "main" ]; then + tags="${{ env.NAME }}:${DATE_TAG},${{ env.NAME }}:latest" + elif [ "$BRANCH_NAME" == "test" ]; then + tags="${{ env.NAME }}:${DATE_TAG},${{ env.NAME }}:next" + fi + fi + echo "tags=$tags" >> $GITHUB_OUTPUT + + - name: Login to Docker Hub + uses: docker/login-action@v3.2.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + logout: true + ecr: false + + - name: Build and push + uses: docker/build-push-action@v6 + with: + push: true + tags: ${{ steps.tags.outputs.tags }} + file: ./docker/Dockerfile + platforms: ${{ matrix.PLATFORM }} + build-args: | + NAME="srsran_${SPLIT}_${SUFFIX}" + LIB=${{ matrix.LIB }} + LIB_VERSION=${{ matrix.LIB_VERSION }} + ARCH=${{ matrix.ARCH }} \ No newline at end of file diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index d5160c0f3b..6b7431e0c8 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -253,7 +253,7 @@ variables: valgrind) G_DEBUG=gc-friendly G_SLICE=always-malloc # Default timeout per test is 1500 (25 min) - export CTEST_TIMEOUT=2700 + export CTEST_TIMEOUT=3600 export CTEST_TEST_TIMEOUT=${CTEST_TIMEOUT} ctest_extra="-T memcheck -LE NO_MEMCHECK --timeout ${CTEST_TIMEOUT}" ;; @@ -1334,7 +1334,7 @@ debian 11 amd64 avx512: COMPILER: clang ENABLE_ASAN: "True" TEST_MODE: default - - OS: [ubuntu-24.04, ubuntu-23.10, ubuntu-22.04] + - OS: [ubuntu-24.04] SANITIZER: valgrind COMPILER: gcc TEST_MODE: valgrind diff --git a/.gitlab/ci/e2e.yml b/.gitlab/ci/e2e.yml index 4d992d58c1..a32e2b48e7 100644 --- a/.gitlab/ci/e2e.yml +++ b/.gitlab/ci/e2e.yml @@ -147,17 +147,19 @@ e2e request and config validation: echo "srsGNB sources+build size is" $(du -hs .) # Remove any existing retina resource for this group - retina-delete-orchestration-network --user-name ^ci_${GROUP} --regex - # Add extra secret env variables to the .env file + # Add extra config env variables to the .env file - | echo "" >> .gitlab/ci/e2e/.env - cat $RETINA_SECRET_ENV >> .gitlab/ci/e2e/.env + cat $RETINA_CONFIG_ENV >> .gitlab/ci/e2e/.env + + echo -e "\nGNB_BINARY_PATH=../../" >> .gitlab/ci/e2e/.env + echo -e "\nGNB_REMOTE_PATH=$CI_PROJECT_DIR" >> .gitlab/ci/e2e/.env + echo -e "\nis_executable=false" >> .gitlab/ci/e2e/.env + # Set username for retina - | cd tests/e2e export LOGNAME=ci_${GROUP}_${GITLAB_USER_LOGIN} - # Change sharing gnb file for complete src + build (With gnb) folder - - | - sed -i "s|- local_path: ../../build/apps/gnb/gnb|- local_path: ../../|; s|remote_path: /usr/local/bin|remote_path: $CI_PROJECT_DIR|; s|is_executable: true|is_executable: false|" ${CI_PROJECT_DIR}/.gitlab/ci/e2e/retina_request_${TESTBED}.yml # Run Retina - | E2E_CMD="retina-launcher ${RETINA_LAUNCHER_ARGS} --retina-request=${CI_PROJECT_DIR}/.gitlab/ci/e2e/retina_request_${TESTBED}.yml ${PYTEST_ARGS} -k '${KEYWORDS}' -m '${MARKERS}' --register-parameter ue.all.log_level=$E2E_LOG_LEVEL gnb.all.log_level=$E2E_LOG_LEVEL ${RETINA_PARAM_ARGS}" @@ -282,6 +284,33 @@ amari 4UE deb: - *txrx-lib - *retina-needs +amari 8UE: + extends: .zmq + variables: + MARKERS: "zmq and not smoke" + E2E_LOG_LEVEL: "info" + RETINA_PARAM_ARGS: "gnb.all.pcap=True gnb.all.rlc_enable=False gnb.all.enable_integrity_protection=True" + needs: + - job: "basic relwithdeb" + artifacts: true + - *txrx-lib + - *retina-needs + parallel: + matrix: + - KEYWORDS: + ["reestablishment and sequentially", "handover and sequentially"] + +amari 8UE beta: + extends: amari 8UE + parallel: + matrix: + - KEYWORDS: + [ + "reestablishment and not sequentially", + "handover and not sequentially", + ] + allow_failure: true + amari 32UE: extends: .zmq variables: @@ -305,13 +334,6 @@ amari 32UE: "iperf and tcp and not band:3", ] -amari 8UE beta: - extends: amari 32UE - parallel: - matrix: - - KEYWORDS: ["reestablishment", "handover"] - allow_failure: true - amari 32UE asan: extends: .zmq variables: diff --git a/.gitlab/ci/e2e/.env b/.gitlab/ci/e2e/.env index 22aaf718df..7e40079375 100644 --- a/.gitlab/ci/e2e/.env +++ b/.gitlab/ci/e2e/.env @@ -1,12 +1,16 @@ +GNB_REMOTE_PATH=/usr/local/bin/gnb +GNB_IS_EXECUTABLE=true SRSGNB_REGISTRY_URI=registry.gitlab.com/softwareradiosystems/srsgnb RETINA_REGISTRY_PREFIX=registry.gitlab.com/softwareradiosystems/ci/retina -RETINA_VERSION=0.49.18 +RETINA_VERSION=0.50.4 UBUNTU_VERSION=24.04 AMARISOFT_VERSION=2023-09-08 SRSUE_VERSION=23.11 OPEN5GS_VERSION=2.7.0 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -METRICS_SERVER_VERSION=1.7.2 +METRICS_SERVER_VERSION=1.7.3 DPDK_VERSION=23.11 ZMQ_HOSTLABEL_0=kubernetes.io/hostname=k8s-worker-vm2 ZMQ_HOSTLABEL_1=kubernetes.io/hostname=k8s-worker-vm2 +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_android_b200.yml b/.gitlab/ci/e2e/retina_request_android_b200.yml index b807218d07..894279c8bb 100644 --- a/.gitlab/ci/e2e/retina_request_android_b200.yml +++ b/.gitlab/ci/e2e/retina_request_android_b200.yml @@ -44,9 +44,9 @@ environment: - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - - local_path: ../../build/apps/gnb/gnb - remote_path: /usr/local/bin - is_executable: true + - local_path: ${GNB_BINARY_PATH} + remote_path: ${GNB_REMOTE_PATH} + is_executable: ${GNB_IS_EXECUTABLE} - name: open5gs type: 5gc diff --git a/.gitlab/ci/e2e/retina_request_android_n300.yml b/.gitlab/ci/e2e/retina_request_android_n300.yml index 28d6767177..8dfb117e5d 100644 --- a/.gitlab/ci/e2e/retina_request_android_n300.yml +++ b/.gitlab/ci/e2e/retina_request_android_n300.yml @@ -30,8 +30,8 @@ requirements: arch: amd64 cpu: - requests: 14 - limits: 14 + requests: 12 + limits: 12 memory: requests: "20G" limits: "20G" @@ -45,9 +45,9 @@ environment: - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - - local_path: ../../build/apps/gnb/gnb - remote_path: /usr/local/bin - is_executable: true + - local_path: ${GNB_BINARY_PATH} + remote_path: ${GNB_REMOTE_PATH} + is_executable: ${GNB_IS_EXECUTABLE} - name: open5gs type: 5gc diff --git a/.gitlab/ci/e2e/retina_request_android_x300.yml b/.gitlab/ci/e2e/retina_request_android_x300.yml index 25ab4cddcd..12cd8e3b7b 100644 --- a/.gitlab/ci/e2e/retina_request_android_x300.yml +++ b/.gitlab/ci/e2e/retina_request_android_x300.yml @@ -30,8 +30,8 @@ requirements: arch: amd64 cpu: - requests: 14 - limits: 14 + requests: 12 + limits: 12 memory: requests: "20G" limits: "20G" @@ -45,9 +45,9 @@ environment: - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - - local_path: ../../build/apps/gnb/gnb - remote_path: /usr/local/bin - is_executable: true + - local_path: ${GNB_BINARY_PATH} + remote_path: ${GNB_REMOTE_PATH} + is_executable: ${GNB_IS_EXECUTABLE} - name: open5gs type: 5gc diff --git a/.gitlab/ci/e2e/retina_request_rf_b200.yml b/.gitlab/ci/e2e/retina_request_rf_b200.yml index a03cf9f600..7d85400bfe 100644 --- a/.gitlab/ci/e2e/retina_request_rf_b200.yml +++ b/.gitlab/ci/e2e/retina_request_rf_b200.yml @@ -35,8 +35,8 @@ requirements: arch: amd64 cpu: - requests: 14 - limits: 14 + requests: 12 + limits: 12 memory: requests: "20G" limits: "20G" @@ -49,9 +49,9 @@ environment: - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - - local_path: ../../build/apps/gnb/gnb - remote_path: /usr/local/bin - is_executable: true + - local_path: ${GNB_BINARY_PATH} + remote_path: ${GNB_REMOTE_PATH} + is_executable: ${GNB_IS_EXECUTABLE} - name: open5gs type: 5gc diff --git a/.gitlab/ci/e2e/retina_request_test_mode.yml b/.gitlab/ci/e2e/retina_request_test_mode.yml index 9329d00e3e..b973546855 100644 --- a/.gitlab/ci/e2e/retina_request_test_mode.yml +++ b/.gitlab/ci/e2e/retina_request_test_mode.yml @@ -28,9 +28,9 @@ environment: - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - - local_path: ../../build/apps/gnb/gnb - remote_path: /usr/local/bin - is_executable: true + - local_path: ${GNB_BINARY_PATH} + remote_path: ${GNB_REMOTE_PATH} + is_executable: ${GNB_IS_EXECUTABLE} - name: open5gs type: 5gc diff --git a/.gitlab/ci/e2e/retina_request_viavi.yml b/.gitlab/ci/e2e/retina_request_viavi.yml index a5ca6d2cf9..13d36d3af9 100644 --- a/.gitlab/ci/e2e/retina_request_viavi.yml +++ b/.gitlab/ci/e2e/retina_request_viavi.yml @@ -33,9 +33,9 @@ - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb - LD_LIBRARY_PATH: /opt/dpdk/${DPDK_VERSION}/lib/x86_64-linux-gnu/ shared_files: - - local_path: ../../build/apps/gnb/gnb - remote_path: /usr/local/bin - is_executable: true + - local_path: ${GNB_BINARY_PATH} + remote_path: ${GNB_REMOTE_PATH} + is_executable: ${GNB_IS_EXECUTABLE} - name: metrics-server type: generic diff --git a/.gitlab/ci/e2e/retina_request_zmq.yml b/.gitlab/ci/e2e/retina_request_zmq.yml index a1b1f0e752..bcf0e0cc6d 100644 --- a/.gitlab/ci/e2e/retina_request_zmq.yml +++ b/.gitlab/ci/e2e/retina_request_zmq.yml @@ -28,7 +28,7 @@ - type: license model: amarisoft-5g shared_files: - - local_path: ../../build_trx_srsran/libtrx_srsran.so + - local_path: ${AMARISOFT_TXRX_BINARY_PATH} remote_path: /opt/lteue/trx_srsran.so is_executable: true @@ -53,9 +53,9 @@ environment: - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - - local_path: ../../build/apps/gnb/gnb - remote_path: /usr/local/bin - is_executable: true + - local_path: ${GNB_BINARY_PATH} + remote_path: ${GNB_REMOTE_PATH} + is_executable: ${GNB_IS_EXECUTABLE} - name: open5gs type: 5gc diff --git a/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml b/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml index c8bcfbe9b0..d3384737d9 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml @@ -29,7 +29,7 @@ - type: license model: amarisoft-5g shared_files: - - local_path: ../../build_trx_srsran/libtrx_srsran.so + - local_path: ${AMARISOFT_TXRX_BINARY_PATH} remote_path: /opt/lteue/trx_srsran.so is_executable: true @@ -56,9 +56,9 @@ environment: - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - - local_path: ../../build/apps/gnb/gnb - remote_path: /usr/local/bin - is_executable: true + - local_path: ${GNB_BINARY_PATH} + remote_path: ${GNB_REMOTE_PATH} + is_executable: ${GNB_IS_EXECUTABLE} - name: open5gs type: 5gc diff --git a/.gitlab/ci/e2e/retina_request_zmq_deb.yml b/.gitlab/ci/e2e/retina_request_zmq_deb.yml index 90a259e9ba..a7bb0b5d11 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_deb.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_deb.yml @@ -28,7 +28,7 @@ - type: license model: amarisoft-5g shared_files: - - local_path: ../../build_trx_srsran/libtrx_srsran.so + - local_path: ${AMARISOFT_TXRX_BINARY_PATH} remote_path: /opt/lteue/trx_srsran.so is_executable: true diff --git a/.gitlab/ci/e2e/retina_request_zmq_single_ue.yml b/.gitlab/ci/e2e/retina_request_zmq_single_ue.yml index 07a9fbf2c2..73b0bcb54e 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_single_ue.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_single_ue.yml @@ -27,7 +27,7 @@ - type: license model: amarisoft-5g shared_files: - - local_path: ../../build_trx_srsran/libtrx_srsran.so + - local_path: ${AMARISOFT_TXRX_BINARY_PATH} remote_path: /opt/lteue/trx_srsran.so is_executable: true @@ -52,9 +52,9 @@ environment: - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - - local_path: ../../build/apps/gnb/gnb - remote_path: /usr/local/bin - is_executable: true + - local_path: ${GNB_BINARY_PATH} + remote_path: ${GNB_REMOTE_PATH} + is_executable: ${GNB_IS_EXECUTABLE} - name: open5gs type: 5gc diff --git a/.gitlab/ci/e2e/retina_request_zmq_srsue.yml b/.gitlab/ci/e2e/retina_request_zmq_srsue.yml index 7b04862e11..68ed57d13c 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_srsue.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_srsue.yml @@ -46,9 +46,9 @@ environment: - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - - local_path: ../../build/apps/gnb/gnb - remote_path: /usr/local/bin - is_executable: true + - local_path: ${GNB_BINARY_PATH} + remote_path: ${GNB_REMOTE_PATH} + is_executable: ${GNB_IS_EXECUTABLE} - name: open5gs type: 5gc diff --git a/CMakeLists.txt b/CMakeLists.txt index 42fdc39e33..8fb7f68f60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,10 +105,6 @@ if (ENABLE_ASAN AND ENABLE_TSAN) message(FATAL_ERROR "ASAN and TSAN cannot be enabled at the same time.") endif () -if (ENABLE_EXPORT) - set(CMAKE_POSITION_INDEPENDENT_CODE ON) -endif (ENABLE_EXPORT) - # Hardware acceleration for both PUSCH and PDSCH is enabled by default when using DPDK. if (ENABLE_DPDK) # Make sure that library export is disabed, as it is not compatible with hardware acceleration. @@ -123,6 +119,32 @@ else (ENABLE_DPDK) unset(ENABLE_PUSCH_HWACC CACHE) endif (ENABLE_DPDK) +######################################################################## +# ENABLE_EXPORT +######################################################################## + +# ENABLE_EXPORT tells cmake to make some libaries available for other +# software to link to. If ON, the code must be compiled in PIC mode. +# We also add a dummy target that will depend on all the exported +# libraries to simplify their compilation. +if (ENABLE_EXPORT) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + add_custom_target(srsran_exported_libs) +endif (ENABLE_EXPORT) + +# Simple macro that tags libraries to be exported and adds them to the +# dependencies of the dummy target. +macro(ADD_TO_EXPORTED_LIBS) + if(ENABLE_EXPORT) + # Tag libraries. + install(TARGETS ${ARGV} EXPORT srsran_export) + # Make libraries dependencies of the srsran_exported_libs dummy + # target, which can be called to compile all exported libraries + # at once. + add_dependencies(srsran_exported_libs ${ARGV}) + endif(ENABLE_EXPORT) +endmacro(ADD_TO_EXPORTED_LIBS) + ######################################################################## # Install Dirs ######################################################################## diff --git a/apps/cu/CMakeLists.txt b/apps/cu/CMakeLists.txt index e5da7b156e..d601e26dfb 100644 --- a/apps/cu/CMakeLists.txt +++ b/apps/cu/CMakeLists.txt @@ -21,6 +21,7 @@ add_executable(srscu cu.cpp cu_appconfig_cli11_schema.cpp + cu_appconfig_validator.cpp cu_worker_manager.cpp ) diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index 20f76c83f1..126914e2db 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -73,6 +73,7 @@ #include "apps/services/application_tracer.h" #include "apps/services/stdin_command_dispatcher.h" #include "cu_appconfig.h" +#include "cu_appconfig_validator.h" #include #include @@ -131,23 +132,23 @@ static void initialize_log(const std::string& filename) srslog::init(); } -static void register_app_logs(const srs_cu::log_appconfig& log_cfg, +static void register_app_logs(const logger_appconfig& log_cfg, const cu_cp_unit_logger_config& cu_cp_loggers, const cu_up_unit_logger_config& cu_up_loggers) { // Set log-level of app and all non-layer specific components to app level. for (const auto& id : {"CU", "ALL", "SCTP-GW", "IO-EPOLL", "UDP-GW", "PCAP"}) { auto& logger = srslog::fetch_basic_logger(id, false); - logger.set_level(srslog::str_to_basic_level(log_cfg.lib_level)); + logger.set_level(log_cfg.lib_level); logger.set_hex_dump_max_size(log_cfg.hex_max_size); } auto& config_logger = srslog::fetch_basic_logger("CONFIG", false); - config_logger.set_level(srslog::str_to_basic_level(log_cfg.config_level)); + config_logger.set_level(log_cfg.config_level); config_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& metrics_logger = srslog::fetch_basic_logger("METRICS", false); - metrics_logger.set_level(srslog::str_to_basic_level(log_cfg.metrics_level)); + metrics_logger.set_level(log_cfg.metrics_level); metrics_logger.set_hex_dump_max_size(log_cfg.hex_max_size); // Register units logs. @@ -212,7 +213,8 @@ int main(int argc, char** argv) CLI11_PARSE(app, argc, argv); // Check the modified configuration. - if (!validate_cu_cp_unit_config(cu_cp_config) || !validate_cu_up_unit_config(cu_up_config)) { + if (!validate_cu_appconfig(cu_cfg) || !validate_cu_cp_unit_config(cu_cp_config) || + !validate_cu_up_unit_config(cu_up_config)) { report_error("Invalid configuration detected.\n"); } @@ -278,7 +280,7 @@ int main(int argc, char** argv) // Create F1-C GW (TODO cleanup port and PPID args with factory) sctp_network_gateway_config f1c_sctp_cfg = {}; f1c_sctp_cfg.if_name = "F1-C"; - f1c_sctp_cfg.bind_address = cu_cfg.f1ap_cfg.bind_address; + f1c_sctp_cfg.bind_address = cu_cfg.f1ap_cfg.bind_addr; f1c_sctp_cfg.bind_port = 38471; f1c_sctp_cfg.ppid = F1AP_PPID; f1c_cu_sctp_gateway_config f1c_server_cfg({f1c_sctp_cfg, *epoll_broker, *cu_cp_dlt_pcaps.f1ap}); diff --git a/apps/cu/cu_appconfig.h b/apps/cu/cu_appconfig.h index 7aa4fb4857..a2fb65500e 100644 --- a/apps/cu/cu_appconfig.h +++ b/apps/cu/cu_appconfig.h @@ -23,28 +23,12 @@ #pragma once #include "apps/gnb/gnb_appconfig.h" +#include "apps/services/logger/logger_appconfig.h" #include namespace srsran { namespace srs_cu { -/// Configuration of logging functionalities. -struct log_appconfig { - /// Path to log file or "stdout" to print to console. - std::string filename = "/tmp/cu.log"; - /// Default log level for all layers. - std::string all_level = "warning"; - /// Generic log level assigned to library components without layer-specific level. - std::string lib_level = "warning"; - std::string e2ap_level = "warning"; - std::string config_level = "none"; - std::string metrics_level = "none"; - /// Maximum number of bytes to write when dumping hex arrays. - int hex_max_size = 0; - /// Set to a valid file path to enable tracing and write the trace to the file. - std::string tracing_filename; -}; - /// NR-U configuration struct cu_nru_appconfig { std::string bind_addr = "127.0.10.1"; // Bind address used by the F1-U interface @@ -54,14 +38,18 @@ struct cu_nru_appconfig { /// F1AP configuration struct cu_f1ap_appconfig { /// F1-C bind address - std::string bind_address = "127.0.10.1"; + std::string bind_addr = "127.0.10.1"; }; + } // namespace srs_cu /// Monolithic gnb application configuration. struct cu_appconfig { - /// Logging configuration. - srs_cu::log_appconfig log_cfg; + /// Default constructor to update the log filename. + cu_appconfig() { log_cfg.filename = "/tmp/cu.log"; } + + /// Loggers configuration. + logger_appconfig log_cfg; /// Expert configuration. expert_execution_appconfig expert_execution_cfg; diff --git a/apps/cu/cu_appconfig_cli11_schema.cpp b/apps/cu/cu_appconfig_cli11_schema.cpp index 80a470fec3..90da41d189 100644 --- a/apps/cu/cu_appconfig_cli11_schema.cpp +++ b/apps/cu/cu_appconfig_cli11_schema.cpp @@ -21,83 +21,16 @@ */ #include "cu_appconfig_cli11_schema.h" +#include "apps/services/logger/logger_appconfig_cli11_schema.h" #include "cu_appconfig.h" #include "srsran/support/cli11_utils.h" #include "CLI/CLI11.hpp" using namespace srsran; -// TODO this is common between DU and CU. -static void configure_cli11_log_args(CLI::App& app, srs_cu::log_appconfig& log_params) -{ - auto level_check = [](const std::string& value) -> std::string { - if (value == "info" || value == "debug" || value == "warning" || value == "error") { - return {}; - } - return "Log level value not supported. Accepted values [info,debug,warning,error]"; - }; - - auto metric_level_check = [](const std::string& value) -> std::string { - if (value == "none" || value == "info" || value == "debug") { - return {}; - } - return "Log level value not supported. Accepted values [none,info,debug]"; - }; - - app.add_option("--filename", log_params.filename, "Log file output path")->capture_default_str(); - app.add_option( - "--all_level", log_params.all_level, "Default log level for PHY, MAC, RLC, PDCP, RRC, SDAP, NGAP and GTPU") - ->capture_default_str() - ->check(level_check); - app.add_option("--lib_level", log_params.lib_level, "Generic log level")->capture_default_str()->check(level_check); - app.add_option("--config_level", log_params.config_level, "Config log level") - ->capture_default_str() - ->check(metric_level_check); - app.add_option("--metrics_level", log_params.metrics_level, "Metrics log level") - ->capture_default_str() - ->check(metric_level_check); - app.add_option( - "--hex_max_size", log_params.hex_max_size, "Maximum number of bytes to print in hex (zero for no hex dumps)") - ->capture_default_str() - ->check(CLI::Range(0, 1024)); - app.add_option("--tracing_filename", log_params.tracing_filename, "Set to a valid file path to enable tracing") - ->always_capture_default(); - - // Post-parsing callback. This allows us to set the log level to "all" level, if no level is provided. - app.callback([&]() { - // Do nothing when all_level is not defined or it is defined as warning. - if (app.count("--all_level") == 0 || log_params.all_level == "warning") { - return; - } - - const auto options = app.get_options(); - for (auto* option : options) { - // Skip all_level option and unrelated options to log level. - if (option->check_name("--all_level") || option->get_name().find("level") == std::string::npos) { - continue; - } - - // Do nothing if option is present. - if (option->count()) { - continue; - } - - // Config and metrics loggers have only subset of levels. - if (option->check_name("--config_level") || option->check_name("--metrics_level")) { - if (log_params.all_level == "error") { - option->default_val("none"); - continue; - } - } - - option->default_val(log_params.all_level); - } - }); -} - static void configure_cli11_f1ap_args(CLI::App& app, srs_cu::cu_f1ap_appconfig& f1ap_params) { - add_option(app, "--bind_address", f1ap_params.bind_address, "F1-C bind address")->capture_default_str(); + add_option(app, "--bind_addr", f1ap_params.bind_addr, "F1-C bind address")->capture_default_str(); } static void configure_cli11_nru_args(CLI::App& app, srs_cu::cu_nru_appconfig& nru_cfg) @@ -118,25 +51,22 @@ static void configure_cli11_buffer_pool_args(CLI::App& app, buffer_pool_appconfi ->capture_default_str(); } -void srsran::configure_cli11_with_cu_appconfig_schema(CLI::App& app, cu_appconfig& cu_parsed_cfg) +void srsran::configure_cli11_with_cu_appconfig_schema(CLI::App& app, cu_appconfig& cu_cfg) { - cu_appconfig& cu_cfg = cu_parsed_cfg; - // Logging section. - CLI::App* log_subcmd = add_subcommand(app, "log", "Logging configuration")->configurable(); - configure_cli11_log_args(*log_subcmd, cu_cfg.log_cfg); + configure_cli11_with_logger_appconfig_schema(app, cu_cfg.log_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(); - configure_cli11_f1ap_args(*f1ap_subcmd, cu_parsed_cfg.f1ap_cfg); + configure_cli11_f1ap_args(*f1ap_subcmd, cu_cfg.f1ap_cfg); // NR-U section. CLI::App* cu_up_subcmd = add_subcommand(app, "cu_up", "CU-UP parameters")->configurable(); CLI::App* nru_subcmd = add_subcommand(*cu_up_subcmd, "nru", "NR-U parameters")->configurable(); - configure_cli11_nru_args(*nru_subcmd, cu_parsed_cfg.nru_cfg); + configure_cli11_nru_args(*nru_subcmd, cu_cfg.nru_cfg); // Buffer pool section. CLI::App* buffer_pool_subcmd = app.add_subcommand("buffer_pool", "Buffer pool configuration")->configurable(); - configure_cli11_buffer_pool_args(*buffer_pool_subcmd, cu_parsed_cfg.buffer_pool_config); + configure_cli11_buffer_pool_args(*buffer_pool_subcmd, cu_cfg.buffer_pool_config); } diff --git a/apps/cu/cu_appconfig_cli11_schema.h b/apps/cu/cu_appconfig_cli11_schema.h index 307090bb4b..5830baed23 100644 --- a/apps/cu/cu_appconfig_cli11_schema.h +++ b/apps/cu/cu_appconfig_cli11_schema.h @@ -28,7 +28,7 @@ namespace srsran { struct cu_appconfig; -/// Configures the given CLI11 application with the gNB application configuration schema. -void configure_cli11_with_cu_appconfig_schema(CLI::App& app, cu_appconfig& cu_parsed_cfg); +/// Configures the given CLI11 application with the CU application configuration schema. +void configure_cli11_with_cu_appconfig_schema(CLI::App& app, cu_appconfig& cu_cfg); } // namespace srsran diff --git a/apps/cu/cu_appconfig_validator.cpp b/apps/cu/cu_appconfig_validator.cpp new file mode 100644 index 0000000000..91b0f50d87 --- /dev/null +++ b/apps/cu/cu_appconfig_validator.cpp @@ -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/. + * + */ + +#include "cu_appconfig_validator.h" +#include "apps/services/logger/logger_appconfig_validator.h" +#include "cu_appconfig.h" + +using namespace srsran; + +bool srsran::validate_cu_appconfig(const cu_appconfig& config) +{ + return validate_logger_appconfig(config.log_cfg); +} \ No newline at end of file diff --git a/apps/cu/cu_appconfig_validator.h b/apps/cu/cu_appconfig_validator.h new file mode 100644 index 0000000000..7d7dee54d5 --- /dev/null +++ b/apps/cu/cu_appconfig_validator.h @@ -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/. + * + */ + +#pragma once + +namespace srsran { + +struct cu_appconfig; + +/// Validates the given CU application configuration. Returns true on success, false otherwise. +bool validate_cu_appconfig(const cu_appconfig& config); + +} // namespace srsran diff --git a/apps/du/du.cpp b/apps/du/du.cpp index bf85b8eabb..784740e5f0 100644 --- a/apps/du/du.cpp +++ b/apps/du/du.cpp @@ -120,25 +120,25 @@ static void initialize_log(const std::string& filename) srslog::init(); } -static void register_app_logs(const srs_du::log_appconfig& log_cfg, const dynamic_du_unit_config& du_loggers) +static void register_app_logs(const logger_appconfig& log_cfg, const dynamic_du_unit_config& du_loggers) { // Set log-level of app and all non-layer specific components to app level. for (const auto& id : {"GNB", "ALL", "SCTP-GW", "IO-EPOLL", "UDP-GW", "PCAP"}) { auto& logger = srslog::fetch_basic_logger(id, false); - logger.set_level(srslog::str_to_basic_level(log_cfg.lib_level)); + logger.set_level(log_cfg.lib_level); logger.set_hex_dump_max_size(log_cfg.hex_max_size); } auto& config_logger = srslog::fetch_basic_logger("CONFIG", false); - config_logger.set_level(srslog::str_to_basic_level(log_cfg.config_level)); + config_logger.set_level(log_cfg.config_level); config_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& metrics_logger = srslog::fetch_basic_logger("METRICS", false); - metrics_logger.set_level(srslog::str_to_basic_level(log_cfg.metrics_level)); + metrics_logger.set_level(log_cfg.metrics_level); metrics_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& e2ap_logger = srslog::fetch_basic_logger("E2AP", false); - e2ap_logger.set_level(srslog::str_to_basic_level(log_cfg.e2ap_level)); + e2ap_logger.set_level(log_cfg.e2ap_level); e2ap_logger.set_hex_dump_max_size(log_cfg.hex_max_size); // Register units logs. diff --git a/apps/du/du_appconfig.h b/apps/du/du_appconfig.h index 952b3cb374..981131b56c 100644 --- a/apps/du/du_appconfig.h +++ b/apps/du/du_appconfig.h @@ -23,6 +23,7 @@ #pragma once #include "../gnb/gnb_appconfig.h" // TODO: Remove +#include "apps/services/logger/logger_appconfig.h" #include "apps/services/os_sched_affinity_manager.h" #include "srsran/adt/byte_buffer.h" #include "srsran/support/executors/unique_thread.h" @@ -31,25 +32,6 @@ namespace srsran { namespace srs_du { -/// Configuration of logging functionalities. -struct log_appconfig { - /// Path to log file or "stdout" to print to console. - std::string filename = "/tmp/du.log"; - /// Default log level for all layers. - std::string all_level = "warning"; - /// Generic log level assigned to library components without layer-specific level. - std::string lib_level = "warning"; - std::string e2ap_level = "warning"; - std::string config_level = "none"; - std::string metrics_level = "none"; - /// Maximum number of bytes to write when dumping hex arrays. - int hex_max_size = 0; - /// Set to true to log broadcasting messages and all PRACH opportunities. - bool broadcast_enabled = false; - /// Set to a valid file path to enable tracing and write the trace to the file. - std::string tracing_filename; -}; - /// Configuration of the F1-C interface of the DU. struct f1ap_appconfig { /// CU-CP F1-C address the DU will connect to. @@ -78,8 +60,10 @@ struct metrics_appconfig { /// DU application configuration. struct du_appconfig { - /// Logging configuration. - srs_du::log_appconfig log_cfg; + /// Default constructor to update the log filename. + du_appconfig() { log_cfg.filename = "/tmp/du.log"; } + /// Loggers configuration. + logger_appconfig log_cfg; /// Metrics configuration. srs_du::metrics_appconfig metrics_cfg; /// E2 configuration. diff --git a/apps/du/du_appconfig_cli11_schema.cpp b/apps/du/du_appconfig_cli11_schema.cpp index 221feeb1eb..3a7732db39 100644 --- a/apps/du/du_appconfig_cli11_schema.cpp +++ b/apps/du/du_appconfig_cli11_schema.cpp @@ -21,6 +21,7 @@ */ #include "du_appconfig_cli11_schema.h" +#include "apps/services/logger/logger_appconfig_cli11_schema.h" #include "du_appconfig.h" #include "srsran/support/cli11_utils.h" @@ -32,83 +33,12 @@ static expected parse_int(const std::string& value) try { return std::stoi(value); } catch (const std::invalid_argument& e) { - return {e.what()}; + return make_unexpected(e.what()); } catch (const std::out_of_range& e) { - return {e.what()}; + return make_unexpected(e.what()); } } -// TODO this is common between DU and CU. -static void configure_cli11_log_args(CLI::App& app, srs_du::log_appconfig& log_params) -{ - auto level_check = [](const std::string& value) -> std::string { - if (value == "info" || value == "debug" || value == "warning" || value == "error") { - return {}; - } - return "Log level value not supported. Accepted values [info,debug,warning,error]"; - }; - - auto metric_level_check = [](const std::string& value) -> std::string { - if (value == "none" || value == "info" || value == "debug") { - return {}; - } - return "Log level value not supported. Accepted values [none,info,debug]"; - }; - - app.add_option("--filename", log_params.filename, "Log file output path")->capture_default_str(); - app.add_option("--all_level", log_params.all_level, "Default log level for PHY, MAC, RLC and F1") - ->capture_default_str() - ->check(level_check); - app.add_option("--lib_level", log_params.lib_level, "Generic log level")->capture_default_str()->check(level_check); - app.add_option("--config_level", log_params.config_level, "Config log level") - ->capture_default_str() - ->check(metric_level_check); - app.add_option("--metrics_level", log_params.metrics_level, "Metrics log level") - ->capture_default_str() - ->check(metric_level_check); - app.add_option( - "--hex_max_size", log_params.hex_max_size, "Maximum number of bytes to print in hex (zero for no hex dumps)") - ->capture_default_str() - ->check(CLI::Range(0, 1024)); - app.add_option("--broadcast_enabled", - log_params.broadcast_enabled, - "Enable logging in the physical and MAC layer of broadcast messages and all PRACH opportunities") - ->always_capture_default(); - app.add_option("--tracing_filename", log_params.tracing_filename, "Set to a valid file path to enable tracing") - ->always_capture_default(); - - // Post-parsing callback. This allows us to set the log level to "all" level, if no level is provided. - app.callback([&]() { - // Do nothing when all_level is not defined or it is defined as warning. - if (app.count("--all_level") == 0 || log_params.all_level == "warning") { - return; - } - - const auto options = app.get_options(); - for (auto* option : options) { - // Skip all_level option and unrelated options to log level. - if (option->check_name("--all_level") || option->get_name().find("level") == std::string::npos) { - continue; - } - - // Do nothing if option is present. - if (option->count()) { - continue; - } - - // Config and metrics loggers have only subset of levels. - if (option->check_name("--config_level") || option->check_name("--metrics_level")) { - if (log_params.all_level == "error") { - option->default_val("none"); - continue; - } - } - - option->default_val(log_params.all_level); - } - }); -} - static void configure_cli11_metrics_args(CLI::App& app, srs_du::metrics_appconfig& metrics_params) { add_option(app, "--enable_json_metrics", metrics_params.enable_json_metrics, "Enable JSON metrics reporting") @@ -162,11 +92,11 @@ static error_type is_valid_cpu_index(unsigned cpu_idx) os_sched_affinity_bitmask one_cpu_mask; if (cpu_idx >= one_cpu_mask.size()) { - return error_message; + 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 error_message; + return make_unexpected(error_message); } return default_success_t(); } @@ -175,13 +105,13 @@ static expected parse_one_cpu(const std::string& value) { expected result = parse_int(value); - if (result.is_error()) { - return fmt::format("Could not parse '{}' string as a CPU index", 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 (validation_result.is_error()) { - return validation_result.error(); + if (not validation_result.has_value()) { + return make_unexpected(validation_result.error()); } return result.value(); @@ -195,8 +125,8 @@ static expected, std::string> parse_cpu_range(const std std::string str; getline(ss, str, '-'); auto parse_result = parse_one_cpu(str); - if (parse_result.is_error()) { - return fmt::format("{}. Could not parse '{}' as a range", parse_result.error(), value); + 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()); @@ -204,11 +134,11 @@ static expected, std::string> parse_cpu_range(const std // A range is defined by two numbers. if (range.size() != 2) { - return fmt::format("Could not parse '{}' as a range", value); + return make_unexpected(fmt::format("Could not parse '{}' as a range", value)); } if (range[1] <= range[0]) { - return fmt::format("Invalid CPU core range detected [{}-{}]", range[0], range[1]); + return make_unexpected(fmt::format("Invalid CPU core range detected [{}-{}]", range[0], range[1])); } return interval(range[0], range[1]); @@ -224,7 +154,7 @@ parse_affinity_mask(os_sched_affinity_bitmask& mask, const std::string& value, c getline(ss, str, ','); if (str.find('-') != std::string::npos) { auto range = parse_cpu_range(str); - if (range.is_error()) { + if (not range.has_value()) { report_error("{} in the '{}' property", range.error(), property_name); } @@ -232,7 +162,7 @@ parse_affinity_mask(os_sched_affinity_bitmask& mask, const std::string& value, c mask.fill(range.value().start(), range.value().stop() + 1); } else { auto cpu_idx = parse_one_cpu(str); - if (cpu_idx.is_error()) { + if (not cpu_idx.has_value()) { report_error("{} in the '{}' property", cpu_idx.error(), property_name); } @@ -332,6 +262,9 @@ static void configure_cli11_hal_args(CLI::App& app, std::optional void srsran::configure_cli11_with_du_appconfig_schema(CLI::App& app, du_appconfig& du_cfg) { + // Loggers section. + configure_cli11_with_logger_appconfig_schema(app, du_cfg.log_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); @@ -340,10 +273,6 @@ void srsran::configure_cli11_with_du_appconfig_schema(CLI::App& app, du_appconfi CLI::App* nru_subcmd = app.add_subcommand("nru", "NR-U interface configuration")->configurable(); configure_cli11_f1u_args(*nru_subcmd, du_cfg.nru_cfg); - // Logging section. - CLI::App* log_subcmd = app.add_subcommand("log", "Logging configuration")->configurable(); - configure_cli11_log_args(*log_subcmd, du_cfg.log_cfg); - // Metrics section. CLI::App* metrics_subcmd = app.add_subcommand("metrics", "Metrics configuration")->configurable(); configure_cli11_metrics_args(*metrics_subcmd, du_cfg.metrics_cfg); diff --git a/apps/du/du_appconfig_validators.cpp b/apps/du/du_appconfig_validators.cpp index c45ede6e0f..bdecf9ef89 100644 --- a/apps/du/du_appconfig_validators.cpp +++ b/apps/du/du_appconfig_validators.cpp @@ -21,11 +21,16 @@ */ #include "du_appconfig_validators.h" +#include "apps/services/logger/logger_appconfig_validator.h" using namespace srsran; bool srsran::validate_appconfig(const du_appconfig& config) { + if (!validate_logger_appconfig(config.log_cfg)) { + return false; + } + if (config.f1ap_cfg.cu_cp_address.empty()) { fmt::print("CU-CP F1-C address is mandatory\n"); return false; diff --git a/apps/examples/phy/radio_ssb.cpp b/apps/examples/phy/radio_ssb.cpp index 418d04ef0b..184eda2060 100644 --- a/apps/examples/phy/radio_ssb.cpp +++ b/apps/examples/phy/radio_ssb.cpp @@ -71,7 +71,7 @@ static const auto modulations = to_array({to_string(modulation_sche to_string(modulation_scheme::QAM64), to_string(modulation_scheme::QAM256)}); -static std::string log_level = "warning"; +static srslog::basic_levels log_level = srslog::basic_levels::warning; /// Program parameters. static subcarrier_spacing scs = subcarrier_spacing::kHz15; @@ -339,9 +339,11 @@ static void parse_args(int argc, char** argv) sync_source = std::string(optarg); } break; - case 'v': - log_level = std::string(optarg); + case 'v': { + auto level = srslog::str_to_basic_level(std::string(optarg)); + log_level = level.has_value() ? level.value() : srslog::basic_levels::info; break; + } case 'd': enable_random_data = true; break; @@ -502,7 +504,7 @@ int main(int argc, char** argv) parse_args(argc, argv); // Make sure thread pool logging matches the test level. - srslog::fetch_basic_logger("POOL").set_level(srslog::str_to_basic_level(log_level)); + srslog::fetch_basic_logger("POOL").set_level(log_level); // Make sure parameters are valid. report_fatal_error_if_not( @@ -595,7 +597,7 @@ int main(int argc, char** argv) // Create lower PHY error adapter logger. srslog::basic_logger& logger = srslog::fetch_basic_logger("PHY"); - logger.set_level(srslog::str_to_basic_level(log_level)); + logger.set_level(log_level); // Create adapters. phy_error_adapter error_adapter(logger); diff --git a/apps/examples/phy/rx_symbol_handler_example.h b/apps/examples/phy/rx_symbol_handler_example.h index 1e051bc817..a9b0f85591 100644 --- a/apps/examples/phy/rx_symbol_handler_example.h +++ b/apps/examples/phy/rx_symbol_handler_example.h @@ -35,9 +35,9 @@ class rx_symbol_handler_example : public upper_phy_rx_symbol_handler std::mutex mutex; public: - rx_symbol_handler_example(std::string log_level) : logger(srslog::fetch_basic_logger("RxSyHan")) + rx_symbol_handler_example(srslog::basic_levels log_level) : logger(srslog::fetch_basic_logger("RxSyHan")) { - logger.set_level(srslog::str_to_basic_level(log_level)); + logger.set_level(log_level); } void handle_rx_symbol(const upper_phy_rx_symbol_context& context, const resource_grid_reader& grid) override diff --git a/apps/examples/phy/upper_phy_ssb_example.cpp b/apps/examples/phy/upper_phy_ssb_example.cpp index 983906965a..5b37906ec3 100644 --- a/apps/examples/phy/upper_phy_ssb_example.cpp +++ b/apps/examples/phy/upper_phy_ssb_example.cpp @@ -278,7 +278,7 @@ class upper_phy_example_sw : public upper_phy_ssb_example std::unique_ptr srsran::upper_phy_ssb_example::create(const configuration& config) { srslog::basic_logger& logger = srslog::fetch_basic_logger("UpperPHY", false); - logger.set_level(srslog::str_to_basic_level(config.log_level)); + logger.set_level(config.log_level); std::shared_ptr crc_calc_factory = create_crc_calculator_factory_sw("lut"); ASSERT_FACTORY(crc_calc_factory); diff --git a/apps/examples/phy/upper_phy_ssb_example.h b/apps/examples/phy/upper_phy_ssb_example.h index 4622bcca5a..dd1845df1a 100644 --- a/apps/examples/phy/upper_phy_ssb_example.h +++ b/apps/examples/phy/upper_phy_ssb_example.h @@ -29,6 +29,7 @@ #include "srsran/ran/cyclic_prefix.h" #include "srsran/ran/pci.h" #include "srsran/ran/ssb_mapping.h" +#include "srsran/srslog/logger.h" #include #include @@ -74,7 +75,7 @@ class upper_phy_ssb_example : public upper_phy_timing_handler /// Collects upper PHY sample configuration parameters. struct configuration { /// General upper PHY logging level. - std::string log_level; + srslog::basic_levels log_level; /// Specifies the maximum number of PRBs. unsigned max_nof_prb; /// Specifies the maximum number of antenna ports. diff --git a/apps/examples/radio/radio_notifier_sample.h b/apps/examples/radio/radio_notifier_sample.h index 82ca6bf00d..310ea0ee2c 100644 --- a/apps/examples/radio/radio_notifier_sample.h +++ b/apps/examples/radio/radio_notifier_sample.h @@ -41,10 +41,10 @@ class radio_notifier_spy : public radio_notification_handler unsigned count_rx_other = 0; public: - radio_notifier_spy(std::string log_level_) : logger(srslog::fetch_basic_logger("Radio notification")) + radio_notifier_spy(srslog::basic_levels log_level_) : logger(srslog::fetch_basic_logger("Radio notification")) { srslog::init(); - logger.set_level(srslog::str_to_basic_level(log_level_)); + logger.set_level(log_level_); } void on_radio_rt_event(const event_description& description) override diff --git a/apps/examples/radio/radio_util_sample.cpp b/apps/examples/radio/radio_util_sample.cpp index 9037cee3db..376f5e89d6 100644 --- a/apps/examples/radio/radio_util_sample.cpp +++ b/apps/examples/radio/radio_util_sample.cpp @@ -39,7 +39,7 @@ using namespace srsran; /// Describes the benchmark configuration. static std::string rx_filename; static double duration_s = 0.1; -static std::string log_level = "info"; +static srslog::basic_levels log_level = srslog::basic_levels::info; static std::string driver_name = "uhd"; static std::string device_arguments = "type=b200"; static std::vector tx_channel_arguments = {}; @@ -205,7 +205,7 @@ static void usage(std::string_view prog) fmt::print("\t-d Enable discontinuous transmission mode. [Default {}]\n", enable_discontinuous_tx); fmt::print("\t-g Discontinuous transmission gap in number of consecutive empty buffers. [Default {}]\n", nof_consecutive_empty_buffers); - fmt::print("\t-v Logging level. [Default {}]\n", log_level); + fmt::print("\t-v Logging level. [Default {}]\n", srslog::basic_level_to_string(log_level)); fmt::print("\t-o saves received signal of stream:port 0:0 in a file. Ignored if none. [Default {}]\n", rx_filename.empty() ? "none" : rx_filename); fmt::print("\t-h print this message.\n"); @@ -239,9 +239,11 @@ static void parse_args(int argc, char** argv) nof_consecutive_empty_buffers = std::strtol(optarg, nullptr, 10); } break; - case 'v': - log_level = std::string(optarg); + case 'v': { + auto level = srslog::str_to_basic_level(std::string(optarg)); + log_level = level.has_value() ? level.value() : srslog::basic_levels::info; break; + } case 'h': default: usage(argv[0]); diff --git a/apps/examples/radio/rx_power_analyzer.cpp b/apps/examples/radio/rx_power_analyzer.cpp index b261431983..12d25e5ca0 100644 --- a/apps/examples/radio/rx_power_analyzer.cpp +++ b/apps/examples/radio/rx_power_analyzer.cpp @@ -52,7 +52,7 @@ using namespace srsran; /// Describes the analysis configuration. static std::string results_filename; static double step_time_s = 1; -static std::string log_level = "info"; +static srslog::basic_levels log_level = srslog::basic_levels::info; static std::string driver_name = "uhd"; static std::string device_arguments = "type=b200"; static std::vector tx_channel_arguments = {}; @@ -106,7 +106,7 @@ static void usage(std::string_view prog) fmt::print("\t-m Minimum Rx gain. [Default {}]\n", rx_gain_min); fmt::print("\t-M Maximum Rx gain. [Default {}]\n", rx_gain_max); fmt::print("\t-g Tx gain. [Default {}]\n", tx_gain); - fmt::print("\t-v Logging level. [Default {}]\n", log_level); + fmt::print("\t-v Logging level. [Default {}]\n", srslog::basic_level_to_string(log_level)); fmt::print("\t-r Saves measurement results in a file. Ignored if none. [Default {}]\n", results_filename.empty() ? "none" : results_filename); fmt::print("\t-T Transmit random data while measuring. [Default {}]\n", tx_active); @@ -164,9 +164,11 @@ static void parse_args(int argc, char** argv) tx_gain = std::strtol(optarg, nullptr, 10); } break; - case 'v': - log_level = std::string(optarg); + case 'v': { + auto level = srslog::str_to_basic_level(std::string(optarg)); + log_level = level.has_value() ? level.value() : srslog::basic_levels::info; break; + } case 'T': tx_active = true; break; @@ -214,7 +216,7 @@ static void resize_buffers(baseband_gateway_buffer_dynamic& tx_baseband_buffer, { // Create a normal distribution for complex numbers with some power back-off. std::mt19937 rgen(0); - complex_normal_distribution dist(0.0, convert_dB_to_amplitude(avg_tx_power_dBFS)); + complex_normal_distribution dist({0, 0}, convert_dB_to_amplitude(avg_tx_power_dBFS)); tx_baseband_buffer.resize(block_size); diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp index d788c67514..6d4b31398c 100644 --- a/apps/gnb/gnb.cpp +++ b/apps/gnb/gnb.cpp @@ -139,7 +139,7 @@ static void initialize_log(const std::string& filename) srslog::init(); } -static void register_app_logs(const log_appconfig& log_cfg, +static void register_app_logs(const logger_appconfig& log_cfg, const cu_cp_unit_logger_config& cu_cp_loggers, const cu_up_unit_logger_config& cu_up_loggers, const dynamic_du_unit_config& du_loggers) @@ -147,20 +147,20 @@ static void register_app_logs(const log_appconfig& log_cfg, // Set log-level of app and all non-layer specific components to app level. for (const auto& id : {"GNB", "ALL", "SCTP-GW", "IO-EPOLL", "UDP-GW", "PCAP"}) { auto& logger = srslog::fetch_basic_logger(id, false); - logger.set_level(srslog::str_to_basic_level(log_cfg.lib_level)); + logger.set_level(log_cfg.lib_level); logger.set_hex_dump_max_size(log_cfg.hex_max_size); } auto& config_logger = srslog::fetch_basic_logger("CONFIG", false); - config_logger.set_level(srslog::str_to_basic_level(log_cfg.config_level)); + config_logger.set_level(log_cfg.config_level); config_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& metrics_logger = srslog::fetch_basic_logger("METRICS", false); - metrics_logger.set_level(srslog::str_to_basic_level(log_cfg.metrics_level)); + metrics_logger.set_level(log_cfg.metrics_level); metrics_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& e2ap_logger = srslog::fetch_basic_logger("E2AP", false); - e2ap_logger.set_level(srslog::str_to_basic_level(log_cfg.e2ap_level)); + e2ap_logger.set_level(log_cfg.e2ap_level); e2ap_logger.set_hex_dump_max_size(log_cfg.hex_max_size); // Register units logs. @@ -220,6 +220,11 @@ int main(int argc, char** argv) tacs.push_back(cell.cell.tac); } + // If test mode is enabled, we auto-enable "no_core" option + if (du_unit_cfg.du_high_cfg.config.test_mode_cfg.test_ue.rnti != rnti_t::INVALID_RNTI) { + cu_cp_config.amf_cfg.no_core = true; + } + autoderive_cu_cp_parameters_after_parsing(app, cu_cp_config, std::move(plmns), std::move(tacs)); }); diff --git a/apps/gnb/gnb_appconfig.h b/apps/gnb/gnb_appconfig.h index 11b47d5fd2..486eb48355 100644 --- a/apps/gnb/gnb_appconfig.h +++ b/apps/gnb/gnb_appconfig.h @@ -22,10 +22,9 @@ #pragma once +#include "apps/services/logger/logger_appconfig.h" #include "apps/services/os_sched_affinity_manager.h" -#include "apps/units/cu_up/cu_up_unit_pcap_config.h" #include "srsran/adt/byte_buffer.h" -#include "srsran/adt/optional.h" #include "srsran/ran/direct_current_offset.h" #include "srsran/ran/gnb_id.h" #include "srsran/support/executors/unique_thread.h" @@ -54,25 +53,6 @@ struct cu_up_appconfig { bool warn_on_drop = false; }; -/// Configuration of logging functionalities. -struct log_appconfig { - /// Path to log file or "stdout" to print to console. - std::string filename = "/tmp/gnb.log"; - /// Default log level for all layers. - std::string all_level = "warning"; - /// Generic log level assigned to library components without layer-specific level. - std::string lib_level = "warning"; - std::string e2ap_level = "warning"; - std::string config_level = "none"; - std::string metrics_level = "none"; - /// Maximum number of bytes to write when dumping hex arrays. - int hex_max_size = 0; - /// Set to true to log broadcasting messages and all PRACH opportunities. - bool broadcast_enabled = false; - /// Set to a valid file path to enable tracing and write the trace to the file. - std::string tracing_filename; -}; - /// Metrics report configuration. struct metrics_appconfig { struct pdcp_metrics { @@ -129,8 +109,10 @@ struct hal_appconfig { /// Monolithic gnb application configuration. struct gnb_appconfig { - /// Logging configuration. - log_appconfig log_cfg; + /// Default constructor to update the log filename. + gnb_appconfig() { log_cfg.filename = "/tmp/gnb.log"; } + /// Loggers configuration. + logger_appconfig log_cfg; /// Metrics configuration. metrics_appconfig metrics_cfg; /// gNodeB identifier. diff --git a/apps/gnb/gnb_appconfig_cli11_schema.cpp b/apps/gnb/gnb_appconfig_cli11_schema.cpp index ea1c241d6a..a1b949ad79 100644 --- a/apps/gnb/gnb_appconfig_cli11_schema.cpp +++ b/apps/gnb/gnb_appconfig_cli11_schema.cpp @@ -21,11 +21,9 @@ */ #include "gnb_appconfig_cli11_schema.h" +#include "apps/services/logger/logger_appconfig_cli11_schema.h" #include "gnb_appconfig.h" -#include "srsran/ran/duplex_mode.h" -#include "srsran/ran/pdsch/pdsch_mcs.h" #include "srsran/support/cli11_utils.h" -#include "srsran/support/config_parsers.h" #include "srsran/support/error_handling.h" #include "CLI/CLI11.hpp" @@ -37,83 +35,12 @@ static expected parse_int(const std::string& value) try { return std::stoi(value); } catch (const std::invalid_argument& e) { - return {e.what()}; + return make_unexpected(e.what()); } catch (const std::out_of_range& e) { - return {e.what()}; + return make_unexpected(e.what()); } } -static void configure_cli11_log_args(CLI::App& app, log_appconfig& log_params) -{ - auto level_check = [](const std::string& value) -> std::string { - if (value == "info" || value == "debug" || value == "warning" || value == "error") { - return {}; - } - return "Log level value not supported. Accepted values [info,debug,warning,error]"; - }; - - auto metric_level_check = [](const std::string& value) -> std::string { - if (value == "none" || value == "info" || value == "debug") { - return {}; - } - return "Log level value not supported. Accepted values [none,info,debug]"; - }; - - app.add_option("--filename", log_params.filename, "Log file output path")->capture_default_str(); - app.add_option( - "--all_level", log_params.all_level, "Default log level for PHY, MAC, RLC, PDCP, RRC, SDAP, NGAP and GTPU") - ->capture_default_str() - ->check(level_check); - app.add_option("--lib_level", log_params.lib_level, "Generic log level")->capture_default_str()->check(level_check); - app.add_option("--config_level", log_params.config_level, "Config log level") - ->capture_default_str() - ->check(metric_level_check); - app.add_option("--metrics_level", log_params.metrics_level, "Metrics log level") - ->capture_default_str() - ->check(metric_level_check); - app.add_option( - "--hex_max_size", log_params.hex_max_size, "Maximum number of bytes to print in hex (zero for no hex dumps)") - ->capture_default_str() - ->check(CLI::Range(0, 1024)); - app.add_option("--broadcast_enabled", - log_params.broadcast_enabled, - "Enable logging in the physical and MAC layer of broadcast messages and all PRACH opportunities") - ->always_capture_default(); - app.add_option("--tracing_filename", log_params.tracing_filename, "Set to a valid file path to enable tracing") - ->always_capture_default(); - - // Post-parsing callback. This allows us to set the log level to "all" level, if no level is provided. - app.callback([&]() { - // Do nothing when all_level is not defined or it is defined as warning. - if (app.count("--all_level") == 0 || log_params.all_level == "warning") { - return; - } - - const auto options = app.get_options(); - for (auto* option : options) { - // Skip all_level option and unrelated options to log level. - if (option->check_name("--all_level") || option->get_name().find("level") == std::string::npos) { - continue; - } - - // Do nothing if option is present. - if (option->count()) { - continue; - } - - // Config and metrics loggers have only subset of levels. - if (option->check_name("--config_level") || option->check_name("--metrics_level")) { - if (log_params.all_level == "error") { - option->default_val("none"); - continue; - } - } - - option->default_val(log_params.all_level); - } - }); -} - static void configure_cli11_metrics_args(CLI::App& app, metrics_appconfig& metrics_params) { app.add_option("--pdcp_report_period", metrics_params.pdcp.report_period, "PDCP metrics report period") @@ -177,11 +104,11 @@ static error_type is_valid_cpu_index(unsigned cpu_idx) os_sched_affinity_bitmask one_cpu_mask; if (cpu_idx >= one_cpu_mask.size()) { - return error_message; + 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 error_message; + return make_unexpected(error_message); } return default_success_t(); } @@ -190,13 +117,13 @@ static expected parse_one_cpu(const std::string& value) { expected result = parse_int(value); - if (result.is_error()) { - return fmt::format("Could not parse '{}' string as a CPU index", 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 (validation_result.is_error()) { - return validation_result.error(); + if (not validation_result.has_value()) { + return make_unexpected(validation_result.error()); } return result.value(); @@ -210,8 +137,8 @@ static expected, std::string> parse_cpu_range(const std std::string str; getline(ss, str, '-'); auto parse_result = parse_one_cpu(str); - if (parse_result.is_error()) { - return fmt::format("{}. Could not parse '{}' as a range", parse_result.error(), value); + 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()); @@ -219,11 +146,11 @@ static expected, std::string> parse_cpu_range(const std // A range is defined by two numbers. if (range.size() != 2) { - return fmt::format("Could not parse '{}' as a range", value); + return make_unexpected(fmt::format("Could not parse '{}' as a range", value)); } if (range[1] <= range[0]) { - return fmt::format("Invalid CPU core range detected [{}-{}]", range[0], range[1]); + return make_unexpected(fmt::format("Invalid CPU core range detected [{}-{}]", range[0], range[1])); } return interval(range[0], range[1]); @@ -239,7 +166,7 @@ parse_affinity_mask(os_sched_affinity_bitmask& mask, const std::string& value, c getline(ss, str, ','); if (str.find('-') != std::string::npos) { auto range = parse_cpu_range(str); - if (range.is_error()) { + if (not range.has_value()) { report_error("{} in the '{}' property", range.error(), property_name); } @@ -247,7 +174,7 @@ parse_affinity_mask(os_sched_affinity_bitmask& mask, const std::string& value, c mask.fill(range.value().start(), range.value().stop() + 1); } else { auto cpu_idx = parse_one_cpu(str); - if (cpu_idx.is_error()) { + if (not cpu_idx.has_value()) { report_error("{} in the '{}' property", cpu_idx.error(), property_name); } @@ -339,9 +266,8 @@ void srsran::configure_cli11_with_gnb_appconfig_schema(CLI::App& app, gnb_appcon ->check(CLI::Range(22, 32)); add_option(app, "--ran_node_name", gnb_cfg.ran_node_name, "RAN node name")->capture_default_str(); - // Logging section. - CLI::App* log_subcmd = app.add_subcommand("log", "Logging configuration")->configurable(); - configure_cli11_log_args(*log_subcmd, gnb_cfg.log_cfg); + // Loggers section. + configure_cli11_with_logger_appconfig_schema(app, gnb_cfg.log_cfg); // Metrics section. CLI::App* metrics_subcmd = app.add_subcommand("metrics", "Metrics configuration")->configurable(); diff --git a/apps/gnb/gnb_appconfig_validators.cpp b/apps/gnb/gnb_appconfig_validators.cpp index 638b9c1989..d1c8f34def 100644 --- a/apps/gnb/gnb_appconfig_validators.cpp +++ b/apps/gnb/gnb_appconfig_validators.cpp @@ -21,22 +21,12 @@ */ #include "gnb_appconfig_validators.h" +#include "apps/services/logger/logger_appconfig_validator.h" #include "apps/units/cu_cp/cu_cp_unit_config.h" #include "apps/units/flexible_du/du_high/du_high_config.h" -#include "srsran/ran/nr_cgi_helpers.h" using namespace srsran; -/// Validates the given logging configuration. Returns true on success, otherwise false. -static bool validate_log_appconfig(const log_appconfig& config) -{ - if (config.filename.empty()) { - return false; - } - - return true; -} - static bool validate_hal_config(const std::optional& config) { #ifdef DPDK_FOUND @@ -55,7 +45,7 @@ static bool validate_hal_config(const std::optional& config) bool srsran::validate_appconfig(const gnb_appconfig& config) { - if (!validate_log_appconfig(config.log_cfg)) { + if (!validate_logger_appconfig(config.log_cfg)) { return false; } diff --git a/apps/services/CMakeLists.txt b/apps/services/CMakeLists.txt index 6067225ca0..42b174f7c7 100644 --- a/apps/services/CMakeLists.txt +++ b/apps/services/CMakeLists.txt @@ -18,6 +18,8 @@ # and at http://www.gnu.org/licenses/. # +add_subdirectory(logger) + set(SOURCES metrics_log_helper.cpp e2_metric_connector_manager.cpp @@ -30,3 +32,4 @@ set(SOURCES 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) diff --git a/apps/services/logger/CMakeLists.txt b/apps/services/logger/CMakeLists.txt new file mode 100644 index 0000000000..cfd9cd6420 --- /dev/null +++ b/apps/services/logger/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 + logger_appconfig_cli11_schema.cpp + logger_appconfig_validator.cpp) + +add_library(srsran_logger_app_service STATIC ${SOURCES}) +target_include_directories(srsran_logger_app_service PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/apps/services/logger/logger_appconfig.h b/apps/services/logger/logger_appconfig.h new file mode 100644 index 0000000000..83893e3ece --- /dev/null +++ b/apps/services/logger/logger_appconfig.h @@ -0,0 +1,47 @@ +/* + * + * 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/srslog/logger.h" +#include + +namespace srsran { + +/// Configuration of logging functionalities. +struct logger_appconfig { + /// Path to log file or "stdout" to print to console. + std::string filename = "stdout"; + /// Default log level for all layers. + srslog::basic_levels all_level = srslog::basic_levels::warning; + /// Generic log level assigned to library components without layer-specific level. + srslog::basic_levels lib_level = srslog::basic_levels::warning; + srslog::basic_levels e2ap_level = srslog::basic_levels::warning; + srslog::basic_levels config_level = srslog::basic_levels::none; + srslog::basic_levels metrics_level = srslog::basic_levels::none; + /// Maximum number of bytes to write when dumping hex arrays. + int hex_max_size = 0; + /// Set to a valid file path to enable tracing and write the trace to the file. + std::string tracing_filename; +}; + +} // namespace srsran diff --git a/apps/services/logger/logger_appconfig_cli11_schema.cpp b/apps/services/logger/logger_appconfig_cli11_schema.cpp new file mode 100644 index 0000000000..50d861f602 --- /dev/null +++ b/apps/services/logger/logger_appconfig_cli11_schema.cpp @@ -0,0 +1,101 @@ +/* + * + * 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 "logger_appconfig_cli11_schema.h" +#include "logger_appconfig.h" +#include "logger_appconfig_cli11_utils.h" +#include "srsran/support/cli11_utils.h" + +using namespace srsran; + +static void configure_cli11_log_args(CLI::App& app, logger_appconfig& log_params) +{ + auto metric_level_check = [](const std::string& value) -> std::string { + if (auto level = srslog::str_to_basic_level(value); !level.has_value() || + level.value() == srslog::basic_levels::error || + level.value() == srslog::basic_levels::warning) { + return "Log level value not supported. Accepted values [none,info,debug]"; + } + + return {}; + }; + + app.add_option("--filename", log_params.filename, "Log file output path")->capture_default_str(); + + app_services::add_log_option( + app, log_params.all_level, "--all_level", "Default log level for PHY, MAC, RLC, PDCP, RRC, SDAP, NGAP and GTPU"); + app_services::add_log_option(app, log_params.lib_level, "--lib_level", "Generic log level "); + app_services::add_log_option(app, log_params.e2ap_level, " --e2ap_level", "E2AP log level"); + + add_option_function( + app, " --config_level", app_services::capture_log_level_function(log_params.config_level), "Config log level") + ->default_str(srslog::basic_level_to_string(log_params.config_level)) + ->check(metric_level_check); + add_option_function( + app, "--metrics_level", app_services::capture_log_level_function(log_params.metrics_level), "Metrics log level") + ->default_str(srslog::basic_level_to_string(log_params.metrics_level)) + ->check(metric_level_check); + add_option( + app, "--hex_max_size", log_params.hex_max_size, "Maximum number of bytes to print in hex (zero for no hex dumps)") + ->capture_default_str() + ->check(CLI::Range(0, 1024)); + add_option(app, "--tracing_filename", log_params.tracing_filename, "Set to a valid file path to enable tracing") + ->always_capture_default(); + + // Post-parsing callback. This allows us to set the log level to "all" level, if no level is provided. + app.callback([&]() { + // Do nothing when all_level is not defined or it is defined as warning. + if (app.count("--all_level") == 0 || log_params.all_level == srslog::basic_levels::warning) { + return; + } + + const auto options = app.get_options(); + for (auto* option : options) { + // Skip all_level option and unrelated options to log level. + if (option->check_name("--all_level") || option->get_name().find("level") == std::string::npos) { + continue; + } + + // Do nothing if option is present. + if (option->count()) { + continue; + } + + // Config and metrics loggers have only subset of levels. + if (option->check_name("--config_level") || option->check_name("--metrics_level")) { + if (log_params.all_level == srslog::basic_levels::error) { + option->default_val("none"); + continue; + } + } + + option->default_val(srslog::basic_level_to_string(log_params.all_level)); + } + }); +} + +void srsran::configure_cli11_with_logger_appconfig_schema(CLI::App& app, logger_appconfig& config) +{ + // Logging section. + CLI::App* log_subcmd = app.add_subcommand("log", "Logging configuration")->configurable(); + configure_cli11_log_args(*log_subcmd, config); +} diff --git a/apps/services/logger/logger_appconfig_cli11_schema.h b/apps/services/logger/logger_appconfig_cli11_schema.h new file mode 100644 index 0000000000..7400042325 --- /dev/null +++ b/apps/services/logger/logger_appconfig_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 + +namespace srsran { + +struct logger_appconfig; + +/// Configures the given CLI11 application with the logger application configuration schema. +void configure_cli11_with_logger_appconfig_schema(CLI::App& app, logger_appconfig& config); + +} // namespace srsran diff --git a/apps/services/logger/logger_appconfig_cli11_utils.h b/apps/services/logger/logger_appconfig_cli11_utils.h new file mode 100644 index 0000000000..df7e7461d0 --- /dev/null +++ b/apps/services/logger/logger_appconfig_cli11_utils.h @@ -0,0 +1,58 @@ +/* + * + * 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/srslog/logger.h" +#include "srsran/support/cli11_utils.h" + +namespace srsran { +namespace app_services { + +/// Function to capture the log level. +inline auto capture_log_level_function = [](srslog::basic_levels& level) { + return [&level](const std::string& value) { + auto val = srslog::str_to_basic_level(value); + level = (val) ? val.value() : srslog::basic_levels::none; + }; +}; + +/// Helper function to add log options to CLI11. +inline CLI::Option* +add_log_option(CLI::App& app, srslog::basic_levels& level, const std::string& name, const std::string& descriptrion) +{ + /// Function to check that the log level is correct. + auto check_log_level = [](const std::string& value) -> std::string { + if (srslog::str_to_basic_level(value).has_value()) { + return {}; + } + + return fmt::format("Log level '{}' not supported. Accepted values [none,info,debug,warning,error]", value); + }; + + return add_option_function(app, name, capture_log_level_function(level), descriptrion) + ->default_str(srslog::basic_level_to_string(level)) + ->check(check_log_level); +} + +} // namespace app_services +} // namespace srsran diff --git a/tests/unittests/ran/bcd_helpers_test.cpp b/apps/services/logger/logger_appconfig_validator.cpp similarity index 69% rename from tests/unittests/ran/bcd_helpers_test.cpp rename to apps/services/logger/logger_appconfig_validator.cpp index 110d4cbaf2..2449631558 100644 --- a/tests/unittests/ran/bcd_helpers_test.cpp +++ b/apps/services/logger/logger_appconfig_validator.cpp @@ -20,19 +20,12 @@ * */ -#include "srsran/ran/bcd_helpers.h" -#include +#include "logger_appconfig_validator.h" +#include "logger_appconfig.h" using namespace srsran; -TEST(bcd_helpers_test, plmn_string_to_bcd) +bool srsran::validate_logger_appconfig(const logger_appconfig& config) { - ASSERT_EQ(plmn_string_to_bcd("00101"), 0xf110); - ASSERT_EQ(plmn_string_to_bcd("20899"), 0x2f899); -} - -TEST(bcd_helpers_test, plmn_bcd_to_string) -{ - ASSERT_EQ(plmn_bcd_to_string(0xf110), "00101"); - ASSERT_EQ(plmn_bcd_to_string(0x2f899), "20899"); + return !config.filename.empty(); } diff --git a/apps/services/logger/logger_appconfig_validator.h b/apps/services/logger/logger_appconfig_validator.h new file mode 100644 index 0000000000..de23ff55a1 --- /dev/null +++ b/apps/services/logger/logger_appconfig_validator.h @@ -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/. + * + */ + +#pragma once + +namespace srsran { + +struct logger_appconfig; + +/// Validates the given logger application configuration. Returns true on success, false otherwise. +bool validate_logger_appconfig(const logger_appconfig& config); + +} // namespace srsran diff --git a/apps/services/metrics_hub.cpp b/apps/services/metrics_hub.cpp index e5bb79221f..cfe23f7b9c 100644 --- a/apps/services/metrics_hub.cpp +++ b/apps/services/metrics_hub.cpp @@ -68,10 +68,9 @@ void scheduler_ue_metrics_source::report_metrics(const scheduler_cell_metrics& m srsran_assert(executor != nullptr, "Task executor must not be nullptr"); // Fetch a metrics report object from the pool. + // Note: We are trying to reuse the pre-existing allocated memory in the cached_metrics object. auto cached_metrics = metrics_pool.get(); - // Try to reuse the pre-existing memory in the cached_metrics object. - cached_metrics->ue_metrics.assign(metrics.ue_metrics.begin(), metrics.ue_metrics.end()); - cached_metrics->nof_error_indications = metrics.nof_error_indications; + *cached_metrics = metrics; if (not executor->execute([this, cached_metrics = std::move(cached_metrics)]() { for (auto& subscriber : subscribers) { diff --git a/apps/services/metrics_log_helper.cpp b/apps/services/metrics_log_helper.cpp index 15187470fc..3ea0ec7cf3 100644 --- a/apps/services/metrics_log_helper.cpp +++ b/apps/services/metrics_log_helper.cpp @@ -117,6 +117,18 @@ static std::string float_to_eng_string(float f, int digits) void metrics_log_helper::report_metrics(const scheduler_cell_metrics& metrics) { fmt::memory_buffer buffer; + + // log cell-wide metrics + fmt::format_to(buffer, "Cell Scheduler Metrics:"); + fmt::format_to(buffer, + " error_indications={} mean_latency={}usec latency_hist=[{}]", + metrics.nof_error_indications, + metrics.average_decision_latency.count(), + fmt::join(metrics.latency_histogram.begin(), metrics.latency_histogram.end(), ", ")); + logger.info("{}", to_c_str(buffer)); + buffer.clear(); + + // log ue-specific metrics for (const auto& ue : metrics.ue_metrics) { fmt::format_to(buffer, "Scheduler UE Metrics:"); fmt::format_to(buffer, " pci={}", ue.pci); diff --git a/apps/services/metrics_plotter_json.cpp b/apps/services/metrics_plotter_json.cpp index 2fd9bbf4c1..adf2dbb9fe 100644 --- a/apps/services/metrics_plotter_json.cpp +++ b/apps/services/metrics_plotter_json.cpp @@ -64,13 +64,15 @@ DECLARE_METRIC_SET("ue_container", /// cell-wide metrics. DECLARE_METRIC("error_indication_count", metric_error_indication_count, unsigned, ""); DECLARE_METRIC("average_latency", metric_average_latency, unsigned, ""); -DECLARE_METRIC("latency_thres_count", metric_latency_thres_count, unsigned, ""); -DECLARE_METRIC_LIST("latency_thres_count", mlist_thres_count, std::vector); +DECLARE_METRIC("latency_bin_start_usec", latency_bin_start_usec, unsigned, ""); +DECLARE_METRIC("latency_bin_count", latency_bin_count, unsigned, ""); +DECLARE_METRIC_SET("latency_bin", latency_bin, latency_bin_start_usec, latency_bin_count); +DECLARE_METRIC_LIST("latency_histogram", latency_histogram, std::vector); DECLARE_METRIC_SET("cell_metrics", cell_metrics, metric_error_indication_count, metric_average_latency, - mlist_thres_count); + latency_histogram); /// Metrics root object. DECLARE_METRIC("timestamp", metric_timestamp_tag, double, ""); @@ -122,10 +124,13 @@ void metrics_plotter_json::report_metrics(const scheduler_cell_metrics& metrics) auto& cell_output = ctx.get(); cell_output.write(metrics.nof_error_indications); cell_output.write(metrics.average_decision_latency.count()); - for (unsigned count : metrics.latency_histogram) { - cell_output.get().emplace_back(); - auto& elem = cell_output.get().back(); - elem.value = count; + unsigned bin_idx = 0; + for (unsigned bin_count : metrics.latency_histogram) { + cell_output.get().emplace_back(); + auto& elem = cell_output.get().back(); + elem.write(bin_idx * scheduler_cell_metrics::nof_usec_per_bin); + elem.write(bin_count); + bin_idx++; } // Log the context. diff --git a/apps/services/stdin_command_dispatcher.cpp b/apps/services/stdin_command_dispatcher.cpp index aab3544742..a92bd78c9d 100644 --- a/apps/services/stdin_command_dispatcher.cpp +++ b/apps/services/stdin_command_dispatcher.cpp @@ -71,7 +71,7 @@ class sleep_app_command : public application_command expected seconds = app_services::parse_int(args.front()); // Verify the argument is numeric. - if (seconds.is_error()) { + if (not seconds.has_value()) { fmt::print("{}.\n", seconds.error()); return; } diff --git a/apps/services/stdin_command_dispatcher_utils.h b/apps/services/stdin_command_dispatcher_utils.h index 89848a72c6..82a37c9ebf 100644 --- a/apps/services/stdin_command_dispatcher_utils.h +++ b/apps/services/stdin_command_dispatcher_utils.h @@ -43,7 +43,7 @@ inline expected parse_int(std::string_view value) return out_value; } - return fmt::format("Could not convert '{}' to integer", value); + return make_unexpected(fmt::format("Could not convert '{}' to integer", value)); } /// Parses hex integer values from a console command. @@ -66,7 +66,7 @@ inline expected parse_unsigned_hex(std::string_view value) return out_value; } - return fmt::format("Could not convert '{}' to integer", value); + return make_unexpected(fmt::format("Could not convert '{}' to integer", value)); } /// \brief Parses floating point values from a console command and attempts to store them in a double. @@ -83,7 +83,7 @@ inline expected parse_double(const std::string& value) double out_value = std::strtod(str_init, &str_end); if (errno == ERANGE || str_end == str_init) { - return fmt::format("Could not convert '{}' to double", value); + return make_unexpected(fmt::format("Could not convert '{}' to double", value)); } return out_value; diff --git a/apps/units/cu_cp/cu_cp_commands.h b/apps/units/cu_cp/cu_cp_commands.h index b111d04e8d..57d36dfd46 100644 --- a/apps/units/cu_cp/cu_cp_commands.h +++ b/apps/units/cu_cp/cu_cp_commands.h @@ -55,19 +55,19 @@ class handover_app_command : public app_services::application_command auto arg = args.begin(); expected serving_pci = app_services::parse_int(*arg); - if (serving_pci.is_error()) { + if (not serving_pci.has_value()) { fmt::print("Invalid serving PCI.\n"); return; } arg++; expected rnti = app_services::parse_unsigned_hex(*arg); - if (rnti.is_error()) { + if (not rnti.has_value()) { fmt::print("Invalid UE RNTI.\n"); return; } arg++; expected target_pci = app_services::parse_int(*arg); - if (target_pci.is_error()) { + if (not target_pci.has_value()) { fmt::print("Invalid target PCI.\n"); return; } diff --git a/apps/units/cu_cp/cu_cp_config_translators.cpp b/apps/units/cu_cp/cu_cp_config_translators.cpp index 5a74f60003..66ac42166f 100644 --- a/apps/units/cu_cp/cu_cp_config_translators.cpp +++ b/apps/units/cu_cp/cu_cp_config_translators.cpp @@ -23,7 +23,6 @@ #include "cu_cp_config_translators.h" #include "cu_cp_unit_config.h" #include "srsran/cu_cp/cu_cp_configuration_helpers.h" -#include "srsran/ran/nr_cgi_helpers.h" #include "srsran/rlc/rlc_config.h" #include @@ -193,6 +192,7 @@ srs_cu_cp::cu_cp_configuration srsran::generate_cu_cp_config(const cu_cp_unit_co srs_cu_cp::cu_cp_configuration out_cfg = config_helpers::make_default_cu_cp_config(); out_cfg.max_nof_dus = cu_cfg.max_nof_dus; out_cfg.max_nof_cu_ups = cu_cfg.max_nof_cu_ups; + out_cfg.max_nof_ues = cu_cfg.max_nof_ues; out_cfg.ngap_config.gnb_id = cu_cfg.gnb_id; out_cfg.ngap_config.ran_node_name = cu_cfg.ran_node_name; @@ -200,7 +200,7 @@ srs_cu_cp::cu_cp_configuration srsran::generate_cu_cp_config(const cu_cp_unit_co srsran_assert(!cu_cfg.plmns.empty(), "PLMN list is empty"); srsran_assert(!cu_cfg.tacs.empty(), "PLMN list is empty"); - out_cfg.ngap_config.plmn = cu_cfg.plmns.front(); + out_cfg.ngap_config.plmn = plmn_identity::parse(cu_cfg.plmns.front()).value(); out_cfg.ngap_config.tac = cu_cfg.tacs.front(); out_cfg.rrc_config.gnb_id = cu_cfg.gnb_id; @@ -221,7 +221,7 @@ srs_cu_cp::cu_cp_configuration srsran::generate_cu_cp_config(const cu_cp_unit_co } out_cfg.ue_config.inactivity_timer = std::chrono::seconds{cu_cfg.inactivity_timer}; - out_cfg.ue_config.max_nof_supported_ues = cu_cfg.max_nof_dus * srsran::srs_cu_cp::MAX_NOF_UES_PER_DU; + out_cfg.ue_config.max_nof_supported_ues = cu_cfg.max_nof_ues; out_cfg.ngap_config.pdu_session_setup_timeout = std::chrono::seconds{cu_cfg.pdu_session_setup_timeout}; out_cfg.statistics_report_period = std::chrono::seconds{cu_cfg.metrics.cu_cp_statistics_report_period}; @@ -234,16 +234,16 @@ srs_cu_cp::cu_cp_configuration srsran::generate_cu_cp_config(const cu_cp_unit_co // Convert appconfig's cell list into cell manager type. for (const auto& app_cfg_item : cu_cfg.mobility_config.cells) { + nr_cell_identity nci = nr_cell_identity::create(app_cfg_item.nr_cell_id).value(); srs_cu_cp::cell_meas_config meas_cfg_item; - meas_cfg_item.serving_cell_cfg.nci = app_cfg_item.nr_cell_id; + meas_cfg_item.serving_cell_cfg.nci = nci; if (app_cfg_item.periodic_report_cfg_id.has_value()) { meas_cfg_item.periodic_report_cfg_id = srs_cu_cp::uint_to_report_cfg_id(app_cfg_item.periodic_report_cfg_id.value()); } if (app_cfg_item.gnb_id_bit_length.has_value()) { - meas_cfg_item.serving_cell_cfg.gnb_id = - config_helpers::get_gnb_id(app_cfg_item.nr_cell_id, app_cfg_item.gnb_id_bit_length.value()); + meas_cfg_item.serving_cell_cfg.gnb_id = nci.gnb_id(app_cfg_item.gnb_id_bit_length.value()); } meas_cfg_item.serving_cell_cfg.pci = app_cfg_item.pci; meas_cfg_item.serving_cell_cfg.band = app_cfg_item.band; @@ -261,7 +261,7 @@ srs_cu_cp::cu_cp_configuration srsran::generate_cu_cp_config(const cu_cp_unit_co for (const auto& ncell : app_cfg_item.ncells) { srs_cu_cp::neighbor_cell_meas_config ncell_meas_cfg; - ncell_meas_cfg.nci = ncell.nr_cell_id; + ncell_meas_cfg.nci = nr_cell_identity::create(ncell.nr_cell_id).value(); for (const auto& report_id : ncell.report_cfg_ids) { ncell_meas_cfg.report_cfg_ids.push_back(srs_cu_cp::uint_to_report_cfg_id(report_id)); } diff --git a/apps/units/cu_cp/cu_cp_logger_registrator.h b/apps/units/cu_cp/cu_cp_logger_registrator.h index 99603a1f16..b2553cb5ef 100644 --- a/apps/units/cu_cp/cu_cp_logger_registrator.h +++ b/apps/units/cu_cp/cu_cp_logger_registrator.h @@ -32,28 +32,28 @@ inline void register_cu_cp_loggers(const cu_cp_unit_logger_config& log_cfg) { for (const auto& id : {"CU-CP", "CU-UEMNG", "CU-CP-E1"}) { auto& cu_cp_logger = srslog::fetch_basic_logger(id, false); - cu_cp_logger.set_level(srslog::str_to_basic_level(log_cfg.cu_level)); + cu_cp_logger.set_level(log_cfg.cu_level); cu_cp_logger.set_hex_dump_max_size(log_cfg.hex_max_size); } auto& pdcp_logger = srslog::fetch_basic_logger("PDCP", false); - pdcp_logger.set_level(srslog::str_to_basic_level(log_cfg.pdcp_level)); + pdcp_logger.set_level(log_cfg.pdcp_level); pdcp_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& rrc_logger = srslog::fetch_basic_logger("RRC", false); - rrc_logger.set_level(srslog::str_to_basic_level(log_cfg.rrc_level)); + rrc_logger.set_level(log_cfg.rrc_level); rrc_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& cu_f1ap_logger = srslog::fetch_basic_logger("CU-CP-F1", false); - cu_f1ap_logger.set_level(srslog::str_to_basic_level(log_cfg.f1ap_level)); + cu_f1ap_logger.set_level(log_cfg.f1ap_level); cu_f1ap_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& ngap_logger = srslog::fetch_basic_logger("NGAP", false); - ngap_logger.set_level(srslog::str_to_basic_level(log_cfg.ngap_level)); + ngap_logger.set_level(log_cfg.ngap_level); ngap_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& sec_logger = srslog::fetch_basic_logger("SEC", false); - sec_logger.set_level(srslog::str_to_basic_level(log_cfg.sec_level)); + sec_logger.set_level(log_cfg.sec_level); sec_logger.set_hex_dump_max_size(log_cfg.hex_max_size); } diff --git a/apps/units/cu_cp/cu_cp_unit_config.h b/apps/units/cu_cp/cu_cp_unit_config.h index c93c6cd94c..2226588943 100644 --- a/apps/units/cu_cp/cu_cp_unit_config.h +++ b/apps/units/cu_cp/cu_cp_unit_config.h @@ -25,8 +25,8 @@ #include "apps/units/cu_cp/cu_cp_unit_pcap_config.h" #include "cu_cp_unit_logger_config.h" #include "srsran/ran/five_qi.h" -#include "srsran/ran/gnb_id.h" #include "srsran/ran/nr_band.h" +#include "srsran/ran/nr_cell_identity.h" #include "srsran/ran/pci.h" #include "srsran/ran/s_nssai.h" #include @@ -247,6 +247,8 @@ struct cu_cp_unit_config { uint16_t max_nof_dus = 6; /// Maximum number of CU-UPs. uint16_t max_nof_cu_ups = 6; + /// Maximum number of UEs. + uint64_t max_nof_ues = 8192; /// Inactivity timer in seconds. int inactivity_timer = 120; /// PDU session setup timeout in seconds (must be larger than T310). 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 3a970ac449..55b9d42f27 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 @@ -21,6 +21,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/support/cli11_utils.h" #include "srsran/support/config_parsers.h" @@ -29,21 +30,12 @@ using namespace srsran; static void configure_cli11_log_args(CLI::App& app, cu_cp_unit_logger_config& log_params) { - auto level_check = [](const std::string& value) -> std::string { - if (value == "info" || value == "debug" || value == "warning" || value == "error") { - return {}; - } - return "Log level value not supported. Accepted values [info,debug,warning,error]"; - }; - - add_option(app, "--pdcp_level", log_params.pdcp_level, "PDCP log level")->capture_default_str()->check(level_check); - add_option(app, "--rrc_level", log_params.rrc_level, "RRC log level")->capture_default_str()->check(level_check); - add_option(app, "--ngap_level", log_params.ngap_level, "NGAP log level")->capture_default_str()->check(level_check); - add_option(app, "--f1ap_level", log_params.f1ap_level, "F1AP log level")->capture_default_str()->check(level_check); - add_option(app, "--cu_level", log_params.cu_level, "Log level for the CU")->capture_default_str()->check(level_check); - add_option(app, "--sec_level", log_params.sec_level, "Security functions log level") - ->capture_default_str() - ->check(level_check); + app_services::add_log_option(app, log_params.pdcp_level, "--pdcp_level", "PDCP log level"); + app_services::add_log_option(app, log_params.rrc_level, "--rrc_level", "RRC log level"); + app_services::add_log_option(app, log_params.ngap_level, "--ngap_level", "NGAP log level"); + app_services::add_log_option(app, log_params.f1ap_level, "--f1ap_level", "F1AP log level"); + app_services::add_log_option(app, log_params.cu_level, "--cu_level", "Log level for the CU"); + app_services::add_log_option(app, log_params.sec_level, "--sec_level", "Security functions log level"); add_option( app, "--hex_max_size", log_params.hex_max_size, "Maximum number of bytes to print in hex (zero for no hex dumps)") @@ -96,14 +88,16 @@ static void configure_cli11_report_args(CLI::App& app, cu_cp_unit_report_config& static void configure_cli11_ncell_args(CLI::App& app, cu_cp_unit_neighbor_cell_config_item& config) { - add_option(app, "--nr_cell_id", config.nr_cell_id, "Neighbor cell id"); + add_option(app, "--nr_cell_id", config.nr_cell_id, "Neighbor cell id") + ->check(CLI::Range(static_cast(0U), nr_cell_identity::max().value())); add_option( app, "--report_configs", config.report_cfg_ids, "Report configurations to configure for this neighbor cell"); } static void configure_cli11_cells_args(CLI::App& app, cu_cp_unit_cell_config_item& config) { - add_option(app, "--nr_cell_id", config.nr_cell_id, "Cell id to be configured"); + add_option(app, "--nr_cell_id", config.nr_cell_id, "Cell id to be configured") + ->check(CLI::Range(static_cast(0U), nr_cell_identity::max().value())); add_option(app, "--periodic_report_cfg_id", config.periodic_report_cfg_id, @@ -251,6 +245,8 @@ static void configure_cli11_cu_cp_args(CLI::App& app, cu_cp_unit_config& cu_cp_p cu_cp_params.max_nof_cu_ups, "Maximum number of CU-UP connections that the CU-CP may accept"); + add_option(app, "--max_nof_ues", cu_cp_params.max_nof_ues, "Maximum number of UEs that the CU-CP may accept"); + add_option(app, "--inactivity_timer", cu_cp_params.inactivity_timer, "UE/PDU Session/DRB inactivity timer in seconds") ->capture_default_str() ->check(CLI::Range(1, 7200)); @@ -497,9 +493,8 @@ void srsran::configure_cli11_with_cu_cp_unit_config_schema(CLI::App& app, cu_cp_ static std::vector auto_generate_plmns() { - std::vector out_cfg = {"00101"}; - - return out_cfg; + std::vector vec = {"00101"}; + return vec; } static std::vector auto_generate_tacs() diff --git a/apps/units/cu_cp/cu_cp_unit_config_validator.cpp b/apps/units/cu_cp/cu_cp_unit_config_validator.cpp index ec159ef6d7..9082f063c4 100644 --- a/apps/units/cu_cp/cu_cp_unit_config_validator.cpp +++ b/apps/units/cu_cp/cu_cp_unit_config_validator.cpp @@ -24,7 +24,6 @@ #include "srsran/adt/span.h" #include "srsran/pdcp/pdcp_t_reordering.h" #include "srsran/ran/nr_cgi.h" -#include "srsran/ran/nr_cgi_helpers.h" #include "srsran/rlc/rlc_config.h" #include #include @@ -32,7 +31,7 @@ using namespace srsran; -static bool validate_mobility_appconfig(const gnb_id_t gnb_id, const cu_cp_unit_mobility_config& config) +static bool validate_mobility_appconfig(gnb_id_t gnb_id, const cu_cp_unit_mobility_config& config) { // check that report config ids are unique std::map report_cfg_ids_to_report_type; @@ -44,12 +43,13 @@ static bool validate_mobility_appconfig(const gnb_id_t gnb_id, const cu_cp_unit_ report_cfg_ids_to_report_type.emplace(report_cfg.report_cfg_id, report_cfg.report_type); } - std::map> cell_to_report_cfg_id; + std::map> cell_to_report_cfg_id; // check cu_cp_cell_config - std::set ncis; + std::set ncis; for (const auto& cell : config.cells) { - if (!ncis.emplace(cell.nr_cell_id).second) { + nr_cell_identity nci = nr_cell_identity::create(cell.nr_cell_id).value(); + if (!ncis.emplace(nci).second) { fmt::print("Cells must be unique ({:#x} already present)\n", cell.nr_cell_id); return false; } @@ -62,8 +62,8 @@ static bool validate_mobility_appconfig(const gnb_id_t gnb_id, const cu_cp_unit_ if (cell.periodic_report_cfg_id.has_value()) { // try to add report config id to cell_to_report_cfg_id map - cell_to_report_cfg_id.emplace(cell.nr_cell_id, std::set()); - auto& report_cfg_ids = cell_to_report_cfg_id.at(cell.nr_cell_id); + cell_to_report_cfg_id.emplace(nci, std::set()); + auto& report_cfg_ids = cell_to_report_cfg_id.at(nci); if (!report_cfg_ids.emplace(cell.periodic_report_cfg_id.value()).second) { fmt::print("cell={}: report_config_id={} already configured for this cell)\n", cell.nr_cell_id, @@ -78,7 +78,7 @@ static bool validate_mobility_appconfig(const gnb_id_t gnb_id, const cu_cp_unit_ } // check if cell is an external managed cell - if (config_helpers::get_gnb_id(cell.nr_cell_id, gnb_id.bit_length) != gnb_id) { + if (nci.gnb_id(gnb_id.bit_length) != gnb_id) { if (!cell.gnb_id_bit_length.has_value() || !cell.pci.has_value() || !cell.band.has_value() || !cell.ssb_arfcn.has_value() || !cell.ssb_scs.has_value() || !cell.ssb_period.has_value() || !cell.ssb_offset.has_value() || !cell.ssb_duration.has_value()) { @@ -104,8 +104,8 @@ static bool validate_mobility_appconfig(const gnb_id_t gnb_id, const cu_cp_unit_ for (const auto& ncell : cell.ncells) { // try to add report config ids to cell_to_report_cfg_id map for (const auto& id : ncell.report_cfg_ids) { - if (cell_to_report_cfg_id.find(ncell.nr_cell_id) != cell_to_report_cfg_id.end() && - !cell_to_report_cfg_id.at(ncell.nr_cell_id).emplace(id).second) { + if (cell_to_report_cfg_id.find(nci) != cell_to_report_cfg_id.end() && + !cell_to_report_cfg_id.at(nci).emplace(id).second) { fmt::print("cell={}: report_config_id={} already configured for this cell\n", ncell.nr_cell_id, id); return false; } @@ -115,8 +115,9 @@ static bool validate_mobility_appconfig(const gnb_id_t gnb_id, const cu_cp_unit_ // verify that each configured neighbor cell is present for (const auto& cell : config.cells) { + nr_cell_identity nci = nr_cell_identity::create(cell.nr_cell_id).value(); for (const auto& ncell : cell.ncells) { - if (ncis.find(ncell.nr_cell_id) == ncis.end()) { + if (ncis.find(nci) == ncis.end()) { fmt::print("Neighbor cell config for nci={:#x} incomplete. No valid configuration for cell nci={:#x} found.\n", cell.nr_cell_id, ncell.nr_cell_id); diff --git a/apps/units/cu_cp/cu_cp_unit_logger_config.h b/apps/units/cu_cp/cu_cp_unit_logger_config.h index 5115cf4d98..f3f8683ca3 100644 --- a/apps/units/cu_cp/cu_cp_unit_logger_config.h +++ b/apps/units/cu_cp/cu_cp_unit_logger_config.h @@ -22,18 +22,18 @@ #pragma once -#include +#include "srsran/srslog/logger.h" namespace srsran { /// Configuration of logging functionalities. struct cu_cp_unit_logger_config { - std::string cu_level = "warning"; - std::string f1ap_level = "warning"; - std::string pdcp_level = "warning"; - std::string rrc_level = "warning"; - std::string ngap_level = "warning"; - std::string sec_level = "warning"; + srslog::basic_levels cu_level = srslog::basic_levels::warning; + srslog::basic_levels f1ap_level = srslog::basic_levels::warning; + srslog::basic_levels pdcp_level = srslog::basic_levels::warning; + srslog::basic_levels rrc_level = srslog::basic_levels::warning; + srslog::basic_levels ngap_level = srslog::basic_levels::warning; + srslog::basic_levels sec_level = srslog::basic_levels::warning; /// Maximum number of bytes to write when dumping hex arrays. int hex_max_size = 0; /// Enable JSON generation for the F1AP Tx and Rx PDUs. diff --git a/apps/units/cu_up/cu_up_logger_registrator.h b/apps/units/cu_up/cu_up_logger_registrator.h index 1f8f93e205..c6b4a94d92 100644 --- a/apps/units/cu_up/cu_up_logger_registrator.h +++ b/apps/units/cu_up/cu_up_logger_registrator.h @@ -32,24 +32,24 @@ inline void register_cu_up_loggers(const cu_up_unit_logger_config& log_cfg) { for (const auto& id : {"CU-UP", "CU-UP-E1"}) { auto& cu_up_logger = srslog::fetch_basic_logger(id, false); - cu_up_logger.set_level(srslog::str_to_basic_level(log_cfg.cu_level)); + cu_up_logger.set_level(log_cfg.cu_level); cu_up_logger.set_hex_dump_max_size(log_cfg.hex_max_size); } auto& pdcp_logger = srslog::fetch_basic_logger("PDCP", false); - pdcp_logger.set_level(srslog::str_to_basic_level(log_cfg.pdcp_level)); + pdcp_logger.set_level(log_cfg.pdcp_level); pdcp_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& f1u_logger = srslog::fetch_basic_logger("CU-F1-U", false); - f1u_logger.set_level(srslog::str_to_basic_level(log_cfg.f1u_level)); + f1u_logger.set_level(log_cfg.f1u_level); f1u_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& gtpu_logger = srslog::fetch_basic_logger("GTPU", false); - gtpu_logger.set_level(srslog::str_to_basic_level(log_cfg.gtpu_level)); + gtpu_logger.set_level(log_cfg.gtpu_level); gtpu_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& sdap_logger = srslog::fetch_basic_logger("SDAP", false); - sdap_logger.set_level(srslog::str_to_basic_level(log_cfg.sdap_level)); + sdap_logger.set_level(log_cfg.sdap_level); sdap_logger.set_hex_dump_max_size(log_cfg.hex_max_size); } diff --git a/apps/units/cu_up/cu_up_unit_config_cli11_schema.cpp b/apps/units/cu_up/cu_up_unit_config_cli11_schema.cpp index 88089c1384..0697fc6a17 100644 --- a/apps/units/cu_up/cu_up_unit_config_cli11_schema.cpp +++ b/apps/units/cu_up/cu_up_unit_config_cli11_schema.cpp @@ -21,6 +21,7 @@ */ #include "cu_up_unit_config_cli11_schema.h" +#include "apps/services/logger/logger_appconfig_cli11_utils.h" #include "apps/units/cu_up/cu_up_unit_config.h" #include "apps/units/cu_up/cu_up_unit_pcap_config.h" #include "srsran/support/cli11_utils.h" @@ -46,18 +47,11 @@ static void configure_cli11_cu_up_args(CLI::App& app, cu_up_unit_config& cu_up_p static void configure_cli11_log_args(CLI::App& app, cu_up_unit_logger_config& log_params) { - auto level_check = [](const std::string& value) -> std::string { - if (value == "info" || value == "debug" || value == "warning" || value == "error") { - return {}; - } - return "Log level value not supported. Accepted values [info,debug,warning,error]"; - }; - - add_option(app, "--pdcp_level", log_params.pdcp_level, "PDCP log level")->capture_default_str()->check(level_check); - add_option(app, "--sdap_level", log_params.sdap_level, "SDAP log level")->capture_default_str()->check(level_check); - add_option(app, "--gtpu_level", log_params.gtpu_level, "GTPU log level")->capture_default_str()->check(level_check); - add_option(app, "--f1u_level", log_params.f1u_level, "F1-U log level")->capture_default_str()->check(level_check); - add_option(app, "--cu_level", log_params.cu_level, "Log level for the CU")->capture_default_str()->check(level_check); + app_services::add_log_option(app, log_params.pdcp_level, "--pdcp_level", "PDCP log level"); + app_services::add_log_option(app, log_params.sdap_level, "--sdap_level", "SDAP log level"); + app_services::add_log_option(app, log_params.gtpu_level, "--gtpu_level", "GTPU log level"); + app_services::add_log_option(app, log_params.f1u_level, "--f1u_level", "F1-U log level"); + app_services::add_log_option(app, log_params.cu_level, "--cu_level", "Log level for the CU"); add_option( app, "--hex_max_size", log_params.hex_max_size, "Maximum number of bytes to print in hex (zero for no hex dumps)") diff --git a/apps/units/cu_up/cu_up_unit_logger_config.h b/apps/units/cu_up/cu_up_unit_logger_config.h index 59301876ed..7144ebc00b 100644 --- a/apps/units/cu_up/cu_up_unit_logger_config.h +++ b/apps/units/cu_up/cu_up_unit_logger_config.h @@ -22,17 +22,17 @@ #pragma once -#include +#include "srsran/srslog/logger.h" namespace srsran { /// Configuration of logging functionalities. struct cu_up_unit_logger_config { - std::string cu_level = "warning"; - std::string gtpu_level = "warning"; - std::string pdcp_level = "warning"; - std::string f1u_level = "warning"; - std::string sdap_level = "warning"; + srslog::basic_levels cu_level = srslog::basic_levels::warning; + srslog::basic_levels gtpu_level = srslog::basic_levels::warning; + srslog::basic_levels pdcp_level = srslog::basic_levels::warning; + srslog::basic_levels f1u_level = srslog::basic_levels::warning; + srslog::basic_levels sdap_level = srslog::basic_levels::warning; /// Maximum number of bytes to write when dumping hex arrays. int hex_max_size = 0; }; diff --git a/apps/units/cu_up/cu_up_wrapper.cpp b/apps/units/cu_up/cu_up_wrapper.cpp index 5e54996578..d0383d9fea 100644 --- a/apps/units/cu_up/cu_up_wrapper.cpp +++ b/apps/units/cu_up/cu_up_wrapper.cpp @@ -47,11 +47,6 @@ std::optional cu_up_wrapper::get_n3_bind_port() return cu_up->get_n3_bind_port(); } -gtpu_demux_rx_upper_layer_interface& cu_up_wrapper::get_ngu_pdu_handler() -{ - return cu_up->get_ngu_pdu_handler(); -} - e1ap_message_handler& cu_up_wrapper::get_e1ap_message_handler() { return cu_up->get_e1ap_message_handler(); @@ -83,8 +78,16 @@ cu_up_wrapper::handle_bearer_context_setup_request(const srs_cu_up::e1ap_bearer_ return cu_up->handle_bearer_context_setup_request(msg); } -srs_cu_up::e1ap_bearer_context_modification_response cu_up_wrapper::handle_bearer_context_modification_request( +// TODO remove from public interface +async_task +cu_up_wrapper::handle_bearer_context_modification_request( const srs_cu_up::e1ap_bearer_context_modification_request& msg) { return cu_up->handle_bearer_context_modification_request(msg); } + +// TODO remove from public interface +void cu_up_wrapper::schedule_ue_async_task(srs_cu_up::ue_index_t ue_index, async_task task) +{ + cu_up->schedule_ue_async_task(ue_index, std::move(task)); +} diff --git a/apps/units/cu_up/cu_up_wrapper.h b/apps/units/cu_up/cu_up_wrapper.h index 23076095e0..ed05279863 100644 --- a/apps/units/cu_up/cu_up_wrapper.h +++ b/apps/units/cu_up/cu_up_wrapper.h @@ -45,9 +45,6 @@ class cu_up_wrapper : public srs_cu_up::cu_up_interface // See interface for documentation. std::optional get_n3_bind_port() override; - // See interface for documentation. - gtpu_demux_rx_upper_layer_interface& get_ngu_pdu_handler() override; - // See interface for documentation. e1ap_message_handler& get_e1ap_message_handler() override; @@ -68,9 +65,13 @@ class cu_up_wrapper : public srs_cu_up::cu_up_interface handle_bearer_context_setup_request(const srs_cu_up::e1ap_bearer_context_setup_request& msg) override; // See interface for documentation. - srs_cu_up::e1ap_bearer_context_modification_response + async_task handle_bearer_context_modification_request(const srs_cu_up::e1ap_bearer_context_modification_request& msg) override; + /// \brief Schedule an async task for an UE. + /// Can be used to initiate UE routines. + void schedule_ue_async_task(srs_cu_up::ue_index_t ue_index, async_task task) override; + private: std::unique_ptr gateway; std::unique_ptr cu_up; diff --git a/apps/units/flexible_du/du_high/du_high_config.h b/apps/units/flexible_du/du_high/du_high_config.h index f77a9812df..4f02222608 100644 --- a/apps/units/flexible_du/du_high/du_high_config.h +++ b/apps/units/flexible_du/du_high/du_high_config.h @@ -42,6 +42,7 @@ #include "srsran/ran/sib/system_info_config.h" #include "srsran/ran/slot_pdu_capacity_constants.h" #include "srsran/ran/subcarrier_spacing.h" +#include "srsran/scheduler/config/scheduler_expert_config.h" #include "srsran/srslog/srslog.h" #include #include @@ -51,11 +52,11 @@ namespace srsran { /// DU high logging functionalities. struct du_high_unit_logger_config { - std::string du_level = "warning"; - std::string mac_level = "warning"; - std::string rlc_level = "warning"; - std::string f1ap_level = "warning"; - std::string f1u_level = "warning"; + srslog::basic_levels du_level = srslog::basic_levels::warning; + srslog::basic_levels mac_level = srslog::basic_levels::warning; + srslog::basic_levels rlc_level = srslog::basic_levels::warning; + srslog::basic_levels f1ap_level = srslog::basic_levels::warning; + srslog::basic_levels f1u_level = srslog::basic_levels::warning; /// Maximum number of bytes to write when dumping hex arrays. int hex_max_size = 0; @@ -65,6 +66,12 @@ struct du_high_unit_logger_config { bool f1ap_json_enabled = false; }; +/// Scheduler expert configuration. +struct du_high_unit_scheduler_expert_config { + /// Policy scheduler expert parameters. + policy_scheduler_expert_config policy_sched_expert_cfg = time_rr_scheduler_expert_config{}; +}; + struct du_high_unit_ssb_config { /// SSB period in milliseconds. unsigned ssb_period_msec = 10; @@ -484,7 +491,7 @@ struct du_high_unit_prach_config { /// Indicates the number of Contention Based preambles per SSB (L1 parameter 'CB-preambles-per-SSB'). See TS 38.331, /// \c ssb-perRACH-OccasionAndCB-PreamblesPerSSB. /// \remark Values of \c cb_preambles_per_ssb depends on value of \c ssb_per_ro. - uint8_t nof_cb_preambles_per_ssb = 4; + uint8_t nof_cb_preambles_per_ssb = 64; }; /// Base cell configuration. @@ -537,6 +544,8 @@ struct du_high_unit_base_cell_config { du_high_unit_paging_config paging_cfg; /// CSI configuration. du_high_unit_csi_config csi_cfg; + /// Scheduler expert configuration. + du_high_unit_scheduler_expert_config sched_expert_cfg; }; struct du_high_unit_test_mode_ue_config { diff --git a/apps/units/flexible_du/du_high/du_high_config_cli11_schema.cpp b/apps/units/flexible_du/du_high/du_high_config_cli11_schema.cpp index 19068169a2..76f16045dc 100644 --- a/apps/units/flexible_du/du_high/du_high_config_cli11_schema.cpp +++ b/apps/units/flexible_du/du_high/du_high_config_cli11_schema.cpp @@ -21,6 +21,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 "du_high_config.h" #include "srsran/ran/du_types.h" @@ -36,26 +37,20 @@ static expected parse_int(const std::string& value) try { return std::stoi(value); } catch (const std::invalid_argument& e) { - return {e.what()}; + return make_unexpected(e.what()); } catch (const std::out_of_range& e) { - return {e.what()}; + return make_unexpected(e.what()); } } static void configure_cli11_log_args(CLI::App& app, du_high_unit_logger_config& log_params) { - auto level_check = [](const std::string& value) -> std::string { - if (value == "info" || value == "debug" || value == "warning" || value == "error") { - return {}; - } - return "Log level value not supported. Accepted values [info,debug,warning,error]"; - }; + app_services::add_log_option(app, log_params.mac_level, "--mac_level", "MAC log level"); + app_services::add_log_option(app, log_params.rlc_level, "--rlc_level", "RLC log level"); + app_services::add_log_option(app, log_params.f1ap_level, "--f1ap_level", "F1AP log level"); + app_services::add_log_option(app, log_params.f1u_level, "--f1u_level", "F1-U log level"); + app_services::add_log_option(app, log_params.du_level, "--du_level", "Log level for the DU"); - add_option(app, "--mac_level", log_params.mac_level, "MAC log level")->capture_default_str()->check(level_check); - add_option(app, "--rlc_level", log_params.rlc_level, "RLC log level")->capture_default_str()->check(level_check); - add_option(app, "--f1ap_level", log_params.f1ap_level, "F1AP log level")->capture_default_str()->check(level_check); - add_option(app, "--f1u_level", log_params.f1u_level, "F1-U log level")->capture_default_str()->check(level_check); - add_option(app, "--du_level", log_params.du_level, "Log level for the DU")->capture_default_str()->check(level_check); add_option( app, "--hex_max_size", log_params.hex_max_size, "Maximum number of bytes to print in hex (zero for no hex dumps)") ->capture_default_str() @@ -510,6 +505,38 @@ static void configure_cli11_csi_args(CLI::App& app, du_high_unit_csi_config& csi ->check(CLI::Range(-8, 15)); } +static void configure_cli11_pf_scheduler_expert_args(CLI::App& app, time_pf_scheduler_expert_config& expert_params) +{ + add_option(app, + "--pf_sched_fairness_coeff", + expert_params.pf_sched_fairness_coeff, + "Fairness Coefficient to use in Proportional Fair policy scheduler") + ->capture_default_str(); +} + +static void configure_cli11_policy_scheduler_expert_args(CLI::App& app, + du_high_unit_scheduler_expert_config& expert_params) +{ + static time_pf_scheduler_expert_config pf_sched_expert_cfg; + CLI::App* pf_sched_cfg_subcmd = + add_subcommand(app, "pf_sched", "Proportional Fair policy scheduler expert configuration")->configurable(); + configure_cli11_pf_scheduler_expert_args(*pf_sched_cfg_subcmd, pf_sched_expert_cfg); + auto pf_sched_verify_callback = [&]() { + CLI::App* pf_sched_sub_cmd = app.get_subcommand("pf_sched"); + if (pf_sched_sub_cmd->count() != 0) { + expert_params.policy_sched_expert_cfg = pf_sched_expert_cfg; + } + }; + pf_sched_cfg_subcmd->parse_complete_callback(pf_sched_verify_callback); +} + +static void configure_cli11_scheduler_expert_args(CLI::App& app, du_high_unit_scheduler_expert_config& expert_params) +{ + CLI::App* policy_sched_cfg_subcmd = + add_subcommand(app, "policy_sched_cfg", "Policy scheduler expert configuration")->configurable(); + configure_cli11_policy_scheduler_expert_args(*policy_sched_cfg_subcmd, expert_params); +} + static void configure_cli11_ul_common_args(CLI::App& app, du_high_unit_ul_common_config& ul_common_params) { add_option(app, "--p_max", ul_common_params.p_max, "Maximum transmit power allowed in this serving cell") @@ -1103,6 +1130,10 @@ static void configure_cli11_common_cell_args(CLI::App& app, du_high_unit_base_ce // CSI configuration. CLI::App* csi_subcmd = add_subcommand(app, "csi", "CSI-Meas parameters"); configure_cli11_csi_args(*csi_subcmd, cell_params.csi_cfg); + + // Scheduler expert configuration. + CLI::App* sched_expert_subcmd = add_subcommand(app, "sched_expert_cfg", "Scheduler expert parameters"); + configure_cli11_scheduler_expert_args(*sched_expert_subcmd, cell_params.sched_expert_cfg); } static void configure_cli11_cells_args(CLI::App& app, du_high_unit_cell_config& cell_params) diff --git a/apps/units/flexible_du/du_high/du_high_config_translators.cpp b/apps/units/flexible_du/du_high/du_high_config_translators.cpp index 11f7738f62..e68e1bfd58 100644 --- a/apps/units/flexible_du/du_high/du_high_config_translators.cpp +++ b/apps/units/flexible_du/du_high/du_high_config_translators.cpp @@ -289,8 +289,8 @@ std::vector srsran::generate_du_cell_config(const du_high_unit_c du_cell_config& out_cell = out_cfg.back(); // Set the rest of the parameters. - out_cell.nr_cgi.plmn = base_cell.plmn; - out_cell.nr_cgi.nci = config_helpers::make_nr_cell_identity(config.gnb_id, cell_id); + out_cell.nr_cgi.plmn_id = plmn_identity::parse(base_cell.plmn).value(); + out_cell.nr_cgi.nci = nr_cell_identity::create(config.gnb_id, cell_id).value(); out_cell.tac = base_cell.tac; out_cell.searchspace0_idx = param.search_space0_index; @@ -785,9 +785,10 @@ scheduler_expert_config srsran::generate_scheduler_expert_config(const du_high_u const du_high_unit_base_cell_config& cell = config.cells_cfg.front().cell; // UE parameters. - const du_high_unit_pdsch_config& pdsch = cell.pdsch_cfg; - const du_high_unit_pusch_config& pusch = cell.pusch_cfg; - out_cfg.ue.dl_mcs = {pdsch.min_ue_mcs, pdsch.max_ue_mcs}; + const du_high_unit_pdsch_config& pdsch = cell.pdsch_cfg; + const du_high_unit_pusch_config& pusch = cell.pusch_cfg; + const du_high_unit_scheduler_expert_config& app_sched_expert_cfg = cell.sched_expert_cfg; + out_cfg.ue.dl_mcs = {pdsch.min_ue_mcs, pdsch.max_ue_mcs}; out_cfg.ue.pdsch_rv_sequence.assign(pdsch.rv_sequence.begin(), pdsch.rv_sequence.end()); out_cfg.ue.dl_harq_la_cqi_drop_threshold = pdsch.harq_la_cqi_drop_threshold; out_cfg.ue.dl_harq_la_ri_drop_threshold = pdsch.harq_la_ri_drop_threshold; @@ -811,6 +812,9 @@ scheduler_expert_config srsran::generate_scheduler_expert_config(const du_high_u out_cfg.ue.olla_max_ul_snr_offset = pusch.olla_max_snr_offset; out_cfg.ue.pdsch_crb_limits = {pdsch.start_rb, pdsch.end_rb}; out_cfg.ue.pusch_crb_limits = {pusch.start_rb, pusch.end_rb}; + if (std::holds_alternative(app_sched_expert_cfg.policy_sched_expert_cfg)) { + out_cfg.ue.strategy_cfg = app_sched_expert_cfg.policy_sched_expert_cfg; + } // PUCCH and scheduler expert parameters. out_cfg.ue.max_ul_grants_per_slot = cell.ul_common_cfg.max_ul_grants_per_slot; diff --git a/apps/units/flexible_du/du_high/du_high_config_validator.cpp b/apps/units/flexible_du/du_high/du_high_config_validator.cpp index 192a98c6f6..165342373c 100644 --- a/apps/units/flexible_du/du_high/du_high_config_validator.cpp +++ b/apps/units/flexible_du/du_high/du_high_config_validator.cpp @@ -452,21 +452,21 @@ validate_prach_cell_unit_config(const du_high_unit_prach_config& config, nr_band auto code = prach_helper::prach_config_index_is_valid(config.prach_config_index.value(), band_helper::get_duplex_mode(band)); - if (code.is_error()) { + if (not code.has_value()) { fmt::print("{}", code.error()); return false; } code = prach_helper::zero_correlation_zone_is_valid( config.zero_correlation_zone, config.prach_config_index.value(), band_helper::get_duplex_mode(band)); - if (code.is_error()) { + if (not code.has_value()) { fmt::print("{}", code.error()); return false; } code = prach_helper::nof_ssb_per_ro_and_nof_cb_preambles_per_ssb_is_valid(config.nof_ssb_per_ro, config.nof_cb_preambles_per_ssb); - if (code.is_error()) { + if (not code.has_value()) { fmt::print("{}", code.error()); return false; } @@ -602,7 +602,7 @@ static bool validate_dl_arfcn_and_band(const du_high_unit_base_cell_config& conf if (config.band.has_value()) { error_type ret = band_helper::is_dl_arfcn_valid_given_band( *config.band, config.dl_arfcn, config.common_scs, config.channel_bw_mhz); - if (ret.is_error()) { + if (not ret.has_value()) { fmt::print("Invalid DL ARFCN={} for band {}. Cause: {}.\n", config.dl_arfcn, band, ret.error()); return false; } diff --git a/apps/units/flexible_du/du_high/du_high_logger_registrator.h b/apps/units/flexible_du/du_high/du_high_logger_registrator.h index d4375ff38e..f773ec25ac 100644 --- a/apps/units/flexible_du/du_high/du_high_logger_registrator.h +++ b/apps/units/flexible_du/du_high/du_high_logger_registrator.h @@ -31,26 +31,26 @@ inline void register_du_high_loggers(const du_high_unit_logger_config& log_cfg) { for (const auto& id : {"DU", "DU-MNG", "UE-MNG"}) { auto& du_logger = srslog::fetch_basic_logger(id, false); - du_logger.set_level(srslog::str_to_basic_level(log_cfg.du_level)); + du_logger.set_level(log_cfg.du_level); du_logger.set_hex_dump_max_size(log_cfg.hex_max_size); } for (const auto& id : {"MAC", "SCHED"}) { auto& mac_logger = srslog::fetch_basic_logger(id, true); - mac_logger.set_level(srslog::str_to_basic_level(log_cfg.mac_level)); + mac_logger.set_level(log_cfg.mac_level); mac_logger.set_hex_dump_max_size(log_cfg.hex_max_size); } auto& rlc_logger = srslog::fetch_basic_logger("RLC", false); - rlc_logger.set_level(srslog::str_to_basic_level(log_cfg.rlc_level)); + rlc_logger.set_level(log_cfg.rlc_level); rlc_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& du_f1ap_logger = srslog::fetch_basic_logger("DU-F1", false); - du_f1ap_logger.set_level(srslog::str_to_basic_level(log_cfg.f1ap_level)); + du_f1ap_logger.set_level(log_cfg.f1ap_level); du_f1ap_logger.set_hex_dump_max_size(log_cfg.hex_max_size); auto& f1u_logger = srslog::fetch_basic_logger("DU-F1-U", false); - f1u_logger.set_level(srslog::str_to_basic_level(log_cfg.f1u_level)); + f1u_logger.set_level(log_cfg.f1u_level); f1u_logger.set_hex_dump_max_size(log_cfg.hex_max_size); } diff --git a/apps/units/flexible_du/du_low/du_low_config.h b/apps/units/flexible_du/du_low/du_low_config.h index c3a74fccad..dbc3f72db7 100644 --- a/apps/units/flexible_du/du_low/du_low_config.h +++ b/apps/units/flexible_du/du_low/du_low_config.h @@ -57,7 +57,7 @@ struct du_low_unit_expert_upper_phy_config { /// DU low logging functionalities. struct du_low_unit_logger_config { - std::string phy_level = "warning"; + srslog::basic_levels phy_level = srslog::basic_levels::warning; /// Set to true to log broadcasting messages and all PRACH opportunities. bool broadcast_enabled = false; /// Maximum number of bytes to write when dumping hex arrays. diff --git a/apps/units/flexible_du/du_low/du_low_config_cli11_schema.cpp b/apps/units/flexible_du/du_low/du_low_config_cli11_schema.cpp index dd6dccb8c6..6d22db0632 100644 --- a/apps/units/flexible_du/du_low/du_low_config_cli11_schema.cpp +++ b/apps/units/flexible_du/du_low/du_low_config_cli11_schema.cpp @@ -21,6 +21,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 "du_low_config.h" #include "srsran/adt/expected.h" @@ -35,22 +36,16 @@ static expected parse_int(const std::string& value) try { return std::stoi(value); } catch (const std::invalid_argument& e) { - return {e.what()}; + return make_unexpected(e.what()); } catch (const std::out_of_range& e) { - return {e.what()}; + return make_unexpected(e.what()); } } static void configure_cli11_log_args(CLI::App& app, du_low_unit_logger_config& log_params) { - auto level_check = [](const std::string& value) -> std::string { - if (value == "info" || value == "debug" || value == "warning" || value == "error") { - return {}; - } - return "Log level value not supported. Accepted values [info,debug,warning,error]"; - }; + app_services::add_log_option(app, log_params.phy_level, "--phy_level", "PHY log level"); - add_option(app, "--phy_level", log_params.phy_level, "PHY log level")->capture_default_str()->check(level_check); add_option(app, "--broadcast_enabled", log_params.broadcast_enabled, diff --git a/apps/units/flexible_du/du_low/du_low_config_translator.cpp b/apps/units/flexible_du/du_low/du_low_config_translator.cpp index 95097624bb..c6b4ddbc5d 100644 --- a/apps/units/flexible_du/du_low/du_low_config_translator.cpp +++ b/apps/units/flexible_du/du_low/du_low_config_translator.cpp @@ -121,7 +121,7 @@ static void generate_du_low_config(du_low_config& out_config, } upper_phy_cell.nof_slots_request_headroom = du_low.expert_phy_cfg.nof_slots_request_headroom; - upper_phy_cell.log_level = srslog::str_to_basic_level(du_low.loggers.phy_level); + 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; upper_phy_cell.rx_symbol_printer_port = du_low.loggers.phy_rx_symbols_port; diff --git a/apps/units/flexible_du/du_low/du_low_logger_registrator.h b/apps/units/flexible_du/du_low/du_low_logger_registrator.h index d4efba623f..6e4bd569d5 100644 --- a/apps/units/flexible_du/du_low/du_low_logger_registrator.h +++ b/apps/units/flexible_du/du_low/du_low_logger_registrator.h @@ -31,7 +31,7 @@ inline void register_du_low_loggers(const du_low_unit_logger_config& log_cfg) { // Set layer-specific logging options. auto& phy_logger = srslog::fetch_basic_logger("PHY", true); - phy_logger.set_level(srslog::str_to_basic_level(log_cfg.phy_level)); + phy_logger.set_level(log_cfg.phy_level); phy_logger.set_hex_dump_max_size(log_cfg.hex_max_size); } diff --git a/apps/units/flexible_du/fapi/fapi_config.h b/apps/units/flexible_du/fapi/fapi_config.h index a305c6981d..a1ec0abd61 100644 --- a/apps/units/flexible_du/fapi/fapi_config.h +++ b/apps/units/flexible_du/fapi/fapi_config.h @@ -31,7 +31,7 @@ struct fapi_unit_config { /// Number of slots the L2 is running ahead of the L1. unsigned l2_nof_slots_ahead = 0; /// FAPI log level. - std::string fapi_level = "warning"; + srslog::basic_levels fapi_level = srslog::basic_levels::warning; }; } // namespace srsran diff --git a/apps/units/flexible_du/fapi/fapi_config_cli11_schema.cpp b/apps/units/flexible_du/fapi/fapi_config_cli11_schema.cpp index 3fcb1463b3..dd5e6e81be 100644 --- a/apps/units/flexible_du/fapi/fapi_config_cli11_schema.cpp +++ b/apps/units/flexible_du/fapi/fapi_config_cli11_schema.cpp @@ -21,6 +21,7 @@ */ #include "fapi_config_cli11_schema.h" +#include "apps/services/logger/logger_appconfig_cli11_utils.h" #include "fapi_config.h" #include "srsran/support/cli11_utils.h" @@ -28,14 +29,7 @@ using namespace srsran; static void configure_cli11_log_args(CLI::App& app, fapi_unit_config& log_params) { - auto level_check = [](const std::string& value) -> std::string { - if (value == "info" || value == "debug" || value == "warning" || value == "error") { - return {}; - } - return "Log level value not supported. Accepted values [info,debug,warning,error]"; - }; - - add_option(app, "--fapi_level", log_params.fapi_level, "FAPI log level")->capture_default_str()->check(level_check); + app_services::add_log_option(app, log_params.fapi_level, "--fapi_level", "FAPI log level"); } static void configure_cli11_fapi_args(CLI::App& app, fapi_unit_config& config) diff --git a/apps/units/flexible_du/fapi/fapi_logger_registrator.h b/apps/units/flexible_du/fapi/fapi_logger_registrator.h index cd66899918..db7d67c967 100644 --- a/apps/units/flexible_du/fapi/fapi_logger_registrator.h +++ b/apps/units/flexible_du/fapi/fapi_logger_registrator.h @@ -30,7 +30,7 @@ namespace srsran { inline void register_fapi_loggers(const fapi_unit_config& log_cfg) { auto& fapi_logger = srslog::fetch_basic_logger("FAPI", true); - fapi_logger.set_level(srslog::str_to_basic_level(log_cfg.fapi_level)); + fapi_logger.set_level(log_cfg.fapi_level); } } // namespace srsran diff --git a/apps/units/flexible_du/flexible_du_commands.h b/apps/units/flexible_du/flexible_du_commands.h index e91eaa26eb..c11830768e 100644 --- a/apps/units/flexible_du/flexible_du_commands.h +++ b/apps/units/flexible_du/flexible_du_commands.h @@ -53,12 +53,12 @@ class tx_gain_app_command : public app_services::application_command } expected port_id = app_services::parse_int(args.front()); - if (port_id.is_error()) { + if (not port_id.has_value()) { fmt::print("Invalid port ID.\n"); return; } expected gain_dB = app_services::parse_double(args.back()); - if (gain_dB.is_error()) { + if (not gain_dB.has_value()) { fmt::print("Invalid gain value.\n"); return; } @@ -95,12 +95,12 @@ class rx_gain_app_command : public app_services::application_command } expected port_id = app_services::parse_int(args.front()); - if (port_id.is_error()) { + if (not port_id.has_value()) { fmt::print("Invalid port ID.\n"); return; } expected gain_dB = app_services::parse_double(args.back()); - if (gain_dB.is_error()) { + if (not gain_dB.has_value()) { fmt::print("Invalid gain value.\n"); return; } @@ -188,14 +188,14 @@ class change_log_level_app_command : public app_services::application_command } // Convert to enum and check if it is valid. - srslog::basic_levels level = srslog::str_to_basic_level(level_str); - if (level_str != basic_level_to_string(level)) { + auto level = srslog::str_to_basic_level(level_str); + if (!level.has_value()) { fmt::print("Invalid {} log level. Valid levels are: none, error, warning, info and debug\n", args.back()); return; } srslog::basic_logger& channel = srslog::fetch_basic_logger(channel_str); - channel.set_level(level); + channel.set_level(level.value()); } }; diff --git a/apps/units/flexible_du/split_7_2/ru_ofh_config.h b/apps/units/flexible_du/split_7_2/ru_ofh_config.h index 6e3948d48e..96eeec5c97 100644 --- a/apps/units/flexible_du/split_7_2/ru_ofh_config.h +++ b/apps/units/flexible_du/split_7_2/ru_ofh_config.h @@ -114,7 +114,7 @@ struct ru_ofh_unit_cell_config { /// RU OFH logging functionalities. struct ru_ofh_unit_logger_config { - std::string ofh_level = "warning"; + srslog::basic_levels ofh_level = srslog::basic_levels::warning; }; /// CPU affinities configuration for the cell. diff --git a/apps/units/flexible_du/split_7_2/ru_ofh_config_cli11_schema.cpp b/apps/units/flexible_du/split_7_2/ru_ofh_config_cli11_schema.cpp index 32c277a58b..e4b2ef3548 100644 --- a/apps/units/flexible_du/split_7_2/ru_ofh_config_cli11_schema.cpp +++ b/apps/units/flexible_du/split_7_2/ru_ofh_config_cli11_schema.cpp @@ -21,6 +21,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 "ru_ofh_config.h" #include "srsran/support/cli11_utils.h" @@ -249,16 +250,7 @@ static void configure_cli11_ru_ofh_args(CLI::App& app, ru_ofh_unit_parsed_config static void configure_cli11_log_args(CLI::App& app, ru_ofh_unit_logger_config& log_params) { - auto level_check = [](const std::string& value) -> std::string { - if (value == "info" || value == "debug" || value == "warning" || value == "error") { - return {}; - } - return "Log level value not supported. Accepted values [info,debug,warning,error]"; - }; - - add_option(app, "--ofh_level", log_params.ofh_level, "Open Fronthaul log level") - ->capture_default_str() - ->check(level_check); + app_services::add_log_option(app, log_params.ofh_level, "--ofh_level", "Open Fronthaul log level"); } static void configure_cli11_cell_affinity_args(CLI::App& app, ru_ofh_unit_cpu_affinities_cell_config& config) diff --git a/apps/units/flexible_du/split_7_2/ru_ofh_logger_registrator.h b/apps/units/flexible_du/split_7_2/ru_ofh_logger_registrator.h index dd3c2740cf..7f3ae2273f 100644 --- a/apps/units/flexible_du/split_7_2/ru_ofh_logger_registrator.h +++ b/apps/units/flexible_du/split_7_2/ru_ofh_logger_registrator.h @@ -30,7 +30,7 @@ namespace srsran { inline void register_ru_ofh_loggers(const ru_ofh_unit_logger_config& log_cfg) { srslog::basic_logger& ofh_logger = srslog::fetch_basic_logger("OFH", false); - ofh_logger.set_level(srslog::str_to_basic_level(log_cfg.ofh_level)); + ofh_logger.set_level(log_cfg.ofh_level); } } // namespace srsran diff --git a/apps/units/flexible_du/split_8/ru_sdr_config.h b/apps/units/flexible_du/split_8/ru_sdr_config.h index 65955da656..b97ed4626b 100644 --- a/apps/units/flexible_du/split_8/ru_sdr_config.h +++ b/apps/units/flexible_du/split_8/ru_sdr_config.h @@ -83,8 +83,8 @@ struct amplitude_control_unit_config { /// Configuration of logging functionalities. struct ru_sdr_unit_logger_config { - std::string radio_level = "info"; - std::string phy_level = "warning"; + srslog::basic_levels radio_level = srslog::basic_levels::info; + srslog::basic_levels phy_level = srslog::basic_levels::warning; }; /// CPU affinities configuration for the cell. diff --git a/apps/units/flexible_du/split_8/ru_sdr_config_cli11_schema.cpp b/apps/units/flexible_du/split_8/ru_sdr_config_cli11_schema.cpp index 9926a0eea4..efda790373 100644 --- a/apps/units/flexible_du/split_8/ru_sdr_config_cli11_schema.cpp +++ b/apps/units/flexible_du/split_8/ru_sdr_config_cli11_schema.cpp @@ -21,6 +21,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 "ru_sdr_config.h" #include "srsran/support/cli11_utils.h" @@ -137,18 +138,8 @@ static void configure_cli11_ru_sdr_args(CLI::App& app, ru_sdr_unit_config& confi static void configure_cli11_log_args(CLI::App& app, ru_sdr_unit_logger_config& log_params) { - auto level_check = [](const std::string& value) -> std::string { - if (value == "info" || value == "debug" || value == "warning" || value == "error") { - return {}; - } - return "Log level value not supported. Accepted values [info,debug,warning,error]"; - }; - - add_option(app, "--radio_level", log_params.radio_level, "Radio log level") - ->capture_default_str() - ->check(level_check); - - add_option(app, "--phy_level", log_params.phy_level, "PHY log level")->capture_default_str()->check(level_check); + app_services::add_log_option(app, log_params.radio_level, "--radio_level", "Radio log level"); + app_services::add_log_option(app, log_params.phy_level, "--phy_level", "PHY log level"); } static void configure_cli11_cell_affinity_args(CLI::App& app, ru_sdr_unit_cpu_affinities_cell_config& config) diff --git a/apps/units/flexible_du/split_8/ru_sdr_factories.cpp b/apps/units/flexible_du/split_8/ru_sdr_factories.cpp index 1ceac33950..b7d0d01bae 100644 --- a/apps/units/flexible_du/split_8/ru_sdr_factories.cpp +++ b/apps/units/flexible_du/split_8/ru_sdr_factories.cpp @@ -48,7 +48,7 @@ std::unique_ptr srsran::create_sdr_radio_unit(const ru_sdr_factory_c low_phy_cfg.ul_task_executor = dependencies.workers->lower_phy_ul_exec[i]; low_phy_cfg.prach_async_executor = dependencies.workers->lower_prach_exec[i]; - low_phy_cfg.logger->set_level(srslog::str_to_basic_level(config.ru_cfg.loggers.phy_level)); + low_phy_cfg.logger->set_level(config.ru_cfg.loggers.phy_level); } return create_generic_ru(ru_config); diff --git a/apps/units/flexible_du/split_8/ru_sdr_logger_registrator.h b/apps/units/flexible_du/split_8/ru_sdr_logger_registrator.h index cb6fb326bf..78ca396aab 100644 --- a/apps/units/flexible_du/split_8/ru_sdr_logger_registrator.h +++ b/apps/units/flexible_du/split_8/ru_sdr_logger_registrator.h @@ -30,10 +30,10 @@ namespace srsran { inline void register_ru_sdr_logs(const ru_sdr_unit_logger_config& log_cfg) { srslog::basic_logger& rf_logger = srslog::fetch_basic_logger("RF", false); - rf_logger.set_level(srslog::str_to_basic_level(log_cfg.radio_level)); + rf_logger.set_level(log_cfg.radio_level); srslog::basic_logger& ru_logger = srslog::fetch_basic_logger("RU", true); - ru_logger.set_level(srslog::str_to_basic_level(log_cfg.radio_level)); + ru_logger.set_level(log_cfg.radio_level); } } // namespace srsran diff --git a/apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.cpp b/apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.cpp index 3986c1fb81..ad428d0614 100644 --- a/apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.cpp +++ b/apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.cpp @@ -33,9 +33,9 @@ static expected parse_int(const std::string& value) try { return std::stoi(value); } catch (const std::invalid_argument& e) { - return {e.what()}; + return make_unexpected(e.what()); } catch (const std::out_of_range& e) { - return {e.what()}; + return make_unexpected(e.what()); } } @@ -47,11 +47,11 @@ static error_type is_valid_cpu_index(unsigned cpu_idx) os_sched_affinity_bitmask one_cpu_mask; if (cpu_idx >= one_cpu_mask.size()) { - return error_message; + 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 error_message; + return make_unexpected(error_message); } return default_success_t(); } @@ -60,13 +60,13 @@ static expected parse_one_cpu(const std::string& value) { expected result = parse_int(value); - if (result.is_error()) { - return fmt::format("Could not parse '{}' string as a CPU index", 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 (validation_result.is_error()) { - return validation_result.error(); + if (not validation_result.has_value()) { + return make_unexpected(validation_result.error()); } return result.value(); @@ -80,8 +80,8 @@ static expected, std::string> parse_cpu_range(const std std::string str; getline(ss, str, '-'); auto parse_result = parse_one_cpu(str); - if (parse_result.is_error()) { - return fmt::format("{}. Could not parse '{}' as a range", parse_result.error(), value); + 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()); @@ -89,11 +89,11 @@ static expected, std::string> parse_cpu_range(const std // A range is defined by two numbers. if (range.size() != 2) { - return fmt::format("Could not parse '{}' as a range", value); + return make_unexpected(fmt::format("Could not parse '{}' as a range", value)); } if (range[1] <= range[0]) { - return fmt::format("Invalid CPU core range detected [{}-{}]", range[0], range[1]); + return make_unexpected(fmt::format("Invalid CPU core range detected [{}-{}]", range[0], range[1])); } return interval(range[0], range[1]); @@ -110,7 +110,7 @@ void srsran::parse_affinity_mask(os_sched_affinity_bitmask& mask, getline(ss, str, ','); if (str.find('-') != std::string::npos) { auto range = parse_cpu_range(str); - if (range.is_error()) { + if (not range.has_value()) { report_error("{} in the '{}' property", range.error(), property_name); } @@ -118,7 +118,7 @@ void srsran::parse_affinity_mask(os_sched_affinity_bitmask& mask, mask.fill(range.value().start(), range.value().stop() + 1); } else { auto cpu_idx = parse_one_cpu(str); - if (cpu_idx.is_error()) { + if (not cpu_idx.has_value()) { report_error("{} in the '{}' property", cpu_idx.error(), property_name); } diff --git a/configs/cu.yml b/configs/cu.yml index ae635529fb..1980a7ca29 100644 --- a/configs/cu.yml +++ b/configs/cu.yml @@ -5,7 +5,7 @@ amf: cu_cp: f1ap: - bind_address: 127.0.10.1 + bind_addr: 127.0.10.1 log: filename: /tmp/cu.log diff --git a/configs/gnb_custom_cell_properties.yml b/configs/gnb_custom_cell_properties.yml index 9765920e55..650308c644 100644 --- a/configs/gnb_custom_cell_properties.yml +++ b/configs/gnb_custom_cell_properties.yml @@ -3,40 +3,40 @@ # base configuration for a single cell. amf: - addr: 127.0.1.100 # The address or hostname of the AMF. - bind_addr: 127.0.0.1 # A local IP that the gNB binds to for traffic from the AMF. + addr: 127.0.1.100 + bind_addr: 127.0.0.1 ru_sdr: - device_driver: uhd # The RF driver name. - device_args: type=b200,num_recv_frames=64,num_send_frames=64 # Optionally pass arguments to the selected RF driver. - clock: # Specify the clock source used by the RF. - sync: # Specify the sync source used by the RF. - srate: 23.04 # RF sample rate might need to be adjusted according to selected bandwidth. + device_driver: uhd + device_args: type=b200,num_recv_frames=64,num_send_frames=64 + clock: + sync: + srate: 23.04 otw_format: sc12 - tx_gain: 50 # Transmit gain of the RF might need to adjusted to the given situation. - rx_gain: 60 # Receive gain of the RF might need to adjusted to the given situation. + tx_gain: 50 + rx_gain: 60 # Properties set in this section are inherited for all cells unless overridden inside the cells section. cell_cfg: - dl_arfcn: 632628 # ARFCN of the downlink carrier (center frequency). - band: 78 # The NR band. - channel_bandwidth_MHz: 20 # Bandwith in MHz. Number of PRBs will be automatically derived. - common_scs: 30 # Subcarrier spacing in kHz used for data. - plmn: "00101" # PLMN broadcasted by the gNB. - tac: 7 # Tracking area code (needs to match the core configuration). - pci: 1 # Physical cell ID. + dl_arfcn: 632628 + band: 78 + channel_bandwidth_MHz: 20 + common_scs: 30 + plmn: "00101" + tac: 7 + pci: 1 # This section is represented as an array of cells. For this example, a single cell is declared that overrides the PCI and channel bandwidth properties from the default values. cells: - - pci: 10 # Override the pci property defined by the default cell in cell_cfg. - channel_bandwidth_MHz: 10 # Override the channel bandwidth property defined by the default cell in cell_cfg. + - pci: 10 + channel_bandwidth_MHz: 10 log: - filename: /tmp/gnb.log # Path of the log file. - all_level: warning # Logging level applied to all layers. + filename: /tmp/gnb.log + all_level: warning pcap: - mac_enable: false # Set to true to enable MAC-layer PCAPs. - mac_filename: /tmp/gnb_mac.pcap # Path where the MAC PCAP is stored. - ngap_enable: false # Set to true to enable NGAP PCAPs. - ngap_filename: /tmp/gnb_ngap.pcap # Path where the NGAP PCAP is stored. + mac_enable: false + mac_filename: /tmp/gnb_mac.pcap + ngap_enable: false + ngap_filename: /tmp/gnb_ngap.pcap \ No newline at end of file diff --git a/configs/gnb_rf_b200_tdd_n78_20mhz.yml b/configs/gnb_rf_b200_tdd_n78_20mhz.yml index 5e92dd3fcf..d02318d5e2 100644 --- a/configs/gnb_rf_b200_tdd_n78_20mhz.yml +++ b/configs/gnb_rf_b200_tdd_n78_20mhz.yml @@ -4,32 +4,32 @@ # is not defined and the default is used. amf: - addr: 127.0.1.100 # The address or hostname of the AMF. - bind_addr: 127.0.0.1 # A local IP that the gNB binds to for traffic from the AMF. + addr: 127.0.1.100 + bind_addr: 127.0.0.1 ru_sdr: - device_driver: uhd # The RF driver name. - device_args: type=b200,num_recv_frames=64,num_send_frames=64 # Optionally pass arguments to the selected RF driver. - srate: 23.04 # RF sample rate might need to be adjusted according to selected bandwidth. + device_driver: uhd + device_args: type=b200,num_recv_frames=64,num_send_frames=64 + srate: 23.04 otw_format: sc12 - tx_gain: 80 # Transmit gain of the RF might need to adjusted to the given situation. - rx_gain: 40 # Receive gain of the RF might need to adjusted to the given situation. + tx_gain: 80 + rx_gain: 40 cell_cfg: - dl_arfcn: 632628 # ARFCN of the downlink carrier (center frequency). - band: 78 # The NR band. - channel_bandwidth_MHz: 20 # Bandwith in MHz. Number of PRBs will be automatically derived. - common_scs: 30 # Subcarrier spacing in kHz used for data. - plmn: "00101" # PLMN broadcasted by the gNB. - tac: 7 # Tracking area code (needs to match the core configuration). - pci: 1 # Physical cell ID. + dl_arfcn: 632628 + band: 78 + channel_bandwidth_MHz: 20 + common_scs: 30 + plmn: "00101" + tac: 7 + pci: 1 log: - filename: /tmp/gnb.log # Path of the log file. - all_level: warning # Logging level applied to all layers. + filename: /tmp/gnb.log + all_level: warning pcap: - mac_enable: false # Set to true to enable MAC-layer PCAPs. - mac_filename: /tmp/gnb_mac.pcap # Path where the MAC PCAP is stored. - ngap_enable: false # Set to true to enable NGAP PCAPs. - ngap_filename: /tmp/gnb_ngap.pcap # Path where the NGAP PCAP is stored. \ No newline at end of file + mac_enable: false + mac_filename: /tmp/gnb_mac.pcap + ngap_enable: false + ngap_filename: /tmp/gnb_ngap.pcap \ No newline at end of file diff --git a/configs/gnb_rf_b210_fdd_srsUE.yml b/configs/gnb_rf_b210_fdd_srsUE.yml index 8424318850..23a480dd4e 100644 --- a/configs/gnb_rf_b210_fdd_srsUE.yml +++ b/configs/gnb_rf_b210_fdd_srsUE.yml @@ -6,40 +6,40 @@ # `cell_cfg` section. These are set to match the capabilities of srsUE. amf: - addr: 10.53.1.2 # The address or hostname of the AMF. - bind_addr: 10.53.1.1 # A local IP that the gNB binds to for> + addr: 10.53.1.2 + bind_addr: 10.53.1.1 ru_sdr: - device_driver: uhd # The RF driver name. - device_args: type=b200 # Optionally pass arguments to the selected RF driver. - sync: external # Set sync to external. This set-up uses a LEO BODNAR GPDSO providing a 10 MHz ref. - srate: 23.04 # RF sample rate might need to be adjusted according to selected bandwidth. - tx_gain: 80 # Transmit gain of the RF might need to adjusted to the given situation. - rx_gain: 40 # Receive gain of the RF might need to adjusted to the given situation. + device_driver: uhd + device_args: type=b200 + sync: external + srate: 23.04 + tx_gain: 80 + rx_gain: 40 cell_cfg: - dl_arfcn: 368500 # ARFCN of the downlink carrier (center frequency). - band: 3 # The NR band. - channel_bandwidth_MHz: 20 # Bandwith in MHz. Number of PRBs will be automatically derived. - common_scs: 15 # Subcarrier spacing in kHz used for data. - plmn: "00101" # PLMN broadcasted by the gNB. - tac: 7 # Tracking area code (needs to match the core configuration). + dl_arfcn: 368500 + band: 3 + channel_bandwidth_MHz: 20 + common_scs: 15 + plmn: "00101" + tac: 7 pdcch: dedicated: - ss2_type: common # Set search space to common to match capabilities - dci_format_0_1_and_1_1: false # Use fallback DCI to match srsUE capabilities + ss2_type: common + dci_format_0_1_and_1_1: false common: - ss0_index: 0 # Set search space zero index to match srsUE capabilities - coreset0_index: 12 # Set search CORESET Zero index to match srsUE capabilities + ss0_index: 0 + coreset0_index: 12 prach: - prach_config_index: 1 # Set PRACH config index to match srsUE expectation + prach_config_index: 1 log: - filename: /tmp/gnb.log # Path of the log file. - all_level: info # Logging level applied to all layers. + filename: /tmp/gnb.log + all_level: info pcap: - mac_enable: enable # Set to true to enable MAC-layer PCAP> - mac_filename: /tmp/gnb_mac.pcap # Path where the MAC PCAP is stored. - ngap_enable: enable # Set to true to enable NGAP PCAPs. - ngap_filename: /tmp/gnb_ngap.pcap # Path where the NGAP PCAP is stored. + mac_enable: enable + mac_filename: /tmp/gnb_mac.pcap + ngap_enable: enable + ngap_filename: /tmp/gnb_ngap.pcap diff --git a/configs/gnb_rf_n310_fdd_n3_20mhz.yml b/configs/gnb_rf_n310_fdd_n3_20mhz.yml index d6d550bbef..4ebc6671cc 100644 --- a/configs/gnb_rf_n310_fdd_n3_20mhz.yml +++ b/configs/gnb_rf_n310_fdd_n3_20mhz.yml @@ -3,33 +3,33 @@ # as the RF frontend using split 8. Note in this example the internal GPDSO of the N310 is used. amf: - addr: 127.0.1.100 # The address or hostname of the AMF. - bind_addr: 127.0.0.1 # A local IP that the gNB binds to for traffic from the AMF. + addr: 127.0.1.100 + bind_addr: 127.0.0.1 ru_sdr: - device_driver: uhd # The RF driver name. - device_args: type=n3xx # Optionally pass arguments to the selected RF driver. - clock: gpsdo # Specify the clock source used by the RF. - sync: gpsdo # Specify the sync source used by the RF. - srate: 30.72 # RF sample rate might need to be adjusted according to selected bandwidth. - tx_gain: 35 # Transmit gain of the RF might need to adjusted to the given situation. - rx_gain: 60 # Receive gain of the RF might need to adjusted to the given situation. + device_driver: uhd + device_args: type=n3xx + clock: gpsdo + sync: gpsdo + srate: 30.72 + tx_gain: 35 + rx_gain: 60 cell_cfg: - dl_arfcn: 368640 # ARFCN of the downlink carrier (center frequency). - band: 3 # The NR band. - channel_bandwidth_MHz: 20 # Bandwith in MHz. Number of PRBs will be automatically derived. - common_scs: 15 # Subcarrier spacing in kHz used for data. - plmn: "00101" # PLMN broadcasted by the gNB. - tac: 7 # Tracking area code (needs to match the core configuration). - pci: 1 # Physical cell ID. + dl_arfcn: 368640 + band: 3 + channel_bandwidth_MHz: 20 + common_scs: 15 + plmn: "00101" + tac: 7 + pci: 1 log: - filename: /tmp/gnb.log # Path of the log file. - all_level: info # Logging level applied to all layers. + filename: /tmp/gnb.log + all_level: info pcap: - mac_enable: false # Set to true to enable MAC-layer PCAPs. - mac_filename: /tmp/gnb_mac.pcap # Path where the MAC PCAP is stored. - ngap_enable: false # Set to true to enable NGAP PCAPs. - ngap_filename: /tmp/gnb_ngap.pcap # Path where the NGAP PCAP is stored. \ No newline at end of file + mac_enable: false + mac_filename: /tmp/gnb_mac.pcap + ngap_enable: false + ngap_filename: /tmp/gnb_ngap.pcap \ No newline at end of file diff --git a/configs/gnb_ru_picocom_scb_tdd_n78_20mhz.yml b/configs/gnb_ru_picocom_scb_tdd_n78_20mhz.yml index d19573c3c2..f49a15fee8 100644 --- a/configs/gnb_ru_picocom_scb_tdd_n78_20mhz.yml +++ b/configs/gnb_ru_picocom_scb_tdd_n78_20mhz.yml @@ -3,59 +3,59 @@ # The parameters used to configure the RU are found in the `ru_ofh` sub-section. This configuration makes used of the OFH Lib from SRS to enable split 7.2. amf: - addr: 127.0.1.100 # The address or hostname of the AMF. - bind_addr: 127.0.0.1 # A local IP that the gNB binds to for traffic from the AMF. + addr: 127.0.1.100 + bind_addr: 127.0.0.1 ru_ofh: - t1a_max_cp_dl: 350 # Maximum T1a on Control-Plane for Downlink in microseconds. - t1a_min_cp_dl: 250 # Minimum T1a on Control-Plane for Downlink in microseconds. - t1a_max_cp_ul: 250 # Maximum T1a on Control-Plane for Uplink in microseconds. - t1a_min_cp_ul: 150 # Minimum T1a on Control-Plane for Uplink in microseconds. - t1a_max_up: 200 # Maximum T1a on User-Plane in microseconds. - t1a_min_up: 80 # Minimum T1a on User-Plane in microseconds. - ta4_max: 300 # Maximum Ta4 on User-Plane in microseconds. - ta4_min: 10 # Minimum Ta4 on User-Plane in microseconds. - is_prach_cp_enabled: false # Configures if Control-Plane messages should be used to receive PRACH messages. - compr_method_ul: bfp # Uplink compression method. - compr_bitwidth_ul: 9 # Uplink IQ samples bitwidth after compression. - compr_method_dl: bfp # Downlink compression method. - compr_bitwidth_dl: 9 # Downlink IQ samples bitwidth after compression. - compr_method_prach: bfp # PRACH compression method. - compr_bitwidth_prach: 9 # PRACH IQ samples bitwidth after compression. - iq_scaling: 1.0 # IQ samples scaling factor applied before compression, should be a positive value smaller than 10. + t1a_max_cp_dl: 350 + t1a_min_cp_dl: 250 + t1a_max_cp_ul: 250 + t1a_min_cp_ul: 150 + t1a_max_up: 200 + t1a_min_up: 80 + ta4_max: 300 + ta4_min: 10 + is_prach_cp_enabled: false + compr_method_ul: bfp + compr_bitwidth_ul: 9 + compr_method_dl: bfp + compr_bitwidth_dl: 9 + compr_method_prach: bfp + compr_bitwidth_prach: 9 + iq_scaling: 1.0 cells: - - network_interface: enp1s0f0 # Ethernet interface name used to communicate with the RU. - ru_mac_addr: ce:fc:6c:09:a6:cd # RU MAC address. - du_mac_addr: 80:61:5f:0d:df:aa # DU MAC address. - vlan_tag_cp: 3 # VLAN tag value for C-Plane. - vlan_tag_up: 3 # VLAN tag value for U-Plane. - prach_port_id: [0] # PRACH eAxC port value. - dl_port_id: [0] # Downlink eAxC port values. - ul_port_id: [0] # Uplink eAxC port values. + - network_interface: enp1s0f0 + ru_mac_addr: ce:fc:6c:09:a6:cd + du_mac_addr: 80:61:5f:0d:df:aa + vlan_tag_cp: 3 + vlan_tag_up: 3 + prach_port_id: [0] + dl_port_id: [0] + ul_port_id: [0] cell_cfg: - dl_arfcn: 625000 # ARFCN of the downlink carrier (center frequency). - band: 78 # The NR band. - channel_bandwidth_MHz: 20 # Bandwith in MHz. Number of PRBs will be automatically derived. - common_scs: 30 # Subcarrier spacing in kHz used for data. - plmn: "00101" # PLMN broadcasted by the gNB. - tac: 7 # Tracking area code (needs to match the core configuration). - pci: 1 # Physical cell ID. + dl_arfcn: 625000 + band: 78 + channel_bandwidth_MHz: 20 + common_scs: 30 + plmn: "00101" + tac: 7 + pci: 1 prach: - prach_config_index: 159 # PRACH configuration index. - prach_root_sequence_index: 1 # PRACH root sequence index. - zero_correlation_zone: 0 # Zero correlation zone. - prach_frequency_start: 0 # Offset in PRBs of lowest PRACH transmission occasion in frequency domain respective to PRB 0. + prach_config_index: 159 + prach_root_sequence_index: 1 + zero_correlation_zone: 0 + prach_frequency_start: 0 tdd_ul_dl_cfg: nof_dl_slots: 7 nof_ul_slots: 2 log: - filename: /tmp/gnb.log # Path of the log file. - all_level: warning # Logging level applied to all layers. + filename: /tmp/gnb.log + all_level: warning pcap: - mac_enable: false # Set to true to enable MAC-layer PCAPs. - mac_filename: /tmp/gnb_mac.pcap # Path where the MAC PCAP is stored. - ngap_enable: false # Set to true to enable NGAP PCAPs. - ngap_filename: /tmp/gnb_ngap.pcap # Path where the NGAP PCAP is stored. + mac_enable: false + mac_filename: /tmp/gnb_mac.pcap + ngap_enable: false + ngap_filename: /tmp/gnb_ngap.pcap diff --git a/configs/gnb_ru_ran550_tdd_n78_100mhz_4x2.yml b/configs/gnb_ru_ran550_tdd_n78_100mhz_4x2.yml index 859fa953fb..9e0a4824d4 100644 --- a/configs/gnb_ru_ran550_tdd_n78_100mhz_4x2.yml +++ b/configs/gnb_ru_ran550_tdd_n78_100mhz_4x2.yml @@ -3,66 +3,66 @@ # The parameters used to configure the RU are found in the `ru_ofh` sub-section. This configuration makes used of the OFH Lib from SRS to enable split 7.2. amf: - addr: 127.0.1.100 # The address or hostname of the AMF. - bind_addr: 127.0.0.1 # A local IP that the gNB binds to for traffic from the AMF. + addr: 127.0.1.100 + bind_addr: 127.0.0.1 ru_ofh: - t1a_max_cp_dl: 535 # Maximum T1a on Control-Plane for Downlink in microseconds. - t1a_min_cp_dl: 286 # Minimum T1a on Control-Plane for Downlink in microseconds. - t1a_max_cp_ul: 535 # Maximum T1a on Control-Plane for Uplink in microseconds. - t1a_min_cp_ul: 286 # Minimum T1a on Control-Plane for Uplink in microseconds. - t1a_max_up: 390 # Maximum T1a on User-Plane in microseconds. - t1a_min_up: 80 # Minimum T1a on User-Plane in microseconds. - ta4_max: 500 # Maximum Ta4 on User-Plane in microseconds. - ta4_min: 25 # Minimum Ta4 on User-Plane in microseconds. - is_prach_cp_enabled: false # Configures if Control-Plane messages should be used to receive PRACH messages. - compr_method_ul: bfp # Uplink compression method. - compr_bitwidth_ul: 9 # Uplink IQ samples bitwidth after compression. - compr_method_dl: bfp # Downlink compression method. - compr_bitwidth_dl: 9 # Downlink IQ samples bitwidth after compression. - compr_method_prach: bfp # PRACH compression method. - compr_bitwidth_prach: 9 # PRACH IQ samples bitwidth after compression. - enable_ul_static_compr_hdr: true # Configures if the compression header is present for uplink User-Plane messages (false) or not present (true). - enable_dl_static_compr_hdr: true # Configures if the compression header is present for downlink User-Plane messages (false) or not present (true). - iq_scaling: 5.5 # IQ samples scaling factor applied before compression, should be a positive value smaller than 10. + t1a_max_cp_dl: 535 + t1a_min_cp_dl: 286 + t1a_max_cp_ul: 535 + t1a_min_cp_ul: 286 + t1a_max_up: 390 + t1a_min_up: 80 + ta4_max: 500 + ta4_min: 25 + is_prach_cp_enabled: false + compr_method_ul: bfp + compr_bitwidth_ul: 9 + compr_method_dl: bfp + compr_bitwidth_dl: 9 + compr_method_prach: bfp + compr_bitwidth_prach: 9 + enable_ul_static_compr_hdr: true + enable_dl_static_compr_hdr: true + iq_scaling: 5.5 cells: - - network_interface: enp1s0f0 # Ethernet interface name used to communicate with the RU. - ru_mac_addr: 70:b3:d5:e1:5b:06 # RU MAC address. - du_mac_addr: 80:61:5f:0d:df:aa # DU MAC address. - vlan_tag_cp: 5 # VLAN tag value for C-Plane. - vlan_tag_up: 5 # VLAN tag value for U-Plane. - prach_port_id: [4, 5] # PRACH eAxC port value. - dl_port_id: [0, 1, 2, 3] # Downlink eAxC port values. - ul_port_id: [0, 1] # Uplink eAxC port values. + - network_interface: enp1s0f0 + ru_mac_addr: 70:b3:d5:e1:5b:06 + du_mac_addr: 80:61:5f:0d:df:aa + vlan_tag_cp: 5 + vlan_tag_up: 5 + prach_port_id: [4, 5] + dl_port_id: [0, 1, 2, 3] + ul_port_id: [0, 1] cell_cfg: - dl_arfcn: 637212 # ARFCN of the downlink carrier (center frequency). - band: 78 # The NR band. - channel_bandwidth_MHz: 100 # Bandwith in MHz. Number of PRBs will be automatically derived. - common_scs: 30 # Subcarrier spacing in kHz used for data. - plmn: "00101" # PLMN broadcasted by the gNB. - tac: 7 # Tracking area code (needs to match the core configuration). - pci: 1 # Physical cell ID. - nof_antennas_dl: 4 # Downlink number of antennas. - nof_antennas_ul: 2 # Uplink number of anntennas. + dl_arfcn: 637212 + band: 78 + channel_bandwidth_MHz: 100 + common_scs: 30 + plmn: "00101" + tac: 7 + pci: 1 + nof_antennas_dl: 4 + nof_antennas_ul: 2 prach: - prach_config_index: 7 # PRACH configuration index. - prach_root_sequence_index: 1 # PRACH root sequence index. - zero_correlation_zone: 0 # Zero correlation zone. - prach_frequency_start: 0 # Offset in PRBs of lowest PRACH transmission occasion in frequency domain respective to PRB 0. + prach_config_index: 7 + prach_root_sequence_index: 1 + zero_correlation_zone: 0 + prach_frequency_start: 0 tdd_ul_dl_cfg: - dl_ul_tx_period: 10 # Optional INT (10). Sets the TDD pattern periodicity in slots. The combination of this value and the chosen numerology must lead to a TDD periodicity of 0.5, > - nof_dl_slots: 7 # Optional INT (6). Number of consecutive full Downlink slots. Supported: [0-80]. - nof_dl_symbols: 6 # Optional INT (0). Number of Downlink symbols at the beginning of the slot following full Downlink slots. Supported: [0-13]. - nof_ul_slots: 2 # Optional INT (3). Number of consecutive full Uplink slots. Supported: [0 - 80]. + dl_ul_tx_period: 10 + nof_dl_slots: 7 + nof_dl_symbols: 6 + nof_ul_slots: 2 nof_ul_symbols: 4 log: - filename: /tmp/gnb.log # Path of the log file. - all_level: warning # Logging level applied to all layers. + filename: /tmp/gnb.log + all_level: warning pcap: - mac_enable: false # Set to true to enable MAC-layer PCAPs. - mac_filename: /tmp/gnb_mac.pcap # Path where the MAC PCAP is stored. - ngap_enable: false # Set to true to enable NGAP PCAPs. - ngap_filename: /tmp/gnb_ngap.pcap # Path where the NGAP PCAP is stored. + mac_enable: false + mac_filename: /tmp/gnb_mac.pcap + ngap_enable: false + ngap_filename: /tmp/gnb_ngap.pcap diff --git a/configs/gnb_ru_ran550_tdd_n78_20mhz.yml b/configs/gnb_ru_ran550_tdd_n78_20mhz.yml index 1ab73764a5..d69886ba59 100644 --- a/configs/gnb_ru_ran550_tdd_n78_20mhz.yml +++ b/configs/gnb_ru_ran550_tdd_n78_20mhz.yml @@ -3,60 +3,60 @@ # The parameters used to configure the RU are found in the `ru_ofh` sub-section. This configuration makes used of the OFH Lib from SRS to enable split 7.2. amf: - addr: 127.0.1.100 # The address or hostname of the AMF. - bind_addr: 127.0.0.1 # A local IP that the gNB binds to for traffic from the AMF. + addr: 127.0.1.100 + bind_addr: 127.0.0.1 ru_ofh: - ru_bandwidth_MHz: 100 # RU instantaneous bandwidth. - t1a_max_cp_dl: 535 # Maximum T1a on Control-Plane for Downlink in microseconds. - t1a_min_cp_dl: 286 # Minimum T1a on Control-Plane for Downlink in microseconds. - t1a_max_cp_ul: 535 # Maximum T1a on Control-Plane for Uplink in microseconds. - t1a_min_cp_ul: 286 # Minimum T1a on Control-Plane for Uplink in microseconds. - t1a_max_up: 390 # Maximum T1a on User-Plane in microseconds. - t1a_min_up: 80 # Minimum T1a on User-Plane in microseconds. - ta4_max: 500 # Maximum Ta4 on User-Plane in microseconds. - ta4_min: 25 # Minimum Ta4 on User-Plane in microseconds. - is_prach_cp_enabled: false # Configures if Control-Plane messages should be used to receive PRACH messages. - is_dl_broadcast_enabled: true # Set to true for a workaround over a firmware bug in the RAN550 when operating in SISO mode. - compr_method_ul: bfp # Uplink compression method. - compr_bitwidth_ul: 9 # Uplink IQ samples bitwidth after compression. - compr_method_dl: bfp # Downlink compression method. - compr_method_prach: bfp # PRACH compression method. - compr_bitwidth_prach: 9 # PRACH IQ samples bitwidth after compression. - compr_bitwidth_dl: 9 # Downlink IQ samples bitwidth after compression. - enable_ul_static_compr_hdr: true # Configures if the compression header is present for uplink User-Plane messages (false) or not present (true). - enable_dl_static_compr_hdr: true # Configures if the compression header is present for downlink User-Plane messages (false) or not present (true). - iq_scaling: 5.5 # IQ samples scaling factor applied before compression, should be a positive value smaller than 10. + ru_bandwidth_MHz: 100 + t1a_max_cp_dl: 535 + t1a_min_cp_dl: 286 + t1a_max_cp_ul: 535 + t1a_min_cp_ul: 286 + t1a_max_up: 390 + t1a_min_up: 80 + ta4_max: 500 + ta4_min: 25 + is_prach_cp_enabled: false + is_dl_broadcast_enabled: true + compr_method_ul: bfp + compr_bitwidth_ul: 9 + compr_method_dl: bfp + compr_method_prach: bfp + compr_bitwidth_prach: 9 + compr_bitwidth_dl: 9 + enable_ul_static_compr_hdr: true + enable_dl_static_compr_hdr: true + iq_scaling: 5.5 cells: - - network_interface: enp1s0f0 # Ethernet interface name used to communicate with the RU. - ru_mac_addr: 70:b3:d5:e1:5b:06 # RU MAC address. - du_mac_addr: 80:61:5f:0d:df:aa # DU MAC address. - vlan_tag_cp: 5 # VLAN tag value for C-Plane. - vlan_tag_up: 5 # VLAN tag value for U-Plane. - prach_port_id: [4] # PRACH eAxC port value. - dl_port_id: [0, 1] # Downlink eAxC port values. - ul_port_id: [0] # Uplink eAxC port values. + - network_interface: enp1s0f0 + ru_mac_addr: 70:b3:d5:e1:5b:06 + du_mac_addr: 80:61:5f:0d:df:aa + vlan_tag_cp: 5 + vlan_tag_up: 5 + prach_port_id: [4] + dl_port_id: [0, 1] + ul_port_id: [0] cell_cfg: - dl_arfcn: 634548 # ARFCN of the downlink carrier (center frequency). - band: 78 # The NR band. - channel_bandwidth_MHz: 20 # Bandwith in MHz. Number of PRBs will be automatically derived. - common_scs: 30 # Subcarrier spacing in kHz used for data. - plmn: "00101" # PLMN broadcasted by the gNB. - tac: 7 # Tracking area code (needs to match the core configuration). - pci: 1 # Physical cell ID. + dl_arfcn: 634548 + band: 78 + channel_bandwidth_MHz: 20 + common_scs: 30 + plmn: "00101" + tac: 7 + pci: 1 prach: - prach_config_index: 7 # PRACH configuration index. - prach_root_sequence_index: 1 # PRACH root sequence index. - zero_correlation_zone: 0 # Zero correlation zone. - prach_frequency_start: 0 # Offset in PRBs of lowest PRACH transmission occasion in frequency domain respective to PRB 0. + prach_config_index: 7 + prach_root_sequence_index: 1 + zero_correlation_zone: 0 + prach_frequency_start: 0 log: - filename: /tmp/gnb.log # Path of the log file. - all_level: warning # Logging level applied to all layers. + filename: /tmp/gnb.log + all_level: warning pcap: - mac_enable: false # Set to true to enable MAC-layer PCAPs. - mac_filename: /tmp/gnb_mac.pcap # Path where the MAC PCAP is stored. - ngap_enable: false # Set to true to enable NGAP PCAPs. - ngap_filename: /tmp/gnb_ngap.pcap # Path where the NGAP PCAP is stored. + mac_enable: false + mac_filename: /tmp/gnb_mac.pcap + ngap_enable: false + ngap_filename: /tmp/gnb_ngap.pcap diff --git a/configs/gnb_ru_rpqn4800e_tdd_n78_20mhz_2x2.yml b/configs/gnb_ru_rpqn4800e_tdd_n78_20mhz_2x2.yml index 39cd2a8b63..f1c1d660cd 100644 --- a/configs/gnb_ru_rpqn4800e_tdd_n78_20mhz_2x2.yml +++ b/configs/gnb_ru_rpqn4800e_tdd_n78_20mhz_2x2.yml @@ -3,63 +3,63 @@ # The parameters used to configure the RU are found in the `ru_ofh` sub-section. This configuration makes used of the OFH Lib from SRS to enable split 7.2. amf: - addr: 127.0.1.100 # The address or hostname of the AMF. - bind_addr: 127.0.0.1 # A local IP that the gNB binds to for traffic from the AMF. + addr: 127.0.1.100 + bind_addr: 127.0.0.1 ru_ofh: - t1a_max_cp_dl: 420 # Maximum T1a on Control-Plane for Downlink in microseconds. - t1a_min_cp_dl: 250 # Minimum T1a on Control-Plane for Downlink in microseconds. - t1a_max_cp_ul: 420 # Maximum T1a on Control-Plane for Uplink in microseconds. - t1a_min_cp_ul: 250 # Minimum T1a on Control-Plane for Uplink in microseconds. - t1a_max_up: 196 # Maximum T1a on User-Plane in microseconds. - t1a_min_up: 80 # Minimum T1a on User-Plane in microseconds. - ta4_max: 500 # Maximum Ta4 on User-Plane in microseconds. - ta4_min: 25 # Minimum Ta4 on User-Plane in microseconds. - is_prach_cp_enabled: true # Configures if Control-Plane messages should be used to receive PRACH messages. - ignore_ecpri_payload_size: true # Configures if eCPRI payload size field should be ignored by the eCPRI packet decoder. - compr_method_ul: bfp # Uplink compression method. - compr_bitwidth_ul: 9 # Uplink IQ samples bitwidth after compression. - compr_method_dl: bfp # Downlink compression method. - compr_bitwidth_dl: 9 # Downlink IQ samples bitwidth after compression. - compr_method_prach: bfp # PRACH compression method. - compr_bitwidth_prach: 9 # PRACH IQ samples bitwidth after compression. - enable_ul_static_compr_hdr: false # Configures if the compression header is present for uplink User-Plane messages (false) or not present (true). - enable_dl_static_compr_hdr: false # Configures if the compression header is present for downlink User-Plane messages (false) or not present (true). - iq_scaling: 1.0 # IQ samples scaling factor applied before compression, should be a positive value smaller than 10. + t1a_max_cp_dl: 420 + t1a_min_cp_dl: 250 + t1a_max_cp_ul: 420 + t1a_min_cp_ul: 250 + t1a_max_up: 196 + t1a_min_up: 80 + ta4_max: 500 + ta4_min: 25 + is_prach_cp_enabled: true + ignore_ecpri_payload_size: true + compr_method_ul: bfp + compr_bitwidth_ul: 9 + compr_method_dl: bfp + compr_bitwidth_dl: 9 + compr_method_prach: bfp + compr_bitwidth_prach: 9 + enable_ul_static_compr_hdr: false + enable_dl_static_compr_hdr: false + iq_scaling: 1.0 cells: - - network_interface: 0000:01:00.1 # Ethernet interface name used to communicate with the RU. - ru_mac_addr: 6c:ad:ad:00:xx:xx # RU MAC address. - du_mac_addr: 50:7c:6f:55:xx:xx # DU MAC address. - vlan_tag_cp: 2 # VLAN tag value for C-Plane. - vlan_tag_up: 2 # VLAN tag value for U-Plane. - prach_port_id: [4, 5, 6, 7] # PRACH eAxC port values. - dl_port_id: [0, 1] # Downlink eAxC port values. - ul_port_id: [0, 1, 2, 3] # Uplink eAxC port values. + - network_interface: 0000:01:00.1 + ru_mac_addr: 6c:ad:ad:00:xx:xx + du_mac_addr: 50:7c:6f:55:xx:xx + vlan_tag_cp: 2 + vlan_tag_up: 2 + prach_port_id: [4, 5, 6, 7] + dl_port_id: [0, 1] + ul_port_id: [0, 1, 2, 3] cell_cfg: - dl_arfcn: 640000 # ARFCN of the downlink carrier (center frequency). - band: 78 # The NR band. - channel_bandwidth_MHz: 20 # Bandwith in MHz. Number of PRBs will be automatically derived. - common_scs: 30 # Subcarrier spacing in kHz used for data. - plmn: "00101" # PLMN broadcasted by the gNB. - tac: 7 # Tracking area code (needs to match the core configuration). - pci: 1 # Physical cell ID. - nof_antennas_dl: 2 # Number of transmission antennas. - nof_antennas_ul: 2 # Number of reception antennas. + dl_arfcn: 640000 + band: 78 + channel_bandwidth_MHz: 20 + common_scs: 30 + plmn: "00101" + tac: 7 + pci: 1 + nof_antennas_dl: 2 + nof_antennas_ul: 2 prach: - prach_config_index: 159 # PRACH configuration index. - prach_root_sequence_index: 1 # PRACH root sequence index. - zero_correlation_zone: 0 # Zero correlation zone. - prach_frequency_start: 12 # Offset in PRBs of lowest PRACH transmission occasion in frequency domain respective to PRB 0. + prach_config_index: 159 + prach_root_sequence_index: 1 + zero_correlation_zone: 0 + prach_frequency_start: 12 pdsch: mcs_table: qam256 log: - filename: /tmp/gnb.log # Path of the log file. - all_level: warning # Logging level applied to all layers. + filename: /tmp/gnb.log + all_level: warning pcap: - mac_enable: false # Set to true to enable MAC-layer PCAPs. - mac_filename: /tmp/gnb_mac.pcap # Path where the MAC PCAP is stored. - ngap_enable: false # Set to true to enable NGAP PCAPs. - ngap_filename: /tmp/gnb_ngap.pcap # Path where the NGAP PCAP is stored. \ No newline at end of file + mac_enable: false + mac_filename: /tmp/gnb_mac.pcap + ngap_enable: false + ngap_filename: /tmp/gnb_ngap.pcap \ No newline at end of file diff --git a/configs/mobility.yml b/configs/mobility.yml index 13a8363871..830d9d15cb 100644 --- a/configs/mobility.yml +++ b/configs/mobility.yml @@ -4,12 +4,12 @@ cu_cp: mobility: # List of all cells known to the CU-CP, their configs (if not provided over F1) and their respective neighbor cells. cells: - - nr_cell_id: 0x66C000 # For the default gnb_id=411 (gnb_id_bit_length=22), NR cell IDs start with 0x66C000 and increment for each cell of the DU. - periodic_report_cfg_id: 1 # This will configure a periodical report config for the serving cell. - ncells: # List of nr_cell_ids that are a neighbor of this serving cell. + - nr_cell_id: 0x66C000 # For the default gnb_id=411 (gnb_id_bit_length=22), NR cell IDs start with 0x66C000 and increment for each cell of the DU. + periodic_report_cfg_id: 1 # This will configure a periodical report config for the serving cell. + ncells: # List of nr_cell_ids that are a neighbor of this serving cell. - nr_cell_id: 0x20 report_configs: [ 2 ] - - nr_cell_id: 0x20 # This cell is not managed by this CU-CP + - nr_cell_id: 0x20 # This cell is not managed by this CU-CP ncells: - nr_cell_id: 0x66C000 report_configs: [ 2 ] diff --git a/configs/qam256.yml b/configs/qam256.yml index aee1bf2474..3fcaf85f79 100644 --- a/configs/qam256.yml +++ b/configs/qam256.yml @@ -2,6 +2,6 @@ cell_cfg: pdsch: - mcs_table: qam256 # Use QAM256 MCS table in downlink + mcs_table: qam256 ssb: - ssb_period: 20 # Increase SSB periodicity to 20ms \ No newline at end of file + ssb_period: 20 \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index 876db00e4f..1a21e366d1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -36,7 +36,7 @@ ARG NUM_CORES="" ################## # Stage 1: Build # ################## -FROM ubuntu:$OS_VERSION as builder +FROM ubuntu:$OS_VERSION AS builder # Adding the complete repo to the context, in /src folder ADD . /src diff --git a/docker/metrics_server/Dockerfile b/docker/metrics_server/Dockerfile index 2ef469e230..656ecb5a5a 100644 --- a/docker/metrics_server/Dockerfile +++ b/docker/metrics_server/Dockerfile @@ -21,7 +21,7 @@ ################### # Package Builder # ################### -FROM python:3.12-alpine as builder +FROM python:3.12-alpine AS builder # Add the folder ADD . /src diff --git a/docker/metrics_server/pyproject.toml b/docker/metrics_server/pyproject.toml index 04c60b6c64..f651d448b2 100644 --- a/docker/metrics_server/pyproject.toml +++ b/docker/metrics_server/pyproject.toml @@ -43,7 +43,7 @@ description = "srsRAN Metrics Server" name = "srs_metrics_server" readme = "README.md" requires-python = ">=3.8" -version = "1.7.2" +version = "1.7.3" [project.scripts] metrics-server = "metrics_server.__main__:main" diff --git a/docker/open5gs/Dockerfile b/docker/open5gs/Dockerfile index eeef086e4d..08296d59a8 100644 --- a/docker/open5gs/Dockerfile +++ b/docker/open5gs/Dockerfile @@ -7,7 +7,7 @@ # ARG OS_VERSION=22.04 -FROM ubuntu:$OS_VERSION as base +FROM ubuntu:$OS_VERSION AS base ENV PYTHONBUFFERED=1 ENV DEBIAN_FRONTEND=noninteractive @@ -87,7 +87,7 @@ RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash - \ # mongodb python prerequisites RUN python3 -m pip install pymongo click pyroute2 ipaddress python-iptables -FROM base as open5gs +FROM base AS open5gs WORKDIR /open5gs COPY open5gs-5gc.yml open5gs-5gc.yml.in diff --git a/docker/open5gs/open5gs-5gc.yml b/docker/open5gs/open5gs-5gc.yml index 6274ffc70d..0ae041e5d5 100644 --- a/docker/open5gs/open5gs-5gc.yml +++ b/docker/open5gs/open5gs-5gc.yml @@ -82,7 +82,8 @@ mme: integrity_order: [EIA2, EIA1, EIA0] ciphering_order: [EEA0, EEA1, EEA2] network_name: - full: Open5GS + full: SRS RAN + short: SRS RAN time: t3412: value: 540 @@ -179,7 +180,8 @@ amf: integrity_order: [NIA2, NIA1, NIA0] ciphering_order: [NEA0, NEA1, NEA2] network_name: - full: Open5GS + full: SRS RAN + short: SRS RAN amf_name: open5gs-amf0 time: t3512: diff --git a/docker/open5gs/open5gs.env b/docker/open5gs/open5gs.env index 17b94ad160..a1d497490c 100644 --- a/docker/open5gs/open5gs.env +++ b/docker/open5gs/open5gs.env @@ -4,3 +4,6 @@ UE_IP_BASE=10.45.0 UPF_ADVERTISE_IP=10.53.1.2 DEBUG=false SUBSCRIBER_DB=001010123456780,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,8000,9,10.45.1.2 + +# Timezone - this setting will also be conveyed by Open5GS towards UE via "configuration update command"; comment for UTC +TZ=Europe/Madrid diff --git a/external/TartanLlama/expected.hpp b/external/TartanLlama/expected.hpp new file mode 100644 index 0000000000..1f92b6b3cd --- /dev/null +++ b/external/TartanLlama/expected.hpp @@ -0,0 +1,2444 @@ +/// +// expected - An implementation of std::expected with extensions +// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) +// +// Documentation available at http://tl.tartanllama.xyz/ +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// . +/// + +#ifndef TL_EXPECTED_HPP +#define TL_EXPECTED_HPP + +#define TL_EXPECTED_VERSION_MAJOR 1 +#define TL_EXPECTED_VERSION_MINOR 1 +#define TL_EXPECTED_VERSION_PATCH 0 + +#include +#include +#include +#include + +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#define TL_EXPECTED_EXCEPTIONS_ENABLED +#endif + +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define TL_EXPECTED_MSVC2015 +#define TL_EXPECTED_MSVC2015_CONSTEXPR +#else +#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC55 +#endif + +#if !defined(TL_ASSERT) +//can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug +#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49) +#include +#define TL_ASSERT(x) assert(x) +#else +#define TL_ASSERT(x) +#endif +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions + +#define TL_EXPECTED_NO_CONSTRR +// GCC < 5 doesn't support some standard C++11 type traits +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign + +// This one will be different for GCC 5.7 if it's ever supported +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible + +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks +// std::vector for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) +#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +namespace tl { +namespace detail { +template +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; +#ifdef _GLIBCXX_VECTOR +template +struct is_trivially_copy_constructible> : std::false_type {}; +#endif +} // namespace detail +} // namespace tl +#endif + +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#else +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#endif + +#if __cplusplus > 201103L +#define TL_EXPECTED_CXX14 +#endif + +#ifdef TL_EXPECTED_GCC49 +#define TL_EXPECTED_GCC49_CONSTEXPR +#else +#define TL_EXPECTED_GCC49_CONSTEXPR constexpr +#endif + +#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ + defined(TL_EXPECTED_GCC49)) +#define TL_EXPECTED_11_CONSTEXPR +#else +#define TL_EXPECTED_11_CONSTEXPR constexpr +#endif + +namespace tl { +template class expected; + +#ifndef TL_MONOSTATE_INPLACE_MUTEX +#define TL_MONOSTATE_INPLACE_MUTEX +class monostate {}; + +struct in_place_t { + explicit in_place_t() = default; +}; +static constexpr in_place_t in_place{}; +#endif + +template class unexpected { +public: + static_assert(!std::is_same::value, "E must not be void"); + + unexpected() = delete; + constexpr explicit unexpected(const E &e) : m_val(e) {} + + constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} + + template ::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) + : m_val(std::forward(args)...) {} + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(args)...) {} + + constexpr const E &value() const & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } + constexpr const E &&value() const && { return std::move(m_val); } + +private: + E m_val; +}; + +#ifdef __cpp_deduction_guides +template unexpected(E) -> unexpected; +#endif + +template +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() == rhs.value(); +} +template +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() != rhs.value(); +} +template +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() < rhs.value(); +} +template +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() <= rhs.value(); +} +template +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() > rhs.value(); +} +template +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() >= rhs.value(); +} + +template +unexpected::type> make_unexpected(E &&e) { + return unexpected::type>(std::forward(e)); +} + +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; + +namespace detail { +template +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward(e); +#else + (void)e; +#ifdef _MSC_VER + __assume(0); +#else + __builtin_unreachable(); +#endif +#endif +} + +#ifndef TL_TRAITS_MUTEX +#define TL_TRAITS_MUTEX +// C++14-style aliases for brevity +template using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; + +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; + +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#endif + +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +template +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; + +template struct is_const_or_const_ref : std::false_type {}; +template struct is_const_or_const_ref : std::true_type {}; +template struct is_const_or_const_ref : std::true_type {}; +#endif + +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template < + typename Fn, typename... Args, +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND + typename = enable_if_t::value && + is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} + +template >::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template struct invoke_result_impl; + +template +struct invoke_result_impl< + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template struct is_swappable : std::true_type {}; + +template struct is_nothrow_swappable : std::true_type {}; +#else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { +// if swap ADL finds this then it would call std::swap otherwise (same +// signature) +struct tag {}; + +template tag swap(T &, T &); +template tag swap(T (&a)[N], T (&b)[N]); + +// helper functions to test if an unqualified swap is possible, and if it +// becomes std::swap +template std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); + +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; + +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; + +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif +#endif + +// Trait for checking if a type is a tl::expected +template struct is_expected_impl : std::false_type {}; +template +struct is_expected_impl> : std::true_type {}; +template using is_expected = is_expected_impl>; + +template +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value && + !std::is_same, detail::decay_t>::value>; + +template +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; + +template +using is_void_or = conditional_t::value, std::true_type, U>; + +template +using is_copy_constructible_or_void = + is_void_or>; + +template +using is_move_constructible_or_void = + is_void_or>; + +template +using is_copy_assignable_or_void = is_void_or>; + +template +using is_move_assignable_or_void = is_void_or>; + +} // namespace detail + +namespace detail { +struct no_init_t {}; +static constexpr no_init_t no_init{}; + +// Implements the storage of the values, and ensures that the destructor is +// trivial if it can be. +// +// This specialization is for where neither `T` or `E` is trivially +// destructible, so the destructors must be called on destruction of the +// `expected` +template ::value, + bool = std::is_trivially_destructible::value> +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// This specialization is for when both `T` and `E` are trivially-destructible, +// so the destructor of the `expected` can be trivial. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// T is trivial, E is not. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) + : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is trivially-destructible +template struct expected_storage_base { + #if __GNUC__ <= 5 + //no constexpr for GCC 4/5 bug + #else + TL_EXPECTED_MSVC2015_CONSTEXPR + #endif + expected_storage_base() : m_has_val(true) {} + + constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is not trivially-destructible +template struct expected_storage_base { + constexpr expected_storage_base() : m_dummy(), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + unexpected m_unexpect; + char m_dummy; + }; + bool m_has_val; +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct(Args &&...args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } + + template void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + + // These assign overloads ensure that the most efficient assignment + // implementation is used while maintaining the strong exception guarantee. + // The problematic case is where rhs has a value, but *this does not. + // + // This overload handles the case where we can just copy-construct `T` + // directly into place without throwing. + template ::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + // This overload handles the case where we can attempt to create a copy of + // `T`, then no-throw move it into place if the copy was successful. + template ::value && + std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + T tmp = rhs.get(); + geterr().~unexpected(); + construct(std::move(tmp)); + } else { + assign_common(rhs); + } + } + + // This overload is the worst-case, where we have to move-construct the + // unexpected value into temporary storage, then try to copy the T into place. + // If the construction succeeds, then everything is fine, but if it throws, + // then we move the old unexpected value back into place before rethrowing the + // exception. + template ::value && + !std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(rhs.get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(rhs.get()); +#endif + } else { + assign_common(rhs); + } + } + + // These overloads do the same as above, but for rvalues + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(std::move(rhs).get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(std::move(rhs).get()); +#endif + } else { + assign_common(std::move(rhs)); + } + } + +#else + + // If exceptions are disabled then we can just copy-construct + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + +#endif + + // The common part of move/copy assigning + template void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(rhs).geterr(); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } + constexpr const T &get() const & { return this->m_val; } + TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_val); } +#endif + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct() noexcept { this->m_has_val = true; } + + // This function doesn't use its argument, but needs it so that code in + // levels above this can work independently of whether T is void + template void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + + template void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + TL_EXPECTED_11_CONSTEXPR void destroy_val() { + // no-op + } +}; + +// This class manages conditionally having a trivial copy constructor +// This specialization is for when T and E are trivially copy constructible +template :: + value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; +}; + +// This specialization is for when T or E are not trivially copy constructible +template +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; + + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base(no_init) { + if (rhs.has_value()) { + this->construct_with(rhs); + } else { + this->construct_error(rhs.geterr()); + } + } + + expected_copy_base(expected_copy_base &&rhs) = default; + expected_copy_base &operator=(const expected_copy_base &rhs) = default; + expected_copy_base &operator=(expected_copy_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef TL_EXPECTED_GCC49 +template >::value + &&std::is_trivially_move_constructible::value> +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; +#else +template struct expected_move_base; +#endif +template +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; + + expected_move_base() = default; + expected_move_base(const expected_move_base &rhs) = default; + + expected_move_base(expected_move_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value) + : expected_copy_base(no_init) { + if (rhs.has_value()) { + this->construct_with(std::move(rhs)); + } else { + this->construct_error(std::move(rhs.geterr())); + } + } + expected_move_base &operator=(const expected_move_base &rhs) = default; + expected_move_base &operator=(expected_move_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial copy assignment operator +template >::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; +}; + +template +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; + + expected_copy_assign_base() = default; + expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; + + expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; + expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { + this->assign(rhs); + return *this; + } + expected_copy_assign_base & + operator=(expected_copy_assign_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef TL_EXPECTED_GCC49 +template , + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>:: + value &&std::is_trivially_destructible::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; +}; +#else +template struct expected_move_assign_base; +#endif + +template +struct expected_move_assign_base + : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; + + expected_move_assign_base() = default; + expected_move_assign_base(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base(expected_move_assign_base &&rhs) = default; + + expected_move_assign_base & + operator=(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base & + operator=(expected_move_assign_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value + &&std::is_nothrow_move_assignable::value) { + this->assign(std::move(rhs)); + return *this; + } +}; + +// expected_delete_ctor_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible +template ::value && + std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value)> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +// expected_delete_assign_base will conditionally delete copy and move +// constructors depending on whether T and E are copy/move constructible + +// assignable +template ::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value && + is_move_assignable_or_void::value && + std::is_move_assignable::value)> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +// This is needed to be able to construct the expected_default_ctor_base which +// follows, while still conditionally deleting the default constructor. +struct default_constructor_tag { + explicit constexpr default_constructor_tag() = default; +}; + +// expected_default_ctor_base will ensure that expected has a deleted default +// consturctor if T is not default constructible. +// This specialization is for when T is default constructible +template ::value || std::is_void::value> +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = default; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; + +// This specialization is for when T is not default constructible +template struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = delete; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; +} // namespace detail + +template class bad_expected_access : public std::exception { +public: + explicit bad_expected_access(E e) : m_val(std::move(e)) {} + + virtual const char *what() const noexcept override { + return "Bad expected access"; + } + + const E &error() const & { return m_val; } + E &error() & { return m_val; } + const E &&error() const && { return std::move(m_val); } + E &&error() && { return std::move(m_val); } + +private: + E m_val; +}; + +/// An `expected` object is an object that contains the storage for +/// another object and manages the lifetime of this contained object `T`. +/// Alternatively it could contain the storage for another unexpected object +/// `E`. The contained object may not be initialized after the expected object +/// has been initialized, and may not be destroyed before the expected object +/// has been destroyed. The initialization state of the contained object is +/// tracked by the expected object. +template +class expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { + static_assert(!std::is_reference::value, "T must not be a reference"); + static_assert(!std::is_same::type>::value, + "T must not be in_place_t"); + static_assert(!std::is_same::type>::value, + "T must not be unexpect_t"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); + static_assert(!std::is_reference::value, "E must not be a reference"); + + T *valptr() { return std::addressof(this->m_val); } + const T *valptr() const { return std::addressof(this->m_val); } + unexpected *errptr() { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } + + template ::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected &err() const { return this->m_unexpect; } + + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; + +public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward(f)); + } + template constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif + +#else + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) && -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward(f)); + } + + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward(f)); + } + + template expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward(f)); + } +#endif + constexpr expected() = default; + constexpr expected(const expected &rhs) = default; + constexpr expected(expected &&rhs) = default; + expected &operator=(const expected &rhs) = default; + expected &operator=(expected &&rhs) = default; + + template ::value> * = + nullptr> + constexpr expected(in_place_t, Args &&...args) + : impl_base(in_place, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) + : impl_base(in_place, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr> + constexpr explicit expected(unexpect_t, Args &&...args) + : impl_base(unexpect, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected(unexpect_t, std::initializer_list il, + Args &&...args) + : impl_base(unexpect, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template < + class U, class G, + detail::enable_if_t::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U, class G, + detail::enable_if_t<(std::is_convertible::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + err().~unexpected(); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } + + return *this; + } + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; +#endif + } + + return *this; + } + + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; + } else { + this->destroy_val(); + ::new (errptr()) unexpected(rhs); + this->m_has_val = false; + } + + return *this; + } + + template ::value && + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + ::new (errptr()) unexpected(std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + } else { + err().~unexpected(); + this->m_has_val = true; + } + ::new (valptr()) T(std::forward(args)...); + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + ::new (valptr()) T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; +#endif + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + err().~unexpected(); + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; +#endif + } + } + +private: + using t_is_void = std::true_type; + using t_is_not_void = std::false_type; + using t_is_nothrow_move_constructible = std::true_type; + using move_constructing_t_can_throw = std::false_type; + using e_is_nothrow_move_constructible = std::true_type; + using move_constructing_e_can_throw = std::false_type; + + void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept { + // swapping void is a no-op + } + + void swap_where_both_have_value(expected &rhs, t_is_not_void) { + using std::swap; + swap(val(), rhs.val()); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( + std::is_nothrow_move_constructible::value) { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { + swap_where_only_one_has_value_and_t_is_not_void( + rhs, typename std::is_nothrow_move_constructible::type{}, + typename std::is_nothrow_move_constructible::type{}); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + e_is_nothrow_move_constructible) noexcept { + auto temp = std::move(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + move_constructing_e_can_throw) { + auto temp = std::move(val()); + val().~T(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + val() = std::move(temp); + throw; + } +#else + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, move_constructing_t_can_throw, + e_is_nothrow_move_constructible) { + auto temp = std::move(rhs.err()); + rhs.err().~unexpected_type(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + rhs.err() = std::move(temp); + throw; + } +#else + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + +public: + template + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, typename std::is_void::type{}); + } else if (!has_value() && rhs.has_value()) { + rhs.swap(*this); + } else if (has_value()) { + swap_where_only_one_has_value(rhs, typename std::is_void::type{}); + } else { + using std::swap; + swap(err(), rhs.err()); + } + } + + constexpr const T *operator->() const { + TL_ASSERT(has_value()); + return valptr(); + } + TL_EXPECTED_11_CONSTEXPR T *operator->() { + TL_ASSERT(has_value()); + return valptr(); + } + + template ::value> * = nullptr> + constexpr const U &operator*() const & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + TL_ASSERT(has_value()); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&operator*() && { + TL_ASSERT(has_value()); + return std::move(val()); + } + + constexpr bool has_value() const noexcept { return this->m_has_val; } + constexpr explicit operator bool() const noexcept { return this->m_has_val; } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + + constexpr const E &error() const & { + TL_ASSERT(!has_value()); + return err().value(); + } + TL_EXPECTED_11_CONSTEXPR E &error() & { + TL_ASSERT(!has_value()); + return err().value(); + } + constexpr const E &&error() const && { + TL_ASSERT(!has_value()); + return std::move(err().value()); + } + TL_EXPECTED_11_CONSTEXPR E &&error() && { + TL_ASSERT(!has_value()); + return std::move(err().value()); + } + + template constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); + } +}; + +namespace detail { +template using exp_t = typename detail::decay_t::value_type; +template using err_t = typename detail::decay_t::error_type; +template using ret_t = expected>; + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#else +template struct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template ())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#else +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#endif +} // namespace detail + +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); +} +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() == rhs.error() : false); +} + +template +constexpr bool operator==(const expected &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator==(const U &v, const expected &x) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator!=(const expected &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template +constexpr bool operator!=(const U &v, const expected &x) { + return x.has_value() ? *x != v : true; +} + +template +constexpr bool operator==(const expected &x, const unexpected &e) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator==(const unexpected &e, const expected &x) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator!=(const expected &x, const unexpected &e) { + return x.has_value() ? true : x.error() != e.value(); +} +template +constexpr bool operator!=(const unexpected &e, const expected &x) { + return x.has_value() ? true : x.error() != e.value(); +} + +template ::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace tl + +#endif \ No newline at end of file diff --git a/external/fmt/CMakeLists.txt b/external/fmt/CMakeLists.txt index 6c6dc17789..d7a6664361 100644 --- a/external/fmt/CMakeLists.txt +++ b/external/fmt/CMakeLists.txt @@ -25,4 +25,4 @@ set(SOURCES add_library(fmt STATIC ${SOURCES}) target_link_libraries(fmt ${CMAKE_THREAD_LIBS_INIT}) -install(TARGETS fmt EXPORT srsran_export) +add_to_exported_libs(fmt) diff --git a/include/srsran/adt/blocking_queue.h b/include/srsran/adt/blocking_queue.h index 421ade7407..0b6fd5956b 100644 --- a/include/srsran/adt/blocking_queue.h +++ b/include/srsran/adt/blocking_queue.h @@ -324,7 +324,7 @@ class base_blocking_queue cvar_empty.notify_one(); return {}; } - return std::move(t); + return make_unexpected(std::move(t)); } template diff --git a/include/srsran/adt/byte_buffer.h b/include/srsran/adt/byte_buffer.h index 57defd1827..57f597829c 100644 --- a/include/srsran/adt/byte_buffer.h +++ b/include/srsran/adt/byte_buffer.h @@ -206,7 +206,7 @@ class byte_buffer { byte_buffer buf; if (not buf.append(bytes)) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } return buf; } @@ -223,7 +223,7 @@ class byte_buffer { byte_buffer buf; if (not buf.append(other_begin, other_end)) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } return buf; } @@ -440,7 +440,7 @@ class byte_buffer_slice { auto buf = byte_buffer::create(bytes); if (not buf) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } return byte_buffer_slice(std::move(buf.value())); } @@ -450,7 +450,7 @@ class byte_buffer_slice { auto buf = byte_buffer::create(bytes); if (not buf) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } return byte_buffer_slice(std::move(buf.value())); } diff --git a/include/srsran/adt/byte_buffer_chain.h b/include/srsran/adt/byte_buffer_chain.h index f36e5031fc..730cc56bb5 100644 --- a/include/srsran/adt/byte_buffer_chain.h +++ b/include/srsran/adt/byte_buffer_chain.h @@ -177,11 +177,11 @@ class byte_buffer_chain static expected create(byte_buffer buf_) { auto buf = create(); - if (buf.is_error()) { - return default_error_t{}; + if (not buf.has_value()) { + return make_unexpected(default_error_t{}); } if (not buf.value().append(std::move(buf_))) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } return buf; } @@ -190,11 +190,11 @@ class byte_buffer_chain static expected create(byte_buffer_slice buf_) { auto buf = create(); - if (buf.is_error()) { - return default_error_t{}; + if (not buf.has_value()) { + return make_unexpected(default_error_t{}); } if (not buf.value().append(std::move(buf_))) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } return buf; } @@ -230,7 +230,7 @@ class byte_buffer_chain byte_buffer buf; for (const byte_buffer_slice& slice : slices()) { if (not buf.append(slice)) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } } return buf; diff --git a/include/srsran/adt/circular_map.h b/include/srsran/adt/circular_map.h index f6c73f387f..b51d45afb1 100644 --- a/include/srsran/adt/circular_map.h +++ b/include/srsran/adt/circular_map.h @@ -201,7 +201,7 @@ class circular_map { size_t idx = key % N; if (present[idx]) { - return srsran::expected(std::move(obj)); + return make_unexpected(std::move(obj)); } buffer[idx].emplace(key, std::move(obj)); present[idx] = true; diff --git a/include/srsran/adt/expected.h b/include/srsran/adt/expected.h index fa27f4cfc8..d15fb35be8 100644 --- a/include/srsran/adt/expected.h +++ b/include/srsran/adt/expected.h @@ -22,17 +22,24 @@ #pragma once -#include "srsran/support/srsran_assert.h" -#include -#include +#include "TartanLlama/expected.hpp" namespace srsran { struct default_error_t {}; struct default_success_t {}; -template -class expected; +template +using unexpected = tl::unexpected; + +template +unexpected> make_unexpected(E&& e) +{ + return unexpected>(std::forward(e)); +} + +template +using expected = tl::expected; namespace detail { template @@ -43,215 +50,6 @@ struct is_expected> : std::true_type { }; } // namespace detail -/// Class that can either hold a value type T, in case of success, or an error type E, in case of failure. -/// \tparam T type of success value -/// \tparam E type of error value -template -class expected -{ - static_assert(not std::is_same::value, "Expected and unexpected types cannot be of the same type"); - -public: - constexpr expected() : has_val(true), val(T{}) {} - expected(T&& t) noexcept : has_val(true), val(std::move(t)) {} - expected(const T& t) noexcept : has_val(true), val(t) {} - expected(E&& e) noexcept : has_val(false), unexpected(std::move(e)) {} - expected(const E& e) noexcept : has_val(false), unexpected(e) {} - template ::value and - not detail::is_expected::type>::value, - int>::type = 0> - explicit expected(U&& u) noexcept : has_val(true), val(std::forward(u)) - { - } - expected(const expected& other) - { - if (other.has_val) { - construct_val(other.val); - } else { - construct_error(other.unexpected); - } - } - expected(expected&& other) noexcept - { - if (other.has_val) { - construct_val(std::move(other.val)); - } else { - construct_error(std::move(other.unexpected)); - } - } - expected& operator=(const expected& other) - { - if (this != &other) { - expected(other).swap(*this); - } - return *this; - } - expected& operator=(expected&& other) noexcept - { - expected(std::move(other)).swap(*this); - return *this; - } - expected& operator=(const T& other) noexcept - { - if (not has_value()) { - unexpected.~E(); - has_val = true; - new (&val) T(other); - } else { - val = other; - } - return *this; - } - expected& operator=(T&& other) noexcept - { - if (not has_value()) { - unexpected.~E(); - has_val = true; - new (&val) T(std::move(other)); - } else { - val = std::move(other); - } - return *this; - } - expected& operator=(const E& other) noexcept - { - if (has_value()) { - val.~T(); - has_val = false; - new (&unexpected) E(other); - } else { - unexpected = other; - } - return *this; - } - expected& operator=(E&& other) noexcept - { - if (has_value()) { - val.~T(); - has_val = false; - new (&unexpected) E(std::move(other)); - } else { - unexpected = std::move(other); - } - return *this; - } - ~expected() { destroy(); } - - void set_value() - { - if (not has_value()) { - unexpected.~E(); - construct_val(T{}); - } else { - val = T{}; - } - } - void set_error() - { - if (has_value()) { - val.~T(); - construct_error(E{}); - } else { - unexpected = E{}; - } - } - template - void set_error(U&& other) - { - if (has_value()) { - val.~T(); - construct_error(std::forward(other)); - } else { - unexpected = std::forward(other); - } - } - - explicit operator bool() const noexcept { return has_value(); } - bool has_value() const noexcept { return has_val; } - bool is_error() const noexcept { return not has_value(); } - const T& value() const& noexcept - { - srsran_assert(has_value(), "Bad expected value access"); - return val; - } - T& value() & noexcept - { - srsran_assert(has_value(), "Bad expected value access"); - return val; - } - T value() && noexcept - { - srsran_assert(has_value(), "Bad expected value access"); - return std::move(val); - } - const E& error() const noexcept - { - srsran_assert(not has_value(), "Bad expected error access"); - return unexpected; - } - E& error() noexcept - { - srsran_assert(not has_value(), "Bad expected error access"); - return unexpected; - } - - void swap(expected& other) noexcept - { - using std::swap; - - if (has_value() and other.has_value()) { - swap(val, other.val); - } else if (not has_value() and not other.has_value()) { - swap(unexpected, other.unexpected); - } else if (has_value() and not other.has_value()) { - E err(std::move(other.unexpected)); - other.unexpected.~E(); - other.construct_val(std::move(val)); - val.~T(); - construct_error(std::move(err)); - } else if (!bool(*this) && bool(other)) { - other.swap(*this); - } - } - -private: - void construct_val(const T& v) noexcept - { - has_val = true; - new (&val) T(v); - } - void construct_val(T&& v) noexcept - { - has_val = true; - new (&val) T(std::move(v)); - } - void construct_error(const E& e) noexcept - { - has_val = false; - new (&unexpected) E(e); - } - void construct_error(E&& e) noexcept - { - has_val = false; - new (&unexpected) E(std::move(e)); - } - void destroy() noexcept - { - if (has_value()) { - val.~T(); - } else { - unexpected.~E(); - } - } - - bool has_val = false; - union { - T val; - E unexpected; - }; -}; - template using error_type = expected; diff --git a/include/srsran/adt/mutexed_mpmc_queue.h b/include/srsran/adt/mutexed_mpmc_queue.h index 7539e04fcb..b397414c0d 100644 --- a/include/srsran/adt/mutexed_mpmc_queue.h +++ b/include/srsran/adt/mutexed_mpmc_queue.h @@ -39,7 +39,7 @@ class queue_impl try_pop() { @@ -76,7 +76,7 @@ class queue_implqueue.push_blocking(elem); } - bool push_blocking(T&& elem) { return not this->queue.push_blocking(std::move(elem)).is_error(); } + bool push_blocking(T&& elem) { return this->queue.push_blocking(std::move(elem)).has_value(); } bool pop_blocking(T& elem) { diff --git a/include/srsran/asn1/asn1_utils.h b/include/srsran/asn1/asn1_utils.h index a7e59a680b..3973451be5 100644 --- a/include/srsran/asn1/asn1_utils.h +++ b/include/srsran/asn1/asn1_utils.h @@ -736,6 +736,14 @@ class fixed_octstring using iterator = typename std::array::iterator; using const_iterator = typename std::array::const_iterator; + fixed_octstring() = default; + explicit fixed_octstring(const std::array& array_) : octets_(array_) {} + fixed_octstring& operator=(const std::array& array_) + { + octets_ = array_; + return *this; + } + const uint8_t& operator[](uint32_t idx) const { return octets_[idx]; } uint8_t& operator[](uint32_t idx) { return octets_[idx]; } bool operator==(const fixed_octstring& other) const { return octets_ == other.octets_; } @@ -759,6 +767,7 @@ class fixed_octstring octet_string_helper::to_octet_string(octets_, val); return *this; } + const std::array to_bytes() const { return octets_; } /// Pack fixed-size octet string as per X.691 Section 16 - Encoding the octetstring type. /// \tparam N - number of items diff --git a/include/srsran/cu_cp/cell_meas_manager_config.h b/include/srsran/cu_cp/cell_meas_manager_config.h index c68787f04f..454bea43b4 100644 --- a/include/srsran/cu_cp/cell_meas_manager_config.h +++ b/include/srsran/cu_cp/cell_meas_manager_config.h @@ -39,8 +39,8 @@ namespace srs_cu_cp { /// Note that some optional values need to be provided by the DU upon F1Setup. struct serving_cell_meas_config { - nr_cell_id_t nci; ///< The NR cell identifier. - gnb_id_t gnb_id; ///< gNodeB identifier + nr_cell_identity nci; ///< The NR cell identifier. + gnb_id_t gnb_id; ///< gNodeB identifier /// If not set in config must be provided by config update after DU attach. std::optional pci; ///< Physical cell identifier. std::optional band; ///< NR band. @@ -50,7 +50,7 @@ struct serving_cell_meas_config { }; struct neighbor_cell_meas_config { - nr_cell_id_t nci; ///< The NR cell identifier. + nr_cell_identity nci; ///< The NR cell identifier. std::vector report_cfg_ids; ///< The configured report configs }; @@ -67,7 +67,7 @@ bool is_complete(const serving_cell_meas_config& cfg); /// \brief Cell manager configuration. struct cell_meas_manager_cfg { - std::map cells; // Measurement related configs for all known cells. + std::map cells; // Measurement related configs for all known cells. std::map report_config_ids; }; diff --git a/include/srsran/cu_cp/cu_cp_configuration.h b/include/srsran/cu_cp/cu_cp_configuration.h index 51713c0e90..3ac22079d2 100644 --- a/include/srsran/cu_cp/cu_cp_configuration.h +++ b/include/srsran/cu_cp/cu_cp_configuration.h @@ -53,6 +53,8 @@ struct cu_cp_configuration { /// Maximum number of CU-UP connections that the CU-CP may accept. unsigned max_nof_cu_ups = 6; /// Maximum number of UEs that the CU-CP may accept. + unsigned max_nof_ues = 8192; + ngap_configuration ngap_config; rrc_cfg_t rrc_config; ue_configuration ue_config; diff --git a/include/srsran/cu_cp/cu_cp_types.h b/include/srsran/cu_cp/cu_cp_types.h index 8d75c62118..69b14f0773 100644 --- a/include/srsran/cu_cp/cu_cp_types.h +++ b/include/srsran/cu_cp/cu_cp_types.h @@ -45,19 +45,17 @@ namespace srsran { namespace srs_cu_cp { -/// Maximum number of UEs per DU (implementation-defined). -const uint16_t MAX_NOF_UES_PER_DU = 1024; /// Maximum number of DUs supported by CU-CP (implementation-defined). const uint16_t MAX_NOF_DUS = 65535; -/// Maximum number of UEs supported by CU-CP (implementation-defined). -#define MAX_NOF_CU_UES (MAX_NOF_DUS * MAX_NOF_UES_PER_DU) -/// Maximum number of CU-UPs supported by CU-CP (implementation-defined). -const uint16_t MAX_NOF_CU_UPS = 65535; /// Maximum number of cells per DU supported by CU-CP (implementation-defined). const uint16_t MAX_NOF_DU_CELLS = 16; +/// Maximum number of CU-UPs supported by CU-CP (implementation-defined). +const uint16_t MAX_NOF_CU_UPS = 65535; +/// Maximum number of UEs supported by CU-CP (implementation-defined). +const uint64_t MAX_NOF_CU_UES = 4294967295; // 2^32 - 1 /// \brief ue_index internally used to identify the UE CU-CP-wide. -/// \remark The ue_index is derived from the DU index and the maximum number of UEs per DU. +/// \remark The ue_index is derived from the maximum number of DUs and the maximum number of UEs per DU. enum class ue_index_t : uint64_t { min = 0, max = MAX_NOF_CU_UES - 1, invalid = MAX_NOF_CU_UES }; /// Convert ue_index type to integer. @@ -87,21 +85,6 @@ constexpr inline std::underlying_type_t du_index_to_uint(du_index_t return static_cast>(du_index); } -/// Get DU index from UE index. -inline du_index_t get_du_index_from_ue_index(ue_index_t index) -{ - if (index == ue_index_t::invalid) { - return du_index_t::invalid; - } - return uint_to_du_index((ue_index_to_uint(index) / MAX_NOF_UES_PER_DU)); -} - -/// Generate a UE index from DU index and an index. -inline ue_index_t generate_ue_index(du_index_t du_index, uint16_t index) -{ - return uint_to_ue_index(du_index_to_uint(du_index) * MAX_NOF_UES_PER_DU + index); -} - /// Maximum number of CU-UPs supported by CU-CP (implementation-defined). enum class cu_up_index_t : uint16_t { min = 0, max = MAX_NOF_CU_UPS - 1, invalid = MAX_NOF_CU_UPS }; @@ -141,8 +124,8 @@ struct cu_cp_qos_config { // ASN1 types converted to common types struct cu_cp_tai { - std::string plmn_id; - uint32_t tac; + plmn_identity plmn_id = plmn_identity::test_value(); + uint32_t tac; }; struct cu_cp_user_location_info_nr { @@ -325,7 +308,7 @@ struct cu_cp_pdu_session_resource_setup_request { ue_index_t ue_index = ue_index_t::invalid; slotted_id_vector pdu_session_res_setup_items; uint64_t ue_aggregate_maximum_bit_rate_dl; - std::string serving_plmn; + plmn_identity serving_plmn = plmn_identity::test_value(); }; enum class cu_cp_qos_flow_map_ind { ul = 0, dl }; @@ -508,8 +491,8 @@ struct cu_cp_recommended_cells_for_paging { }; struct cu_cp_global_gnb_id { - std::string plmn_id; - gnb_id_t gnb_id; + plmn_identity plmn_id = plmn_identity::test_value(); + gnb_id_t gnb_id; }; struct cu_cp_amf_paging_target { diff --git a/include/srsran/cu_up/cu_up.h b/include/srsran/cu_up/cu_up.h index c4dfecf7bd..2f5b7b6453 100644 --- a/include/srsran/cu_up/cu_up.h +++ b/include/srsran/cu_up/cu_up.h @@ -56,7 +56,7 @@ class cu_up_e1ap_interface /// \brief Create a new UE context and handle bearer modification request. /// \param[in] msg The original bearer modification request. /// \return Returns message containing the index of the created UE and all response/failure message. - virtual e1ap_bearer_context_modification_response + virtual async_task handle_bearer_context_modification_request(const e1ap_bearer_context_modification_request& msg) = 0; /// \brief Handle bearer release command and remove the associated UE context. @@ -69,23 +69,16 @@ class cu_up_e1ap_interface /// \brief Get the state of the E1AP connection. /// \return True if E1AP is connected, false otherwise. virtual bool e1ap_is_connected() = 0; -}; - -/// Interface to notify about GTP-U packets (from the NGU) to the CU-UP -class cu_up_ngu_interface -{ -public: - virtual ~cu_up_ngu_interface() = default; - /// \brief Get the NGu PDU handler interface. - /// \return The NGu PDU handler interface. - virtual gtpu_demux_rx_upper_layer_interface& get_ngu_pdu_handler() = 0; + /// \brief Schedule an async task for an UE. + /// Can be used to initiate UE routines. + virtual void schedule_ue_async_task(ue_index_t ue_index, async_task task) = 0; }; -class cu_up_interface : public cu_up_e1ap_connection_notifier, public cu_up_e1ap_interface, public cu_up_ngu_interface +class cu_up_interface : public cu_up_e1ap_connection_notifier, public cu_up_e1ap_interface { public: - virtual ~cu_up_interface() = default; + ~cu_up_interface() override = default; virtual void start() = 0; diff --git a/include/srsran/du/du_cell_config_helpers.h b/include/srsran/du/du_cell_config_helpers.h index a1cce7a6c7..aef9097e93 100644 --- a/include/srsran/du/du_cell_config_helpers.h +++ b/include/srsran/du/du_cell_config_helpers.h @@ -27,7 +27,6 @@ #include "srsran/mac/config/mac_config_helpers.h" #include "srsran/ran/band_helper.h" #include "srsran/ran/five_qi.h" -#include "srsran/ran/nr_cgi_helpers.h" #include "srsran/ran/pdcch/pdcch_type0_css_coreset_config.h" #include "srsran/ran/tdd/tdd_ul_dl_config.h" #include "srsran/scheduler/config/cell_config_builder_params.h" @@ -84,10 +83,10 @@ inline scheduler_expert_config make_default_scheduler_expert_config() inline du_cell_config make_default_du_cell_config(const cell_config_builder_params_extended& params = {}) { du_cell_config cfg{}; - cfg.pci = params.pci; - cfg.tac = 1; - cfg.nr_cgi.plmn = "00101"; - cfg.nr_cgi.nci = config_helpers::make_nr_cell_identity({411, 22}, 1); + cfg.pci = params.pci; + cfg.tac = 1; + cfg.nr_cgi.plmn_id = plmn_identity::test_value(); + cfg.nr_cgi.nci = nr_cell_identity::create({411, 22}, 1).value(); cfg.dl_carrier = make_default_dl_carrier_configuration(params); cfg.ul_carrier = make_default_ul_carrier_configuration(params); diff --git a/include/srsran/du/du_high_wrapper_config.h b/include/srsran/du/du_high_wrapper_config.h index 9bfba97b34..a866399906 100644 --- a/include/srsran/du/du_high_wrapper_config.h +++ b/include/srsran/du/du_high_wrapper_config.h @@ -35,7 +35,7 @@ class slot_last_message_notifier; /// FAPI configuration for the DU high wrapper. struct du_high_wrapper_fapi_config { - std::string log_level; + srslog::basic_levels log_level; unsigned l2_nof_slots_ahead; std::optional executor; }; diff --git a/include/srsran/e1ap/cu_cp/e1ap_cu_cp_bearer_context_update.h b/include/srsran/e1ap/cu_cp/e1ap_cu_cp_bearer_context_update.h index d3f21f4f7e..ed1689605f 100644 --- a/include/srsran/e1ap/cu_cp/e1ap_cu_cp_bearer_context_update.h +++ b/include/srsran/e1ap/cu_cp/e1ap_cu_cp_bearer_context_update.h @@ -32,7 +32,7 @@ struct e1ap_bearer_context_setup_request { ue_index_t ue_index = ue_index_t::invalid; e1ap_security_info security_info; uint64_t ue_dl_aggregate_maximum_bit_rate; - std::string serving_plmn; + plmn_identity serving_plmn = plmn_identity::test_value(); std::string activity_notif_level; slotted_id_vector pdu_session_res_to_setup_list; std::optional ue_dl_maximum_integrity_protected_data_rate; diff --git a/include/srsran/e1ap/cu_up/e1ap_cu_up.h b/include/srsran/e1ap/cu_up/e1ap_cu_up.h index e3d56c9a9c..cb16cd371a 100644 --- a/include/srsran/e1ap/cu_up/e1ap_cu_up.h +++ b/include/srsran/e1ap/cu_up/e1ap_cu_up.h @@ -77,12 +77,15 @@ class e1ap_cu_up_notifier /// \brief Notifies the UE manager to create a UE context. /// \param[in] msg The received bearer context modification message. /// \return Returns a bearer context response message containing the index of the created UE context. - virtual e1ap_bearer_context_modification_response + virtual async_task on_bearer_context_modification_request_received(const e1ap_bearer_context_modification_request& msg) = 0; /// \brief Notifies the UE manager to release a UE context. /// \param[in] msg The received bearer context release command. virtual void on_bearer_context_release_command_received(const e1ap_bearer_context_release_command& msg) = 0; + + /// \brief Schedules async task on UE. + virtual void on_schedule_ue_async_task(srs_cu_up::ue_index_t ue_index, async_task task) = 0; }; /// \brief Interface to query statistics from the E1AP interface. diff --git a/include/srsran/f1ap/cu_cp/f1ap_cu.h b/include/srsran/f1ap/cu_cp/f1ap_cu.h index 76a047bfcc..a00928bb95 100644 --- a/include/srsran/f1ap/cu_cp/f1ap_cu.h +++ b/include/srsran/f1ap/cu_cp/f1ap_cu.h @@ -117,19 +117,22 @@ class f1ap_rrc_message_notifier /// This request should be made once the C-RNTI and cell of the UE is known. That generally corresponds to the moment /// a Initial UL RRC Message or a F1AP UE Context Setup Response are received. struct ue_rrc_context_creation_request { - ue_index_t ue_index; - rnti_t c_rnti; - nr_cell_global_id_t cgi; - byte_buffer du_to_cu_rrc_container; + ue_index_t ue_index = ue_index_t::invalid; ///> If this is invalid, a new UE will be created. + rnti_t c_rnti; + nr_cell_global_id_t cgi; + byte_buffer du_to_cu_rrc_container; std::optional prev_context; }; /// \brief Response by CU-CP to F1AP-CU request to create UE RRC context. struct ue_rrc_context_creation_response { + ue_index_t ue_index = ue_index_t::invalid; /// Notifier to be used by the F1AP to push new RRC PDUs to the UE RRC layer. f1ap_rrc_message_notifier* f1ap_rrc_notifier = nullptr; }; +using ue_rrc_context_creation_outcome = expected; + /// Notification from the F1AP that the loss of transaction reference information for some UEs has been lost. struct f1_ue_transaction_info_loss_event { std::vector ues_lost; @@ -151,11 +154,8 @@ class f1ap_du_processor_notifier : public du_setup_notifier, public f1ap_common_ public: virtual ~f1ap_du_processor_notifier() = default; - /// \brief Notifies the CU-CP to create a new UE instance. - virtual ue_index_t on_new_cu_cp_ue_required() = 0; - /// \brief Notifies the CU-CP that an RRC context has been created for an existing CU-CP UE. - virtual ue_rrc_context_creation_response + virtual ue_rrc_context_creation_outcome on_ue_rrc_context_creation_request(const ue_rrc_context_creation_request& req) = 0; /// \brief Indicates the reception of a UE Context Release Request (gNB-DU initiated) as per TS 38.473 diff --git a/include/srsran/f1ap/cu_cp/f1ap_cu_factory.h b/include/srsran/f1ap/cu_cp/f1ap_cu_factory.h index 2e8c3683ed..ad03a89438 100644 --- a/include/srsran/f1ap/cu_cp/f1ap_cu_factory.h +++ b/include/srsran/f1ap/cu_cp/f1ap_cu_factory.h @@ -33,7 +33,7 @@ namespace srs_cu_cp { /// Creates an instance of an F1AP interface, notifying outgoing packets on the specified listener object. std::unique_ptr create_f1ap(const f1ap_configuration& f1ap_cfg_, f1ap_message_notifier& f1ap_pdu_notifier_, - f1ap_du_processor_notifier& f1ap_du_processor_notifier_, + f1ap_du_processor_notifier& du_processor_notifier_, timer_manager& timers_, task_executor& ctrl_exec_); diff --git a/include/srsran/f1ap/cu_cp/f1ap_cu_ue_context_update.h b/include/srsran/f1ap/cu_cp/f1ap_cu_ue_context_update.h index 1990da1ad7..877d8001ef 100644 --- a/include/srsran/f1ap/cu_cp/f1ap_cu_ue_context_update.h +++ b/include/srsran/f1ap/cu_cp/f1ap_cu_ue_context_update.h @@ -134,7 +134,7 @@ struct f1ap_rat_freq_prio_info { }; struct f1ap_res_coordination_transfer_info { - uint64_t m_enb_cell_id; + nr_cell_identity m_enb_cell_id; // ResourceCoordinationEUTRACellInfo (optional) }; @@ -279,7 +279,7 @@ struct f1ap_ue_context_modification_request { }; struct f1ap_associated_scell_item { - nr_cell_id_t scell_id; + nr_cell_identity scell_id; }; /// \brief Response from F1AP-CU to CU once "UE Context Modification" procedure is complete. diff --git a/include/srsran/f1u/cu_up/f1u_rx_delivery_notifier.h b/include/srsran/f1u/cu_up/f1u_rx_delivery_notifier.h index 69d7eab0b9..808db22888 100644 --- a/include/srsran/f1u/cu_up/f1u_rx_delivery_notifier.h +++ b/include/srsran/f1u/cu_up/f1u_rx_delivery_notifier.h @@ -34,8 +34,10 @@ class f1u_rx_delivery_notifier public: virtual ~f1u_rx_delivery_notifier() = default; - virtual void on_transmit_notification(uint32_t highest_pdcp_sn) = 0; - virtual void on_delivery_notification(uint32_t highest_pdcp_sn) = 0; + virtual void on_transmit_notification(uint32_t highest_pdcp_sn) = 0; + virtual void on_delivery_notification(uint32_t highest_pdcp_sn) = 0; + virtual void on_retransmit_notification(uint32_t highest_pdcp_sn) = 0; + virtual void on_delivery_retransmitted_notification(uint32_t highest_pdcp_sn) = 0; }; } // namespace srs_cu_up diff --git a/include/srsran/f1u/cu_up/f1u_tx_sdu_handler.h b/include/srsran/f1u/cu_up/f1u_tx_sdu_handler.h index 4473a26b4a..d868a30122 100644 --- a/include/srsran/f1u/cu_up/f1u_tx_sdu_handler.h +++ b/include/srsran/f1u/cu_up/f1u_tx_sdu_handler.h @@ -37,7 +37,8 @@ class f1u_tx_sdu_handler /// \brief Immediately transmits a PDCP TX PDU to lower layers towards the DU. /// \param sdu The PDCP TX PDU to be transmitted to lower layers. - virtual void handle_sdu(byte_buffer sdu) = 0; + /// \param is_retx Determines whether the SDU is a PDCP retransmission or not. + virtual void handle_sdu(byte_buffer sdu, bool is_retx) = 0; /// \brief Enqueues a notification to discard the given PDCP TX PDU at the DU. /// diff --git a/include/srsran/f1u/du/f1u_rx_sdu_notifier.h b/include/srsran/f1u/du/f1u_rx_sdu_notifier.h index 2e6925b864..20c9b1f504 100644 --- a/include/srsran/f1u/du/f1u_rx_sdu_notifier.h +++ b/include/srsran/f1u/du/f1u_rx_sdu_notifier.h @@ -35,8 +35,8 @@ class f1u_rx_sdu_notifier public: virtual ~f1u_rx_sdu_notifier() = default; - virtual void on_new_sdu(byte_buffer sdu) = 0; - virtual void on_discard_sdu(uint32_t pdcp_sn) = 0; + virtual void on_new_sdu(byte_buffer sdu, bool is_retx) = 0; + virtual void on_discard_sdu(uint32_t pdcp_sn) = 0; }; } // namespace srs_du diff --git a/include/srsran/f1u/du/f1u_tx_delivery_handler.h b/include/srsran/f1u/du/f1u_tx_delivery_handler.h index 7e051825ae..7c9e38f4d5 100644 --- a/include/srsran/f1u/du/f1u_tx_delivery_handler.h +++ b/include/srsran/f1u/du/f1u_tx_delivery_handler.h @@ -59,6 +59,32 @@ class f1u_tx_delivery_handler /// /// \param highest_pdcp_sn The highest delivered PDCP sequence number virtual void handle_delivery_notification(uint32_t highest_pdcp_sn) = 0; + + /// \brief Handles a retransmit notification from lower layers (i.e. from RLC AM). + /// + /// Notification must be performed in ascending order of PDCP sequence numbers. It is the responsibility of the + /// calling function (i.e. the RLC) to prevent out-of-order notifications. + /// + /// This function is quick and shall be called directly from pcell_executor to avoid excessive transitions across + /// executors. + /// + /// Safe execution from: pcell_executor + /// + /// \param highest_pdcp_sn The highest retransmitted PDCP sequence number + virtual void handle_retransmit_notification(uint32_t highest_pdcp_sn) = 0; + + /// \brief Handles a delivery retransmitted notification from lower layers (i.e. from RLC AM). + /// + /// Notification must be performed in ascending order of PDCP sequence numbers. It is the responsibility of the + /// calling function (i.e. the RLC) to prevent out-of-order notifications. + /// + /// This function is quick and shall be called directly from pcell_executor to avoid excessive transitions across + /// executors. + /// + /// Safe execution from: pcell_executor + /// + /// \param highest_pdcp_sn The highest delivered retransmitted PDCP sequence number + virtual void handle_delivery_retransmitted_notification(uint32_t highest_pdcp_sn) = 0; }; } // namespace srs_du diff --git a/include/srsran/ngap/ngap_configuration.h b/include/srsran/ngap/ngap_configuration.h index 94f62159f2..9f101fd6fa 100644 --- a/include/srsran/ngap/ngap_configuration.h +++ b/include/srsran/ngap/ngap_configuration.h @@ -36,7 +36,7 @@ namespace srs_cu_cp { struct ngap_configuration { gnb_id_t gnb_id{0, 22}; std::string ran_node_name; - std::string plmn; /// Full PLMN as string (without possible filler digit) e.g. "00101" + plmn_identity plmn = plmn_identity::test_value(); unsigned tac; std::vector slice_configurations; std::chrono::seconds pdu_session_setup_timeout; // timeout for pdu session setup in seconds diff --git a/include/srsran/ngap/ngap_configuration_helpers.h b/include/srsran/ngap/ngap_configuration_helpers.h index 57314551e7..dde0e07594 100644 --- a/include/srsran/ngap/ngap_configuration_helpers.h +++ b/include/srsran/ngap/ngap_configuration_helpers.h @@ -36,7 +36,7 @@ inline srs_cu_cp::ngap_configuration make_default_ngap_config() srs_cu_cp::ngap_configuration cfg{}; cfg.gnb_id = {411, 22}; cfg.ran_node_name = "srsgnb01"; - cfg.plmn = "00101"; + cfg.plmn = plmn_identity::test_value(); cfg.tac = 7; s_nssai_t slice_cfg; slice_cfg.sst = 1; @@ -53,17 +53,6 @@ inline bool is_valid_configuration(const srs_cu_cp::ngap_configuration& config) fmt::print("RAN node name is empty\n"); return false; } - - if (config.plmn.empty()) { - fmt::print("PLMN id is empty\n"); - return false; - } - - if (config.plmn.find("0x") != std::string::npos) { - fmt::print("PLMN must not contain 0x\n"); - return false; - } - return true; } diff --git a/include/srsran/ngap/ngap_handover.h b/include/srsran/ngap/ngap_handover.h index da65526ea9..6c9df7afed 100644 --- a/include/srsran/ngap/ngap_handover.h +++ b/include/srsran/ngap/ngap_handover.h @@ -38,7 +38,7 @@ struct ngap_ue_source_handover_context { struct ngap_handover_preparation_request { ue_index_t ue_index = ue_index_t::invalid; gnb_id_t gnb_id; - nr_cell_id_t nci; + nr_cell_identity nci; std::map> pdu_sessions; }; diff --git a/include/srsran/ngap/ngap_setup.h b/include/srsran/ngap/ngap_setup.h index 59796b04ee..d18f6b4cb8 100644 --- a/include/srsran/ngap/ngap_setup.h +++ b/include/srsran/ngap/ngap_setup.h @@ -33,7 +33,7 @@ namespace srs_cu_cp { // enum class ngap_handov_type { intra5gs = 0, fivegs_to_eps, eps_to_5gs, fivegs_to_utran }; struct ngap_broadcast_plmn_item { - std::string plmn_id; + plmn_identity plmn_id = plmn_identity::test_value(); std::vector tai_slice_support_list; }; diff --git a/include/srsran/ngap/ngap_types.h b/include/srsran/ngap/ngap_types.h index 015d4d902f..3a149254ec 100644 --- a/include/srsran/ngap/ngap_types.h +++ b/include/srsran/ngap/ngap_types.h @@ -23,7 +23,7 @@ #pragma once #include "srsran/adt/optional.h" -#include +#include "srsran/ran/plmn_identity.h" #include namespace srsran { @@ -48,10 +48,10 @@ inline amf_ue_id_t uint_to_amf_ue_id(std::underlying_type_t id) // Globally unique AMF identifier. struct guami_t { - std::string plmn; - uint16_t amf_set_id; - uint8_t amf_pointer; - uint8_t amf_region_id; + plmn_identity plmn = plmn_identity::test_value(); + uint16_t amf_set_id; + uint8_t amf_pointer; + uint8_t amf_region_id; }; struct ngap_ue_aggr_max_bit_rate { diff --git a/include/srsran/nru/nru_message.h b/include/srsran/nru/nru_message.h index dd44ceb28d..e12fbb6b57 100644 --- a/include/srsran/nru/nru_message.h +++ b/include/srsran/nru/nru_message.h @@ -180,8 +180,8 @@ struct nru_dl_message { { nru_dl_message copy = {}; expected buf = t_pdu.deep_copy(); - if (buf.is_error()) { - return default_error_t{}; + if (not buf.has_value()) { + return make_unexpected(default_error_t{}); } copy.t_pdu = std::move(buf.value()); copy.dl_user_data = dl_user_data; @@ -209,12 +209,12 @@ struct nru_ul_message { nru_ul_message copy = {}; if (t_pdu.has_value()) { expected buf = t_pdu.value().deep_copy(); - if (buf.is_error()) { - return default_error_t{}; + if (not buf.has_value()) { + return make_unexpected(default_error_t{}); } expected chain = byte_buffer_chain::create(std::move(buf.value())); - if (chain.is_error()) { - return default_error_t{}; + if (not chain.has_value()) { + return make_unexpected(default_error_t{}); } copy.t_pdu = std::move(chain.value()); } diff --git a/include/srsran/pdcp/pdcp_tx.h b/include/srsran/pdcp/pdcp_tx.h index fe5f77bcaa..ea1370d0cd 100644 --- a/include/srsran/pdcp/pdcp_tx.h +++ b/include/srsran/pdcp/pdcp_tx.h @@ -59,8 +59,8 @@ class pdcp_tx_lower_notifier pdcp_tx_lower_notifier(const pdcp_tx_lower_notifier&&) = delete; pdcp_tx_lower_notifier& operator=(const pdcp_tx_lower_notifier&&) = delete; - virtual void on_new_pdu(byte_buffer pdu) = 0; ///< Pass PDCP PDU to the lower layers. - virtual void on_discard_pdu(uint32_t pdcp_sn) = 0; ///< Order lower layers to discard PDU + virtual void on_new_pdu(byte_buffer pdu, bool is_retx) = 0; ///< Pass PDCP PDU to the lower layers. + virtual void on_discard_pdu(uint32_t pdcp_sn) = 0; ///< Order lower layers to discard PDU }; /// This interface represents the notification entry point of the transmitting side of a PDCP entity. @@ -96,6 +96,22 @@ class pdcp_tx_lower_interface /// /// \param highest_sn Highest in a sequence delivered PDCP PDU sequence number. virtual void handle_delivery_notification(uint32_t highest_sn) = 0; + + /// \brief Informs the PDCP entity about the highest PDCP PDU sequence number of the PDCP PDU that was retransmitted + /// by the lower layers (i.e. by the RLC AM). + /// + /// This notification is only applicable for RLC AM. + /// + /// \param highest_sn Highest retransmitted PDCP PDU sequence number. + virtual void handle_retransmit_notification(uint32_t highest_sn) = 0; + + /// \brief Informs the PDCP about the highest PDCP PDU sequence number of the retransmitted PDCP PDU that was + /// successfully delivered in sequence towards the UE. + /// + /// This notification is only applicable for RLC AM. + /// + /// \param highest_sn Highest in a sequence delivered retransmitted PDCP PDU sequence number. + virtual void handle_delivery_retransmitted_notification(uint32_t highest_sn) = 0; }; /// This interface represents the data entry point of the transmitting side of a PDCP entity. diff --git a/include/srsran/phy/upper/channel_processors/channel_processor_factories.h b/include/srsran/phy/upper/channel_processors/channel_processor_factories.h index 47476c3b8b..b07b075fe4 100644 --- a/include/srsran/phy/upper/channel_processors/channel_processor_factories.h +++ b/include/srsran/phy/upper/channel_processors/channel_processor_factories.h @@ -182,9 +182,12 @@ create_pdsch_lite_processor_factory_sw(std::shared_ptr modulator_factory, std::shared_ptr dmrs_factory); -std::shared_ptr create_pdsch_processor_pool(std::shared_ptr, - unsigned max_nof_processors, - bool blocking = false); +std::shared_ptr +create_pdsch_processor_asynchronous_pool(std::shared_ptr pdsch_proc_factory, + unsigned max_nof_processors); + +std::shared_ptr +create_pdsch_processor_pool(std::shared_ptr pdsch_proc_factory, unsigned max_nof_processors); class prach_detector_factory { diff --git a/include/srsran/radio/radio_configuration.h b/include/srsran/radio/radio_configuration.h index ffa8687b9c..21066f3a11 100644 --- a/include/srsran/radio/radio_configuration.h +++ b/include/srsran/radio/radio_configuration.h @@ -24,6 +24,7 @@ #include "srsran/adt/static_vector.h" #include "srsran/radio/radio_constants.h" +#include "srsran/srslog/logger.h" #include "srsran/support/error_handling.h" namespace srsran { @@ -133,7 +134,7 @@ struct radio { /// \remark Not all driver and/or devices support this feature. std::string args; /// Logging level. Leave empty for default. - std::string log_level; + srslog::basic_levels log_level; }; /// Converts a string into a clock source. No error or invalid type is returned if the string is not valid. diff --git a/include/srsran/ran/bcd_helpers.h b/include/srsran/ran/bcd_helper.h similarity index 63% rename from include/srsran/ran/bcd_helpers.h rename to include/srsran/ran/bcd_helper.h index 542797908a..91f92cef84 100644 --- a/include/srsran/ran/bcd_helpers.h +++ b/include/srsran/ran/bcd_helper.h @@ -22,40 +22,155 @@ #pragma once -#include -#include +#include "srsran/adt/span.h" +#include #include namespace srsran { -/// Convert between string and BCD-coded MCC. -/// Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xf. -/// MCC 001 results in 0xf001 -inline bool string_to_mcc(std::string str, uint16_t* mcc) +namespace bcd_helper { + +/// Check if integer corresponds to a valid BCD-encoded MCC value (3 digits). +inline bool is_valid_mcc(uint16_t mcc) +{ + if ((mcc & 0xf000U) != 0xf000U) { + return false; + } + if (((mcc & 0xf00U) >> 8U) > 9U || ((mcc & 0x0f0U) >> 4U) > 9U || (mcc & 0x00fU) > 9U) { + return false; + } + return true; +} + +/// Check if string corresponds to a valid MCC value (3 digits). +inline bool is_valid_mcc(const std::string& mcc_str) { - uint32_t len = (uint32_t)str.size(); + size_t len = mcc_str.size(); if (len != 3) { return false; } - if (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2])) { + if (!isdigit(mcc_str[0]) || !isdigit(mcc_str[1]) || !isdigit(mcc_str[2])) { return false; } - *mcc = 0xf000; - *mcc |= ((uint8_t)(str[0] - '0') << 8); - *mcc |= ((uint8_t)(str[1] - '0') << 4); - *mcc |= ((uint8_t)(str[2] - '0')); + return true; +} + +/// Check if array of digits corresponds to a valid MCC value (3 digits). +inline bool is_valid_mcc(const std::array& bytes) +{ + for (uint8_t byte : bytes) { + if (byte > 9) { + return false; + } + } + return true; +} + +/// Check if integer corresponds to a valid BCD-encoded MNC value (2 or 3 digits). +inline bool is_valid_mnc(uint16_t mnc) +{ + if ((mnc & 0xf000) != 0xf000) { + return false; + } + unsigned digit3 = mnc & 0xf00U; + if (digit3 != 0xf00U && digit3 > 0x900U) { + return false; + } + if ((mnc & 0x0f0U) > 0x90U || (mnc & 0x00fU) > 9U) { + return false; + } + return true; +} + +/// Check if string corresponds to a valid MNC value (2 or 3 digits). +inline bool is_valid_mnc(const std::string& mnc_str) +{ + size_t len = mnc_str.size(); + if (len != 3U && len != 2U) { + return false; + } + for (unsigned i = 0; i != len; ++i) { + if (!isdigit(mnc_str[i])) { + return false; + } + } + return true; +} + +/// Check if array of digits corresponds to a valid MNC value (2 or 3 digits). +inline bool is_valid_mnc(span bytes) +{ + if (bytes.size() != 2 and bytes.size() != 3) { + return false; + } + for (uint8_t byte : bytes) { + if (byte > 9) { + return false; + } + } + return true; +} + +inline bool is_valid_plmn(uint32_t bcd_plmn) +{ + if ((bcd_plmn & 0xff000000U) != 0) { + return false; + } + return true; +} + +/// Extract BCD-coded MCC from PLMN +inline uint16_t bcd_plmn_to_mcc(uint32_t plmn) +{ + if (not is_valid_plmn(plmn)) { + return 0U; + } + uint16_t mcc = 0xf000U; + mcc |= (plmn & 0x0f0000U) >> 8; // MCC digit 1 + mcc |= (plmn & 0xf00000U) >> 16; // MCC digit 2 + mcc |= (plmn & 0x00f00U) >> 8; // MCC digit 3 + return mcc; +} + +/// Extract BCD-coded MCC from PLMN +inline uint16_t bcd_plmn_to_mnc(uint32_t plmn) +{ + if (not is_valid_plmn(plmn)) { + return 0U; + } + bool is_2_digit = (plmn & 0x00f000) >> 12 == 0xf; + uint16_t mnc; + mnc = 0xf000U; + mnc |= is_2_digit ? 0x0f00U : (plmn & 0x00f000) >> 4; // MNC digit 1 + mnc |= (plmn & 0xf) << 4; // MNC digit 2 + mnc |= (plmn & 0xf0) >> 4; // MNC digit 3 + return mnc; +} + +/// \brief Convert string to BCD-coded MCC. +/// Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xf. +/// MCC 001 results in 0xf001 +inline bool string_to_mcc(const std::string& str, uint16_t* mcc) +{ + if (not bcd_helper::is_valid_mcc(str)) { + return false; + } + *mcc = 0xf000U; + *mcc |= static_cast(str[0] - '0') << 8U; + *mcc |= static_cast(str[1] - '0') << 4U; + *mcc |= static_cast(str[2] - '0'); return true; } inline bool mcc_to_string(uint16_t mcc, std::string* str) { - if ((mcc & 0xf000) != 0xf000) { + if (not bcd_helper::is_valid_mcc(mcc)) { return false; } *str = ""; - *str += ((mcc & 0x0f00) >> 8) + '0'; - *str += ((mcc & 0x00f0) >> 4) + '0'; - *str += (mcc & 0x000f) + '0'; + *str += ((mcc & 0x0f00U) >> 8U) + '0'; + *str += ((mcc & 0x00f0U) >> 4U) + '0'; + *str += (mcc & 0x000fU) + '0'; return true; } @@ -97,30 +212,22 @@ inline std::string mcc_bytes_to_string(uint8_t* mcc_bytes) /// Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xf. /// MNC 001 results in 0xf001 /// MNC 01 results in 0xff01 -inline bool string_to_mnc(std::string str, uint16_t* mnc) +inline bool string_to_mnc(const std::string& str, uint16_t* mnc) { - uint32_t len = str.size(); - if (len != 3 && len != 2) { + if (not bcd_helper::is_valid_mnc(str)) { return false; } + uint32_t len = str.size(); if (len == 3) { - if (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2])) { - return false; - } *mnc = 0xf000; *mnc |= ((uint8_t)(str[0] - '0') << 8); *mnc |= ((uint8_t)(str[1] - '0') << 4); *mnc |= ((uint8_t)(str[2] - '0')); - } - if (len == 2) { - if (!isdigit(str[0]) || !isdigit(str[1])) { - return false; - } + } else { *mnc = 0xff00; *mnc |= ((uint8_t)(str[0] - '0') << 4); *mnc |= ((uint8_t)(str[1] - '0')); } - return true; } @@ -209,33 +316,19 @@ std::string mnc_bytes_to_string(Vec mnc_bytes) /// MNC 001 represented as 0xf001 /// MNC 01 represented as 0xff01 /// PLMN encoded as per TS 38.413 sec 9.3.3.5 -inline void ngap_plmn_to_mccmnc(uint32_t plmn, uint16_t* mcc, uint16_t* mnc) +inline bool ngap_plmn_to_mccmnc(uint32_t plmn, uint16_t* mcc, uint16_t* mnc) { - uint8_t nibbles[6]; - nibbles[0] = (plmn & 0xf00000) >> 20; - nibbles[1] = (plmn & 0x0f0000) >> 16; - nibbles[2] = (plmn & 0x00f000) >> 12; - nibbles[3] = (plmn & 0x000f00) >> 8; - nibbles[4] = (plmn & 0x0000f0) >> 4; - nibbles[5] = (plmn & 0x00000f); - - *mcc = 0xf000; - *mnc = 0xf000; - *mcc |= nibbles[1] << 8; // MCC digit 1 - *mcc |= nibbles[0] << 4; // MCC digit 2 - *mcc |= nibbles[3]; // MCC digit 3 - - if (nibbles[2] == 0xf) { - // 2-digit MNC - *mnc |= 0x0f00; // MNC digit 1 - *mnc |= nibbles[5] << 4; // MNC digit 2 - *mnc |= nibbles[4]; // MNC digit 3 - } else { - // 3-digit MNC - *mnc |= nibbles[2] << 8; // MNC digit 1 - *mnc |= nibbles[5] << 4; // MNC digit 2 - *mnc |= nibbles[4]; // MNC digit 3 + uint16_t mcc_tmp = bcd_plmn_to_mcc(plmn); + if (mcc_tmp == 0) { + return false; + } + uint16_t mnc_tmp = bcd_plmn_to_mnc(plmn); + if (mnc_tmp == 0) { + return false; } + *mcc = mcc_tmp; + *mnc = mnc_tmp; + return true; } /// Convert BCD-coded MCC and MNC to PLMN. @@ -296,11 +389,14 @@ inline uint32_t plmn_string_to_bcd(const std::string& plmn) inline std::string plmn_bcd_to_string(uint32_t plmn) { uint16_t mcc, mnc; - ngap_plmn_to_mccmnc(plmn, &mcc, &mnc); + if (not bcd_helper::ngap_plmn_to_mccmnc(plmn, &mcc, &mnc)) { + return ""; + } std::string mcc_string, mnc_string; mcc_to_string(mcc, &mcc_string); mnc_to_string(mnc, &mnc_string); return mcc_string + mnc_string; } +} // namespace bcd_helper } // namespace srsran diff --git a/include/srsran/ran/gnb_id.h b/include/srsran/ran/gnb_id.h index 1be1749f80..070d8030c9 100644 --- a/include/srsran/ran/gnb_id.h +++ b/include/srsran/ran/gnb_id.h @@ -26,14 +26,10 @@ namespace srsran { -using nr_cell_id_t = uint64_t; - struct gnb_id_t { uint32_t id; ///< gNodeB identifier. uint8_t bit_length; ///< Length of gNB identity in bits. Values {22,...,32}. - bool contains_nci(nr_cell_id_t nci) const { return (nci >> (36U - bit_length)) == id; } - bool operator==(const gnb_id_t& rhs) const { return id == rhs.id && bit_length == rhs.bit_length; } bool operator!=(const gnb_id_t& rhs) const { return id != rhs.id || bit_length != rhs.bit_length; } }; diff --git a/include/srsran/ran/nr_cell_identity.h b/include/srsran/ran/nr_cell_identity.h new file mode 100644 index 0000000000..1181c3ee37 --- /dev/null +++ b/include/srsran/ran/nr_cell_identity.h @@ -0,0 +1,124 @@ +/* + * + * 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/expected.h" +#include "srsran/ran/gnb_id.h" +#include "srsran/support/srsran_assert.h" +#include +#include +#include + +namespace srsran { + +/// \brief 36-bit identifying an NR Cell Id as specified in subclause 9.3.1.7 of 3GPP TS 38.413. +/// \remark The leftmost (22-32) bits of the NR Cell Identity correspond to the gNB ID and remaining (4-14) bits for +/// local Cell ID. +class nr_cell_identity +{ + constexpr nr_cell_identity(uint64_t val_) : val(val_) {} + +public: + static constexpr nr_cell_identity min() { return nr_cell_identity{0x0}; } + static constexpr nr_cell_identity max() { return nr_cell_identity{((uint64_t)1U << 36U) - 1U}; } + + nr_cell_identity() : val(0) {} + + static expected create(uint64_t val) + { + if (val > max().val) { + return make_unexpected(default_error_t{}); + } + return nr_cell_identity{val}; + } + + static expected create(gnb_id_t gnb_id, uint16_t local_cell_id) + { + if (gnb_id.bit_length < 22 or gnb_id.bit_length > 32) { + // invalid bit length. + return make_unexpected(default_error_t{}); + } + if (local_cell_id >= (1U << (36U - gnb_id.bit_length))) { + // invalid local cell id. + return make_unexpected(default_error_t{}); + } + return nr_cell_identity{(uint64_t)gnb_id.id << (36U - gnb_id.bit_length) | local_cell_id}; + } + + static expected parse_hex(const std::string& hex_str) + { + const unsigned digits = nof_bits() / 4; + if (hex_str.size() > digits) { + return make_unexpected(default_error_t{}); + } + char* p; + uint64_t n = std::strtoul(hex_str.c_str(), &p, 16); + if (*p != 0) { + return make_unexpected(default_error_t{}); + } + return nr_cell_identity{n}; + } + + uint64_t value() const { return val; } + + /// Extract local cell ID from NR Cell Identity. + uint16_t local_cell_id(unsigned nof_local_cell_id_bits) const + { + srsran_assert(nof_local_cell_id_bits >= 4 and nof_local_cell_id_bits <= 14, "Invalid number of local cell id bits"); + return val & ((1U << nof_local_cell_id_bits) - 1U); + } + + /// Extract gNB-DU ID from NR Cell Identity. + gnb_id_t gnb_id(unsigned nof_gnb_id_bits) const + { + srsran_assert(nof_gnb_id_bits >= 22 and nof_gnb_id_bits <= 32, "Invalid number of gNB-DU ID bits"); + return gnb_id_t{static_cast(val >> (36U - nof_gnb_id_bits)), static_cast(nof_gnb_id_bits)}; + } + + static size_t nof_bits() { return 36; } + + bool operator==(const nr_cell_identity& nci) const { return val == nci.val; } + bool operator!=(const nr_cell_identity& nci) const { return !(*this == nci); } + bool operator<(const nr_cell_identity& nci) const { return val < nci.val; } + bool operator<=(const nr_cell_identity& nci) const { return val <= nci.val; } + bool operator>(const nr_cell_identity& nci) const { return val > nci.val; } + bool operator>=(const nr_cell_identity& nci) const { return val >= nci.val; } + +private: + uint64_t val; +}; + +} // namespace srsran + +namespace fmt { + +template <> +struct formatter : formatter { + template + auto format(const srsran::nr_cell_identity& val, FormatContext& ctx) + { + return formatter::format(val.value(), ctx); + } +}; + +} // namespace fmt \ No newline at end of file diff --git a/include/srsran/ran/nr_cgi.h b/include/srsran/ran/nr_cgi.h index b67101adca..7a8e38c885 100644 --- a/include/srsran/ran/nr_cgi.h +++ b/include/srsran/ran/nr_cgi.h @@ -22,43 +22,22 @@ #pragma once -#include -#include -#include +#include "srsran/ran/nr_cell_identity.h" +#include "srsran/ran/plmn_identity.h" namespace srsran { -/// \brief 36-bit identifying an NR Cell Id as specified in subclause 9.3.1.7 of 3GPP TS 38.413 -/// \remark The leftmost (22-32) bits of the NR Cell Identity correspond to the gNB ID and remaining (4-14) bits for -/// Cell ID. -using nr_cell_id_t = uint64_t; - /// \brief The NR Cell Global Identity (NR-CGI) struct nr_cell_global_id_t { nr_cell_global_id_t() = default; - nr_cell_global_id_t(uint16_t mcc_, uint16_t mnc_, std::string plmn_, std::string plmn_hex_, nr_cell_id_t nci_) : - mcc(mcc_), mnc(mnc_), plmn(plmn_), plmn_hex(plmn_hex_), nci(nci_) - { - } + nr_cell_global_id_t(plmn_identity plmn_id_, nr_cell_identity nci_) : plmn_id(plmn_id_), nci(nci_) {} - /// 3 digits mobile country code (BCD encoded). - uint16_t mcc; - /// 2 or 3 digits mobile network code (BCD encoded). - uint16_t mnc; - /// Full PLMN as string (without possible filler digit). - std::string plmn; - /// Full PLMN as hex string with filler digit if needed. - std::string plmn_hex; + /// PLMN identity. + plmn_identity plmn_id = plmn_identity::test_value(); /// NR cell id. - nr_cell_id_t nci; + nr_cell_identity nci; - bool operator==(const nr_cell_global_id_t& rhs) const - { - std::string plmn_copy{plmn}, rhs_plmn_copy{rhs.plmn}; - std::transform(plmn_copy.begin(), plmn_copy.end(), plmn_copy.begin(), ::toupper); - std::transform(rhs_plmn_copy.begin(), rhs_plmn_copy.end(), rhs_plmn_copy.begin(), ::toupper); - return nci == rhs.nci && plmn_copy == rhs_plmn_copy; - } + bool operator==(const nr_cell_global_id_t& rhs) const { return plmn_id == rhs.plmn_id and nci == rhs.nci; } bool operator!=(const nr_cell_global_id_t& rhs) const { return !(rhs == *this); } }; diff --git a/include/srsran/ran/nr_cgi_helpers.h b/include/srsran/ran/nr_cgi_helpers.h deleted file mode 100644 index eda49debd1..0000000000 --- a/include/srsran/ran/nr_cgi_helpers.h +++ /dev/null @@ -1,69 +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 "gnb_id.h" -#include "nr_cgi.h" -#include "fmt/format.h" - -namespace srsran { -namespace config_helpers { - -/// Returns true if the given struct is valid, otherwise false. -inline bool is_valid(const nr_cell_global_id_t& cgi) -{ - // MCC and MNC cannot be null (init value is 0xf000). - if (cgi.mcc == 0 || cgi.mcc == 0xf000 || cgi.mnc == 0 || cgi.mnc == 0xf000 || cgi.plmn.empty() || - cgi.plmn_hex.empty()) { - fmt::print("Invalid MCC, MNC or PLMN configuration.\n"); - return false; - } - - // Allow gNB ID and cell ID to be zero. - - return true; -} - -/// Returns NR Cell Identity. -inline uint64_t make_nr_cell_identity(gnb_id_t gnb_id, uint16_t cell_identity) -{ - // The leftmost (22-32) bits of the NR Cell Identity correspond to the gNB ID and remaining (4-14) bits for Cell ID. - uint64_t nci = 0; - const uint64_t gnb_id_mask = ((uint64_t)1U << gnb_id.bit_length) - 1; - const uint16_t cell_identity_mask = ((uint16_t)1U << (36U - gnb_id.bit_length)) - 1; - nci = (uint64_t)(gnb_id.id & gnb_id_mask) << (36U - gnb_id.bit_length); - nci |= (cell_identity & cell_identity_mask); - return nci; -} - -/// Returns the gNB ID from the NR Cell Identity. -inline gnb_id_t get_gnb_id(nr_cell_id_t nr_cell_id, uint8_t gnb_id_bit_length) -{ - gnb_id_t gnb_id; - gnb_id.id = (uint32_t)nr_cell_id >> (36 - gnb_id_bit_length); - gnb_id.bit_length = gnb_id_bit_length; - return gnb_id; -}; - -} // namespace config_helpers -} // namespace srsran diff --git a/include/srsran/ran/plmn_identity.h b/include/srsran/ran/plmn_identity.h new file mode 100644 index 0000000000..f46222973c --- /dev/null +++ b/include/srsran/ran/plmn_identity.h @@ -0,0 +1,282 @@ +/* + * + * 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/expected.h" +#include "srsran/adt/static_vector.h" +#include "srsran/ran/bcd_helper.h" + +namespace srsran { + +/// Identifier of a Mobile Country Code (MCC). +class mobile_country_code +{ + constexpr mobile_country_code(uint16_t mcc) : bcd(mcc) {} + +public: + mobile_country_code() = delete; + + /// Test value for mobile_country_code "001". + static constexpr mobile_country_code test_value() { return mobile_country_code{0xf001}; } + + /// Conversion of a BCD-encoded MCC integer into a mobile_country_code. + static expected from_bcd(uint16_t bcd_mcc) + { + if (!bcd_helper::is_valid_mcc(bcd_mcc)) { + return make_unexpected(default_error_t{}); + } + return mobile_country_code{bcd_mcc}; + } + + /// Conversion of string of 3 digits into a mobile_country_code. + static expected from_string(const std::string& mcc_str) + { + uint16_t mcc; + if (!bcd_helper::string_to_mcc(mcc_str, &mcc)) { + return make_unexpected(default_error_t{}); + } + return mobile_country_code{mcc}; + } + + /// Conversion of an array of 3 bytes into a mobile_country_code. + static expected from_bytes(const std::array& bytes) + { + uint16_t mcc; + if (not bcd_helper::is_valid_mcc(bytes)) { + return make_unexpected(default_error_t{}); + } + bcd_helper::bytes_to_mcc(bytes.data(), &mcc); + return mobile_country_code{mcc}; + } + + uint16_t to_bcd() const { return bcd; } + + std::string to_string() const + { + std::string str; + bcd_helper::mcc_to_string(bcd, &str); + return str; + } + + std::array to_bytes() const + { + std::array bytes = {}; + bcd_helper::mcc_to_bytes(bcd, bytes.data()); + return bytes; + } + + static size_t nof_digits() { return 3; } + + bool operator==(const mobile_country_code& rhs) const { return bcd == rhs.bcd; } + bool operator!=(const mobile_country_code& rhs) const { return bcd != rhs.bcd; } + +private: + // BCD representation of MCC. + uint16_t bcd; +}; + +/// Identifier of a Mobile Network Code (MNC). +class mobile_network_code +{ + constexpr mobile_network_code(uint16_t mnc) : bcd(mnc) {} + +public: + mobile_network_code() = delete; + + /// Test value for mobile_country_code "01". + static constexpr mobile_network_code test_value() { return mobile_network_code{0xff01U}; } + + /// Conversion of a BCD-encoded MNC integer into a mobile_network_code. + static expected from_bcd(uint16_t bcd_mnc) + { + if (!bcd_helper::is_valid_mnc(bcd_mnc)) { + return make_unexpected(default_error_t{}); + } + return mobile_network_code{bcd_mnc}; + } + + /// Conversion of string of 2/3 digits into a mobile_network_code. + static expected from_string(const std::string& mnc_str) + { + uint16_t mnc; + if (!bcd_helper::string_to_mnc(mnc_str, &mnc)) { + return make_unexpected(default_error_t{}); + } + return mobile_network_code{mnc}; + } + + /// Conversion of an array of 2/3 bytes into a mobile_network_code. + static expected from_bytes(span bytes) + { + if (not bcd_helper::is_valid_mnc(bytes)) { + return make_unexpected(default_error_t{}); + } + uint16_t mnc; + bcd_helper::bytes_to_mnc(bytes.data(), &mnc, bytes.size()); + return mobile_network_code{mnc}; + } + + uint16_t to_bcd() const { return bcd; } + + std::string to_string() const + { + std::string str; + bcd_helper::mnc_to_string(bcd, &str); + return str; + } + + static_vector to_bytes() const + { + static_vector bytes(3); + uint8_t len; + bcd_helper::mnc_to_bytes(bcd, bytes.data(), &len); + bytes.resize(len); + return bytes; + } + + /// Number of digits of the MNC. + size_t nof_digits() const { return (bcd & 0xf00U) == 0xf00U ? 2 : 3; } + + bool operator==(const mobile_network_code& rhs) const { return bcd == rhs.bcd; } + bool operator!=(const mobile_network_code& rhs) const { return bcd != rhs.bcd; } + +private: + // BCD representation of MNC. + uint16_t bcd; +}; + +/// Identifier of a Public Land Mobile Network (PLMN). +class plmn_identity +{ + constexpr plmn_identity(uint32_t bcd_plmn) : data(bcd_plmn) {} + plmn_identity(uint16_t bcd_mcc, uint16_t bcd_mnc) { bcd_helper::ngap_mccmnc_to_plmn(bcd_mcc, bcd_mnc, &data); } + +public: + /// Test value for PLMN identity "00101". + static constexpr plmn_identity test_value() { return plmn_identity{0xf110}; } + + plmn_identity() = delete; + plmn_identity(mobile_country_code mcc, mobile_network_code mnc) : plmn_identity(mcc.to_bcd(), mnc.to_bcd()) {} + + /// Convert PLMN string representation to PLMN instance (e.g. "00101" for test network). + static expected parse(const std::string& plmn_id_str) + { + uint32_t plmn = bcd_helper::plmn_string_to_bcd(plmn_id_str); + if (plmn == 0) { + return make_unexpected(default_error_t{}); + } + return plmn_identity{plmn}; + } + + /// \brief Convert array of bytes representation to PLMN instance. + /// + /// The PLMN identity byte representation consists of 3 digits from MCC followed by either a filler digit plus 2 + /// digits from MNC (2 digit MNC) or 3 digits from MNC (3 digit MNC). + static expected from_bytes(const std::array& bytes) + { + uint16_t mcc = 0xf000U + (static_cast(bytes[0] & 0xfU) << 8U) + (bytes[0] & 0xf0U) + (bytes[1] & 0xfU); + uint16_t mnc = 0xf000U + (static_cast(bytes[1] & 0xf0U) << 4U) + ((bytes[2] & 0xf0U) >> 4U) + + ((bytes[2] & 0xfU) << 4U); + if (not bcd_helper::is_valid_mcc(mcc) or not bcd_helper::is_valid_mnc(mnc)) { + return make_unexpected(default_error_t{}); + } + return plmn_identity{mcc, mnc}; + } + + uint32_t to_bcd() const { return data; } + + std::array to_bytes() const + { + std::array bytes = {}; + uint16_t mcc = bcd_helper::bcd_plmn_to_mcc(data); + uint16_t mnc = bcd_helper::bcd_plmn_to_mnc(data); + bytes[0] = ((mcc & 0xf00U) >> 8U) + (mcc & 0xf0U); + bytes[1] = (mcc & 0xfU) + ((mnc & 0xf00U) >> 4U); + bytes[2] = ((mnc & 0xfU) << 4U) + ((mnc & 0xf0U) >> 4U); + return bytes; + } + + mobile_country_code mcc() const { return mobile_country_code::from_bcd(bcd_helper::bcd_plmn_to_mcc(data)).value(); } + + mobile_network_code mnc() const { return mobile_network_code::from_bcd(bcd_helper::bcd_plmn_to_mnc(data)).value(); } + + /// Convert PLMN to string representation (e.g. "00101" for test network). + std::string to_string() const { return bcd_helper::plmn_bcd_to_string(data); } + + bool operator==(const plmn_identity& rhs) const { return data == rhs.data; } + bool operator!=(const plmn_identity& rhs) const { return data != rhs.data; } + +private: + uint32_t data; +}; + +} // namespace srsran + +namespace fmt { + +template <> +struct formatter { + template + auto parse(ParseContext& ctx) + { + return ctx.begin(); + } + + template + auto format(const srsran::mobile_country_code& val, FormatContext& ctx) + { + return format_to(ctx.out(), "{}", val.to_string()); + } +}; + +template <> +struct formatter { + template + auto parse(ParseContext& ctx) + { + return ctx.begin(); + } + + template + auto format(const srsran::mobile_network_code& val, FormatContext& ctx) + { + return format_to(ctx.out(), "{}", val.to_string()); + } +}; + +template <> +struct formatter { + template + auto parse(ParseContext& ctx) + { + return ctx.begin(); + } + + template + auto format(const srsran::plmn_identity& val, FormatContext& ctx) + { + return format_to(ctx.out(), "{}", val.to_string()); + } +}; + +} // namespace fmt \ No newline at end of file diff --git a/include/srsran/rlc/rlc_tx.h b/include/srsran/rlc/rlc_tx.h index 9e76d4954e..212ca067ea 100644 --- a/include/srsran/rlc/rlc_tx.h +++ b/include/srsran/rlc/rlc_tx.h @@ -53,8 +53,9 @@ namespace srsran { /// can optionally be accompanied with the corresponding PDCP sequence number (SN) /// so that RLC AM can notify the PDCP of ACKs, and PDCP can notify RLC AM/UM to discard PDCP PDUs struct rlc_sdu { - byte_buffer buf = {}; - std::optional pdcp_sn; + byte_buffer buf = {}; ///< 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; rlc_sdu() = default; rlc_sdu(byte_buffer buf_, std::optional pdcp_sn_) : buf(std::move(buf_)), pdcp_sn(pdcp_sn_) {} @@ -75,7 +76,8 @@ class rlc_tx_upper_layer_data_interface /// \brief Interface for higher layers to pass SDUs into RLC /// \param sdu_buf SDU to be handled - virtual void handle_sdu(byte_buffer sdu_buf) = 0; + /// \param is_retx Determines wheter the SDU is a PDCP retransmission or not + virtual void handle_sdu(byte_buffer sdu_buf, bool is_retx) = 0; /// \brief Interface for higher layers to discard SDUs from RLC queue /// \param pdcp_sn PDCP sequence number (SN) of the SDU that is to be discarded @@ -83,11 +85,13 @@ class rlc_tx_upper_layer_data_interface }; /// This interface represents the data upper layer that the TX RLC bearer must notify on transmission and/or delivery of -/// SDUs it can stop its discard timer. +/// (new or ReTx) SDUs so it can stop its discard timer. /// /// The following events shall be notified: -/// - on transmission of an SDU, i.e. pop from SDU queue -/// - on successful delivery of an SDU (only RLC AM) +/// - on transmission of a new SDU, i.e. pop from SDU queue with is_retx == false. +/// - on successful delivery of a new SDU with is_retx == false (only RLC AM). +/// - on transmission of a ReTx SDU, i.e. pop from SDU queue with is_retx == true (only RLC AM). +/// - on successful delivery of a ReTx SDU with is_retx == true (only RLC AM). class rlc_tx_upper_layer_data_notifier { public: @@ -95,13 +99,27 @@ class rlc_tx_upper_layer_data_notifier /// \brief Informs upper layer about the highest PDCP PDU sequence number of the PDCP PDU that was transmitted to the /// lower layers. + /// /// \param max_tx_pdcp_sn Highest transmitted PDCP PDU sequence number. virtual void on_transmitted_sdu(uint32_t max_tx_pdcp_sn) = 0; /// \brief Informs upper layer about the highest PDCP PDU sequence number of the PDCP PDU that was successfully /// delivered in sequence towards the UE. + /// /// \param max_deliv_pdcp_sn Highest in a sequence delivered PDCP PDU sequence number. virtual void on_delivered_sdu(uint32_t max_deliv_pdcp_sn) = 0; + + /// \brief Informs upper layer about the highest PDCP PDU sequence number of the retransmitted PDCP PDU that was + /// transmitted to the lower layers. + /// + /// \param max_retx_pdcp_sn Highest retransmitted PDCP PDU sequence number. + virtual void on_retransmitted_sdu(uint32_t max_retx_pdcp_sn) = 0; + + /// \brief Informs upper layer about the highest PDCP PDU sequence number of the retransmitted PDCP PDU that was + /// successfully delivered in sequence towards the UE. + /// + /// \param max_deliv_retx_pdcp_sn Highest in a sequence delivered PDCP PDU sequence number. + virtual void on_delivered_retransmitted_sdu(uint32_t max_deliv_retx_pdcp_sn) = 0; }; /// This interface represents the control upper layer that the diff --git a/include/srsran/rrc/rrc_du.h b/include/srsran/rrc/rrc_du.h index f8e643d95a..9d388ecb77 100644 --- a/include/srsran/rrc/rrc_du.h +++ b/include/srsran/rrc/rrc_du.h @@ -63,6 +63,9 @@ class rrc_du_ue_repository rrc_du_ue_repository() = default; virtual ~rrc_du_ue_repository() = default; + /// \brief Get the RRC Reject message to send to the UE. + virtual byte_buffer get_rrc_reject() = 0; + /// Creates a new RRC UE object and returns a handle to it. virtual rrc_ue_interface* add_ue(const rrc_ue_creation_message& msg) = 0; @@ -79,7 +82,7 @@ class rrc_du_measurement_config_notifier /// \brief Request to update the measurement related parameters for the given cell id. /// \param[in] nci The cell id of the serving cell to update. /// \param[in] serv_cell_cfg_ The serving cell meas config to update. - virtual bool on_cell_config_update_request(nr_cell_id_t nci, const serving_cell_meas_config& serv_cell_cfg_) = 0; + virtual bool on_cell_config_update_request(nr_cell_identity nci, const serving_cell_meas_config& serv_cell_cfg_) = 0; }; /// Handle RRC UE removal diff --git a/include/srsran/rrc/rrc_ue.h b/include/srsran/rrc/rrc_ue.h index 1252bd5a7b..9fc007f36c 100644 --- a/include/srsran/rrc/rrc_ue.h +++ b/include/srsran/rrc/rrc_ue.h @@ -380,7 +380,7 @@ class rrc_ue_measurement_notifier /// \param[in] nci The cell id of the serving cell to update. /// \param[in] current_meas_config The current meas config of the UE (if applicable). virtual std::optional - on_measurement_config_request(nr_cell_id_t nci, std::optional current_meas_config = {}) = 0; + on_measurement_config_request(nr_cell_identity nci, std::optional current_meas_config = {}) = 0; /// \brief Submit measurement report for given UE to cell manager. virtual void on_measurement_report(const rrc_meas_results& meas_results) = 0; diff --git a/include/srsran/scheduler/config/scheduler_expert_config.h b/include/srsran/scheduler/config/scheduler_expert_config.h index 6964272e31..3228001750 100644 --- a/include/srsran/scheduler/config/scheduler_expert_config.h +++ b/include/srsran/scheduler/config/scheduler_expert_config.h @@ -35,9 +35,22 @@ #include "srsran/ran/sib/sib_configuration.h" #include "srsran/ran/slot_pdu_capacity_constants.h" #include +#include namespace srsran { +/// \brief Proportional fair policy scheduler expert parameters. +struct time_pf_scheduler_expert_config { + /// Fairness Coefficient to use in Proportional Fair policy scheduler. + double pf_sched_fairness_coeff = 2.0F; +}; + +/// \brief Round-Robin policy scheduler expert parameters. +struct time_rr_scheduler_expert_config {}; + +/// \brief Policy scheduler expert parameters. +using policy_scheduler_expert_config = std::variant; + /// \brief UE scheduling statically configurable expert parameters. struct scheduler_ue_expert_config { /// Range of allowed MCS indices for DL UE scheduling. To use a fixed mcs, set the minimum mcs equal to the maximum. @@ -106,6 +119,8 @@ struct scheduler_ue_expert_config { crb_interval pdsch_crb_limits{0, MAX_NOF_PRBS}; /// Boundaries in RB interval for resource allocation of UE PUSCHs. crb_interval pusch_crb_limits{0, MAX_NOF_PRBS}; + /// Expert parameters to be passed to the policy scheduler. + policy_scheduler_expert_config strategy_cfg = time_rr_scheduler_expert_config{}; }; /// \brief System Information scheduling statically configurable expert parameters. diff --git a/include/srsran/srslog/logger.h b/include/srsran/srslog/logger.h index 53eee77523..57fef791ab 100644 --- a/include/srsran/srslog/logger.h +++ b/include/srsran/srslog/logger.h @@ -23,6 +23,7 @@ #pragma once #include "srsran/srslog/log_channel.h" +#include namespace srslog { @@ -160,7 +161,7 @@ struct basic_logger_channels { using basic_logger = build_logger_type; /// Translates a string to the corresponding logger basic level. -inline basic_levels str_to_basic_level(std::string s) +inline std::optional str_to_basic_level(std::string s) { std::transform(s.begin(), s.end(), s.begin(), ::toupper); @@ -179,7 +180,10 @@ inline basic_levels str_to_basic_level(std::string s) if ("DEBUG" == s) { return basic_levels::debug; } - return basic_levels::none; + if ("NONE" == s) { + return basic_levels::none; + } + return {}; } /// Translates a logger basic level to the corresponding string. @@ -187,17 +191,17 @@ inline const char* basic_level_to_string(basic_levels level) { switch (level) { case basic_levels::debug: - return "DEBUG"; + return "debug"; case basic_levels::info: - return "INFO"; + return "info"; case basic_levels::warning: - return "WARNING"; + return "warning"; case basic_levels::error: - return "ERROR"; + return "error"; default: break; } - return "NONE"; + return "none"; } } // namespace srslog diff --git a/include/srsran/srslog/srslog.h b/include/srsran/srslog/srslog.h index 7583c2bfb4..7ce7502a89 100644 --- a/include/srsran/srslog/srslog.h +++ b/include/srsran/srslog/srslog.h @@ -25,6 +25,7 @@ #include "srsran/srslog/detail/support/any.h" #include "srsran/srslog/logger.h" #include "srsran/srslog/shared_types.h" +#include namespace srslog { diff --git a/include/srsran/support/async/protocol_transaction_manager.h b/include/srsran/support/async/protocol_transaction_manager.h index 599969010e..c80dd5d3df 100644 --- a/include/srsran/support/async/protocol_transaction_manager.h +++ b/include/srsran/support/async/protocol_transaction_manager.h @@ -107,7 +107,7 @@ class protocol_transaction bool aborted() const { srsran_assert(valid(), "Trying to check completion of invalid transaction"); - return complete() and ev->get().is_error(); + return complete() and not ev->get().has_value(); } /// \brief Get cause of transaction failure. @@ -262,7 +262,7 @@ class protocol_transaction_manager template SRSRAN_NODISCARD bool set_response(unsigned transaction_id, U&& result) { - static_assert(std::is_convertible::value, "Invalid transaction response type being set"); + static_assert(std::is_convertible_v, "Invalid transaction response type being set"); return set_transaction_outcome(transaction_id, std::forward(result)); } @@ -281,7 +281,7 @@ class protocol_transaction_manager }; template - SRSRAN_NODISCARD bool set_transaction_outcome(unsigned transaction_id, U&& result) + [[nodiscard]] bool set_transaction_outcome(unsigned transaction_id, U&& result) { auto it = running_transactions.find(transaction_id); if (it == running_transactions.end()) { @@ -293,7 +293,11 @@ class protocol_transaction_manager it->second.timer.stop(); // Store result. - it->second.event.set(std::forward(result)); + if constexpr (std::is_convertible_v) { + it->second.event.set(make_unexpected(std::forward(result))); + } else { + it->second.event.set(T{std::forward(result)}); + } return true; } diff --git a/include/srsran/support/cli11_utils.h b/include/srsran/support/cli11_utils.h index fb4e1ab2c2..0352bed76d 100644 --- a/include/srsran/support/cli11_utils.h +++ b/include/srsran/support/cli11_utils.h @@ -105,7 +105,7 @@ CLI::Option* add_option_function(CLI::App& app, { auto* opt = app.get_option_no_throw(option_name); if (!opt) { - return app.add_option_function(option_name, func, desc); + return app.add_option_function(option_name, func, desc)->run_callback_for_default(); } // Option was found. Get the callback and create new option. diff --git a/include/srsran/support/config/validator_helpers.h b/include/srsran/support/config/validator_helpers.h index e75bc6b24d..55f1d53467 100644 --- a/include/srsran/support/config/validator_helpers.h +++ b/include/srsran/support/config/validator_helpers.h @@ -30,14 +30,14 @@ namespace srsran { /// Macro used to check a condition and propagate an error message if the validation fails. #define VERIFY(cond, ...) \ if (not(cond)) { \ - return error_type(fmt::format(__VA_ARGS__)); \ + return make_unexpected(fmt::format(__VA_ARGS__)); \ } /// Macro used to check a validation result and propagate the error in case of failure. #define HANDLE_ERROR(cond) \ { \ auto ret = cond; \ - if (ret.is_error()) { \ + if (not ret.has_value()) { \ return ret; \ } \ } diff --git a/lib/cu_cp/adapters/cell_meas_manager_adapters.h b/lib/cu_cp/adapters/cell_meas_manager_adapters.h index 79db6a7789..041bc10a68 100644 --- a/lib/cu_cp/adapters/cell_meas_manager_adapters.h +++ b/lib/cu_cp/adapters/cell_meas_manager_adapters.h @@ -36,10 +36,10 @@ class cell_meas_mobility_manager_adapter : public cell_meas_mobility_manager_not void connect_mobility_manager(mobility_manager_measurement_handler& handler_) { handler = &handler_; } - void on_neighbor_better_than_spcell(ue_index_t ue_index, - gnb_id_t neighbor_gnb_id, - nr_cell_id_t neighbor_nci, - pci_t neighbor_pci) override + void on_neighbor_better_than_spcell(ue_index_t ue_index, + gnb_id_t neighbor_gnb_id, + nr_cell_identity neighbor_nci, + pci_t neighbor_pci) override { srsran_assert(handler != nullptr, "Mobility manager handler must not be nullptr"); handler->handle_neighbor_better_than_spcell(ue_index, neighbor_gnb_id, neighbor_nci, neighbor_pci); diff --git a/lib/cu_cp/adapters/du_processor_adapters.h b/lib/cu_cp/adapters/du_processor_adapters.h index c91ecb3196..fbe8b3dab0 100644 --- a/lib/cu_cp/adapters/du_processor_adapters.h +++ b/lib/cu_cp/adapters/du_processor_adapters.h @@ -169,6 +169,12 @@ class du_processor_rrc_du_adapter : public du_processor_rrc_du_ue_notifier return rrc_du_cell_handler->handle_served_cell_list(served_cell_list); } + byte_buffer on_rrc_reject_required() override + { + srsran_assert(rrc_du_handler != nullptr, "RRC DU UE handler must not be nullptr"); + return rrc_du_handler->get_rrc_reject(); + } + rrc_ue_interface* on_ue_creation_request(const rrc_ue_creation_message& msg) override { srsran_assert(rrc_du_handler != nullptr, "RRC DU UE handler must not be nullptr"); diff --git a/lib/cu_cp/adapters/f1ap_adapters.h b/lib/cu_cp/adapters/f1ap_adapters.h index e15e3b378f..0ee8d18dbb 100644 --- a/lib/cu_cp/adapters/f1ap_adapters.h +++ b/lib/cu_cp/adapters/f1ap_adapters.h @@ -49,13 +49,7 @@ class f1ap_du_processor_adapter : public f1ap_du_processor_notifier return du_setup_hdlr->handle_du_setup_request(msg); } - ue_index_t on_new_cu_cp_ue_required() override - { - srsran_assert(du_f1ap_handler != nullptr, "F1AP handler must not be nullptr"); - return du_f1ap_handler->allocate_new_ue_index(); - } - - ue_rrc_context_creation_response + ue_rrc_context_creation_outcome on_ue_rrc_context_creation_request(const ue_rrc_context_creation_request& req) override { srsran_assert(du_f1ap_handler != nullptr, "F1AP handler must not be nullptr"); diff --git a/lib/cu_cp/adapters/ngap_adapters.h b/lib/cu_cp/adapters/ngap_adapters.h index bae479a98d..8d93700fd9 100644 --- a/lib/cu_cp/adapters/ngap_adapters.h +++ b/lib/cu_cp/adapters/ngap_adapters.h @@ -44,12 +44,6 @@ class ngap_cu_cp_adapter : public ngap_cu_cp_du_repository_notifier, public ngap cu_cp_handler = &cu_cp_handler_; } - ue_index_t request_new_ue_index_allocation(nr_cell_global_id_t cgi) override - { - srsran_assert(du_repository_handler != nullptr, "CU-CP Paging handler must not be nullptr"); - return du_repository_handler->handle_ue_index_allocation_request(cgi); - } - void on_paging_message(cu_cp_paging_message& msg) override { srsran_assert(du_repository_handler != nullptr, "CU-CP Paging handler must not be nullptr"); @@ -115,6 +109,12 @@ class ngap_cu_cp_adapter : public ngap_cu_cp_du_repository_notifier, public ngap return cu_cp_handler->handle_new_handover_command(ue_index, std::move(command)); } + ue_index_t request_new_ue_index_allocation(nr_cell_global_id_t cgi) override + { + srsran_assert(cu_cp_handler != nullptr, "CU-CP NGAP handler must not be nullptr"); + return cu_cp_handler->handle_ue_index_allocation_request(cgi); + } + void on_n2_disconnection() override { srsran_assert(cu_cp_handler != nullptr, "CU-CP NGAP handler must not be nullptr"); diff --git a/lib/cu_cp/adapters/rrc_du_adapters.h b/lib/cu_cp/adapters/rrc_du_adapters.h index 594fe44608..989288eaa6 100644 --- a/lib/cu_cp/adapters/rrc_du_adapters.h +++ b/lib/cu_cp/adapters/rrc_du_adapters.h @@ -37,7 +37,7 @@ class rrc_du_cu_cp_adapter : public rrc_du_measurement_config_notifier meas_config_handler = &meas_config_handler_; } - bool on_cell_config_update_request(nr_cell_id_t nci, const serving_cell_meas_config& serv_cell_cfg) override + bool on_cell_config_update_request(nr_cell_identity nci, const serving_cell_meas_config& serv_cell_cfg) override { srsran_assert(meas_config_handler != nullptr, "Measurement config handler must not be nullptr"); return meas_config_handler->handle_cell_config_update_request(nci, serv_cell_cfg); diff --git a/lib/cu_cp/adapters/rrc_ue_adapters.h b/lib/cu_cp/adapters/rrc_ue_adapters.h index aefe9e3f84..2462bb5768 100644 --- a/lib/cu_cp/adapters/rrc_ue_adapters.h +++ b/lib/cu_cp/adapters/rrc_ue_adapters.h @@ -256,7 +256,7 @@ class rrc_ue_cu_cp_adapter : public rrc_ue_context_update_notifier, public rrc_u } std::optional - on_measurement_config_request(nr_cell_id_t nci, std::optional current_meas_config = {}) override + on_measurement_config_request(nr_cell_identity nci, std::optional current_meas_config = {}) override { srsran_assert(meas_handler != nullptr, "Measurement handler must not be nullptr"); return meas_handler->handle_measurement_config_request(ue_index, nci, current_meas_config); diff --git a/lib/cu_cp/cell_meas_manager/cell_meas_manager_helpers.cpp b/lib/cu_cp/cell_meas_manager/cell_meas_manager_helpers.cpp index 83e1af0ae5..70e759f327 100644 --- a/lib/cu_cp/cell_meas_manager/cell_meas_manager_helpers.cpp +++ b/lib/cu_cp/cell_meas_manager/cell_meas_manager_helpers.cpp @@ -54,7 +54,7 @@ bool srsran::srs_cu_cp::is_complete(const serving_cell_meas_config& cfg) #ifdef SSB_ARFC_VALIDATOR error_type ret = band_helper::is_dl_arfcn_valid_given_band(cfg.band.value(), cfg.ssb_arfcn.value(), cfg.ssb_scs.value()); - if (ret.is_error()) { + if (not ret.has_value()) { srslog::fetch_basic_logger(LOG_CHAN).error( "Invalid SSB ARFCN={} for band {}. Cause: {}", cfg.ssb_arfcn.value(), cfg.band.value(), ret.error()); return false; @@ -68,7 +68,7 @@ bool srsran::srs_cu_cp::is_valid_configuration( const cell_meas_manager_cfg& cfg, const std::unordered_map& ssb_freq_to_meas_object) { - std::vector ncis; + std::vector ncis; // Verify neighbor cell lists: cell id must not be included in neighbor cell list. for (const auto& cell : cfg.cells) { const auto& nci = cell.first; @@ -152,7 +152,7 @@ void srsran::srs_cu_cp::add_old_meas_config_to_rem_list(const rrc_meas_cfg& old_ } std::vector srsran::srs_cu_cp::generate_measurement_object_list(const cell_meas_manager_cfg& cfg, - nr_cell_id_t serving_nci) + nr_cell_identity serving_nci) { srsran_assert(cfg.cells.find(serving_nci) != cfg.cells.end(), "No cell config for nci={:#x}", serving_nci); @@ -179,7 +179,7 @@ std::vector srsran::srs_cu_cp::generate_measurement_object_list } void srsran::srs_cu_cp::generate_report_config(const cell_meas_manager_cfg& cfg, - const nr_cell_id_t nci, + const nr_cell_identity nci, const report_cfg_id_t report_cfg_id, rrc_meas_cfg& meas_cfg, cell_meas_manager_ue_context& ue_meas_context) diff --git a/lib/cu_cp/cell_meas_manager/cell_meas_manager_helpers.h b/lib/cu_cp/cell_meas_manager/cell_meas_manager_helpers.h index 3fa352ac5b..6c75b063dc 100644 --- a/lib/cu_cp/cell_meas_manager/cell_meas_manager_helpers.h +++ b/lib/cu_cp/cell_meas_manager/cell_meas_manager_helpers.h @@ -47,7 +47,7 @@ void add_old_meas_config_to_rem_list(const rrc_meas_cfg& old_cfg, rrc_meas_cfg& /// \param[in] nci The cell id. /// \returns A vector of SSB frequencies that correlate to measurement objects. std::vector generate_measurement_object_list(const cell_meas_manager_cfg& cfg, - nr_cell_id_t serving_nci); + nr_cell_identity serving_nci); /// \brief Generate report configuration for the given cell configuration. /// \param[in] cfg The cell configuration. @@ -56,7 +56,7 @@ std::vector generate_measurement_object_list(const cell_meas_ma /// \param[out] meas_cfg The resulting measurement configuration. /// \param[out] ue_meas_context The UE measurement context. void generate_report_config(const cell_meas_manager_cfg& cfg, - const nr_cell_id_t nci, + const nr_cell_identity nci, const report_cfg_id_t report_cfg_id, rrc_meas_cfg& meas_cfg, cell_meas_manager_ue_context& ue_meas_context); 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 d4d1397b4d..1c4c38e114 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 @@ -41,7 +41,7 @@ cell_meas_manager::cell_meas_manager(const cell_meas_manager_cfg& cfg_, } std::optional cell_meas_manager::get_measurement_config(ue_index_t ue_index, - nr_cell_id_t serving_nci, + nr_cell_identity serving_nci, std::optional current_meas_config) { std::optional meas_cfg; @@ -139,7 +139,7 @@ std::optional cell_meas_manager::get_measurement_config(ue_index_t return meas_cfg; } -std::optional cell_meas_manager::get_cell_config(nr_cell_id_t nci) +std::optional cell_meas_manager::get_cell_config(nr_cell_identity nci) { std::optional cell_cfg; if (cfg.cells.find(nci) != cfg.cells.end()) { @@ -148,7 +148,7 @@ std::optional cell_meas_manager::get_cell_config(nr_cell_id_t return cell_cfg; } -bool cell_meas_manager::update_cell_config(nr_cell_id_t nci, const serving_cell_meas_config& serv_cell_cfg) +bool cell_meas_manager::update_cell_config(nr_cell_identity nci, const serving_cell_meas_config& serv_cell_cfg) { // Store old config to revert if new config is invalid. cell_meas_manager_cfg tmp_cfg = cfg; @@ -279,7 +279,8 @@ void cell_meas_manager::generate_measurement_objects_for_serving_cells() log_meas_objects(logger, ssb_freq_to_meas_object); } -void cell_meas_manager::update_measurement_object(nr_cell_id_t nci, const serving_cell_meas_config& serving_cell_cfg) +void cell_meas_manager::update_measurement_object(nr_cell_identity nci, + const serving_cell_meas_config& serving_cell_cfg) { srsran_assert(is_complete(serving_cell_cfg), "Incomplete measurement object update for nci={:#x}", nci); @@ -289,7 +290,7 @@ void cell_meas_manager::update_measurement_object(nr_cell_id_t nci, const servin if (ssb_freq_to_ncis.find(ssb_freq) != ssb_freq_to_ncis.end()) { ssb_freq_to_ncis.at(ssb_freq).push_back(nci); } else { - ssb_freq_to_ncis.emplace(ssb_freq, std::vector{nci}); + ssb_freq_to_ncis.emplace(ssb_freq, std::vector{nci}); } if (ssb_freq_to_meas_object.find(ssb_freq) != ssb_freq_to_meas_object.end()) { diff --git a/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.h b/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.h index 017c810240..764efb69a7 100644 --- a/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.h +++ b/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.h @@ -39,10 +39,10 @@ class cell_meas_mobility_manager_notifier virtual ~cell_meas_mobility_manager_notifier() = default; /// \brief Notifies that a neighbor cell became stronger than the current serving cell. - virtual void on_neighbor_better_than_spcell(ue_index_t ue_index, - gnb_id_t neighbor_gnb_id, - nr_cell_id_t neighbor_nci, - pci_t neighbor_pci) = 0; + virtual void on_neighbor_better_than_spcell(ue_index_t ue_index, + gnb_id_t neighbor_gnb_id, + nr_cell_identity neighbor_nci, + pci_t neighbor_pci) = 0; }; /// Basic cell manager implementation @@ -54,17 +54,18 @@ class cell_meas_manager ue_manager& ue_mng_); ~cell_meas_manager() = default; - std::optional - get_measurement_config(ue_index_t ue_index, nr_cell_id_t nci, std::optional current_meas_config = {}); - std::optional get_cell_config(nr_cell_id_t nci); - bool update_cell_config(nr_cell_id_t nci, const serving_cell_meas_config& serv_cell_cfg); - void report_measurement(ue_index_t ue_index, const rrc_meas_results& meas_results); + std::optional get_measurement_config(ue_index_t ue_index, + nr_cell_identity nci, + std::optional current_meas_config = {}); + std::optional get_cell_config(nr_cell_identity nci); + bool update_cell_config(nr_cell_identity nci, const serving_cell_meas_config& serv_cell_cfg); + void report_measurement(ue_index_t ue_index, const rrc_meas_results& meas_results); private: /// \brief Generate measurement objects for the given cell configuration. void generate_measurement_objects_for_serving_cells(); - void update_measurement_object(nr_cell_id_t nci, const serving_cell_meas_config& serving_cell_cfg); + void update_measurement_object(nr_cell_identity nci, const serving_cell_meas_config& serving_cell_cfg); cell_meas_manager_cfg cfg; cell_meas_mobility_manager_notifier& mobility_mng_notifier; @@ -72,7 +73,7 @@ class cell_meas_manager std::unordered_map ssb_freq_to_meas_object; // unique measurement objects, indexed by SSB frequency. - std::unordered_map> ssb_freq_to_ncis; + std::unordered_map> ssb_freq_to_ncis; srslog::basic_logger& logger; }; diff --git a/lib/cu_cp/cell_meas_manager/measurement_context.h b/lib/cu_cp/cell_meas_manager/measurement_context.h index df7cd8f283..a42b699eb3 100644 --- a/lib/cu_cp/cell_meas_manager/measurement_context.h +++ b/lib/cu_cp/cell_meas_manager/measurement_context.h @@ -35,11 +35,11 @@ namespace srsran { namespace srs_cu_cp { struct meas_context_t { - meas_obj_id_t meas_obj_id = meas_obj_id_t::invalid; - report_cfg_id_t report_cfg_id = report_cfg_id_t::invalid; - gnb_id_t gnb_id; - nr_cell_id_t nci; - pci_t pci; + meas_obj_id_t meas_obj_id = meas_obj_id_t::invalid; + report_cfg_id_t report_cfg_id = report_cfg_id_t::invalid; + gnb_id_t gnb_id; + nr_cell_identity nci; + pci_t pci; }; class cell_meas_manager_ue_context @@ -101,9 +101,9 @@ class cell_meas_manager_ue_context slotted_array meas_ids; // 0 is reserved for invalid meas_id slotted_array meas_obj_ids; // 0 is reserved for invalid meas_obj_id - std::map meas_id_to_meas_context; - std::map nci_to_meas_obj_id; - std::map> meas_obj_id_to_ncis; + std::map meas_id_to_meas_context; + std::map nci_to_meas_obj_id; + std::map> meas_obj_id_to_ncis; cell_meas_manager_ue_context() { diff --git a/lib/cu_cp/cu_cp_impl.cpp b/lib/cu_cp/cu_cp_impl.cpp index e0f03129ca..c8c4f9df94 100644 --- a/lib/cu_cp/cu_cp_impl.cpp +++ b/lib/cu_cp/cu_cp_impl.cpp @@ -234,7 +234,7 @@ async_task cu_cp_impl::handle_rrc_reestablishment_context_modification_req ue_index, ue->get_security_manager().get_up_as_config(), cu_up_db.find_cu_up_processor(uint_to_cu_up_index(0))->get_e1ap_bearer_context_manager(), - du_db.get_du_processor(get_du_index_from_ue_index(ue_index)).get_f1ap_interface().get_f1ap_ue_context_manager(), + du_db.get_du_processor(ue->get_du_index()).get_f1ap_interface().get_f1ap_ue_context_manager(), ue->get_rrc_ue_notifier(), ue->get_up_resource_manager()); } @@ -269,9 +269,18 @@ async_task cu_cp_impl::handle_ue_context_transfer(ue_index_t ue_index, ue_ return false; } + auto* old_ue = ue_mng.find_du_ue(old_ue_index); + + if (ue_mng.find_du_ue(ue_index) == nullptr) { + logger.warning("UE index={} got removed", ue_index); + return false; + } + + auto* ue = ue_mng.find_du_ue(ue_index); + // Notify old F1AP UE context to F1AP. - if (get_du_index_from_ue_index(old_ue_index) == get_du_index_from_ue_index(ue_index)) { - const bool result = du_db.get_du_processor(get_du_index_from_ue_index(old_ue_index)) + if (old_ue->get_du_index() == ue->get_du_index()) { + const bool result = du_db.get_du_processor(old_ue->get_du_index()) .get_f1ap_ue_context_notifier() .on_intra_du_reestablishment(ue_index, old_ue_index); if (not result) { @@ -388,9 +397,7 @@ cu_cp_impl::handle_new_pdu_session_resource_setup_request(cu_cp_pdu_session_reso request, ue->get_security_manager().get_up_as_config(), cu_up_db.find_cu_up_processor(uint_to_cu_up_index(0))->get_e1ap_bearer_context_manager(), - du_db.get_du_processor(get_du_index_from_ue_index(request.ue_index)) - .get_f1ap_interface() - .get_f1ap_ue_context_manager(), + du_db.get_du_processor(ue->get_du_index()).get_f1ap_interface().get_f1ap_ue_context_manager(), ue->get_rrc_ue_notifier(), ue->get_up_resource_manager()); } @@ -407,9 +414,7 @@ cu_cp_impl::handle_new_pdu_session_resource_modify_request(const cu_cp_pdu_sessi return routine_mng.start_pdu_session_resource_modification_routine( request, cu_up_db.find_cu_up_processor(uint_to_cu_up_index(0))->get_e1ap_bearer_context_manager(), - du_db.get_du_processor(get_du_index_from_ue_index(request.ue_index)) - .get_f1ap_interface() - .get_f1ap_ue_context_manager(), + du_db.get_du_processor(ue->get_du_index()).get_f1ap_interface().get_f1ap_ue_context_manager(), ue->get_rrc_ue_notifier(), ue->get_up_resource_manager()); } @@ -426,9 +431,7 @@ cu_cp_impl::handle_new_pdu_session_resource_release_command(const cu_cp_pdu_sess return routine_mng.start_pdu_session_resource_release_routine( command, cu_up_db.find_cu_up_processor(uint_to_cu_up_index(0))->get_e1ap_bearer_context_manager(), - du_db.get_du_processor(get_du_index_from_ue_index(command.ue_index)) - .get_f1ap_interface() - .get_f1ap_ue_context_manager(), + du_db.get_du_processor(ue->get_du_index()).get_f1ap_interface().get_f1ap_ue_context_manager(), ngap_entity->get_ngap_control_message_handler(), ue->get_rrc_ue_notifier(), ue->get_task_sched(), @@ -438,6 +441,9 @@ cu_cp_impl::handle_new_pdu_session_resource_release_command(const cu_cp_pdu_sess async_task cu_cp_impl::handle_ue_context_release_command(const cu_cp_ue_context_release_command& command) { + cu_cp_ue* ue = ue_mng.find_du_ue(command.ue_index); + srsran_assert(ue != nullptr, "ue={}: Could not find DU UE", command.ue_index); + e1ap_bearer_context_manager* e1ap_bearer_ctxt_mng = nullptr; if (cu_up_db.find_cu_up_processor(uint_to_cu_up_index(0)) != nullptr) { e1ap_bearer_ctxt_mng = &cu_up_db.find_cu_up_processor(uint_to_cu_up_index(0))->get_e1ap_bearer_context_manager(); @@ -446,15 +452,15 @@ cu_cp_impl::handle_ue_context_release_command(const cu_cp_ue_context_release_com return routine_mng.start_ue_context_release_routine( command, e1ap_bearer_ctxt_mng, - du_db.get_du_processor(get_du_index_from_ue_index(command.ue_index)) - .get_f1ap_interface() - .get_f1ap_ue_context_manager(), + du_db.get_du_processor(ue->get_du_index()).get_f1ap_interface().get_f1ap_ue_context_manager(), get_cu_cp_ue_removal_handler()); } async_task cu_cp_impl::handle_ngap_handover_request(const ngap_handover_request& request) { + cu_cp_ue* ue = ue_mng.find_du_ue(request.ue_index); + srsran_assert(ue != nullptr, "ue={}: Could not find DU UE", request.ue_index); srsran_assert(cu_up_db.find_cu_up_processor(uint_to_cu_up_index(0)) != nullptr, "cu_up_index={}: could not find CU-UP", uint_to_cu_up_index(0)); @@ -462,9 +468,7 @@ cu_cp_impl::handle_ngap_handover_request(const ngap_handover_request& request) return routine_mng.start_inter_cu_handover_target_routine( request, cu_up_db.find_cu_up_processor(uint_to_cu_up_index(0))->get_e1ap_bearer_context_manager(), - du_db.get_du_processor(get_du_index_from_ue_index(request.ue_index)) - .get_f1ap_interface() - .get_f1ap_ue_context_manager(), + du_db.get_du_processor(ue->get_du_index()).get_f1ap_interface().get_f1ap_ue_context_manager(), get_cu_cp_ue_removal_handler()); } @@ -496,7 +500,7 @@ async_task cu_cp_impl::handle_new_handover_command(ue_index_t ue_index, by ue_context_mod_request.rrc_container = ho_reconfig_pdu.copy(); CORO_AWAIT_VALUE(ue_context_mod_response, - du_db.get_du_processor(get_du_index_from_ue_index(ue_index)) + du_db.get_du_processor(ue_mng.find_du_ue(ue_index)->get_du_index()) .get_f1ap_interface() .get_f1ap_ue_context_manager() .handle_ue_context_modification_request(ue_context_mod_request)); @@ -505,6 +509,16 @@ async_task cu_cp_impl::handle_new_handover_command(ue_index_t ue_index, by }); } +ue_index_t cu_cp_impl::handle_ue_index_allocation_request(const nr_cell_global_id_t& cgi) +{ + du_index_t du_index = du_db.find_du(cgi); + if (du_index == du_index_t::invalid) { + logger.warning("Could not find DU for CGI={}", cgi.nci); + return ue_index_t::invalid; + } + return ue_mng.add_ue(du_index); +} + void cu_cp_impl::handle_n2_disconnection() { // TODO @@ -512,7 +526,7 @@ void cu_cp_impl::handle_n2_disconnection() std::optional cu_cp_impl::handle_measurement_config_request(ue_index_t ue_index, - nr_cell_id_t nci, + nr_cell_identity nci, std::optional current_meas_config) { return cell_meas_mng.get_measurement_config(ue_index, nci, current_meas_config); @@ -523,7 +537,7 @@ void cu_cp_impl::handle_measurement_report(const ue_index_t ue_index, const rrc_ cell_meas_mng.report_measurement(ue_index, meas_results); } -bool cu_cp_impl::handle_cell_config_update_request(nr_cell_id_t nci, const serving_cell_meas_config& serv_cell_cfg) +bool cu_cp_impl::handle_cell_config_update_request(nr_cell_identity nci, const serving_cell_meas_config& serv_cell_cfg) { return cell_meas_mng.update_cell_config(nci, serv_cell_cfg); } @@ -551,7 +565,15 @@ cu_cp_impl::handle_inter_du_handover_request(const cu_cp_inter_du_handover_reque async_task cu_cp_impl::handle_ue_removal_request(ue_index_t ue_index) { - du_index_t du_index = get_du_index_from_ue_index(ue_index); + if (ue_mng.find_du_ue(ue_index) == nullptr) { + logger.warning("ue={}: Could not find DU UE", ue_index); + return launch_async([](coro_context>& ctx) { + CORO_BEGIN(ctx); + CORO_RETURN(); + }); + } + + du_index_t du_index = ue_mng.find_du_ue(ue_index)->get_du_index(); cu_up_index_t cu_up_index = uint_to_cu_up_index(0); // TODO: Update when mapping from UE index to CU-UP exists e1ap_bearer_context_removal_handler* e1ap_removal_handler = nullptr; @@ -571,7 +593,8 @@ async_task cu_cp_impl::handle_ue_removal_request(ue_index_t ue_index) void cu_cp_impl::handle_pending_ue_task_cancellation(ue_index_t ue_index) { - du_index_t du_index = get_du_index_from_ue_index(ue_index); + srsran_assert(ue_mng.find_du_ue(ue_index) != nullptr, "ue={}: Could not find DU UE", ue_index); + du_index_t du_index = ue_mng.find_du_ue(ue_index)->get_du_index(); // Clear all enqueued tasks for this UE. ue_mng.get_task_sched().clear_pending_tasks(ue_index); diff --git a/lib/cu_cp/cu_cp_impl.h b/lib/cu_cp/cu_cp_impl.h index c2e17433c8..2f2afccb59 100644 --- a/lib/cu_cp/cu_cp_impl.h +++ b/lib/cu_cp/cu_cp_impl.h @@ -93,17 +93,18 @@ class cu_cp_impl final : public cu_cp, async_task handle_ngap_handover_request(const ngap_handover_request& request) override; async_task handle_new_handover_command(ue_index_t ue_index, byte_buffer command) override; + ue_index_t handle_ue_index_allocation_request(const nr_cell_global_id_t& cgi) override; void handle_n2_disconnection() override; // cu_cp_measurement_handler std::optional handle_measurement_config_request(ue_index_t ue_index, - nr_cell_id_t nci, + nr_cell_identity nci, std::optional current_meas_config = {}) override; void handle_measurement_report(const ue_index_t ue_index, const rrc_meas_results& meas_results) override; // cu_cp_measurement_config_handler - bool handle_cell_config_update_request(nr_cell_id_t nci, const serving_cell_meas_config& serv_cell_cfg) override; + bool handle_cell_config_update_request(nr_cell_identity nci, const serving_cell_meas_config& serv_cell_cfg) override; // cu_cp_mobility_manager_handler async_task diff --git a/lib/cu_cp/cu_cp_impl_interface.h b/lib/cu_cp/cu_cp_impl_interface.h index 565ac066f4..495044e1fd 100644 --- a/lib/cu_cp/cu_cp_impl_interface.h +++ b/lib/cu_cp/cu_cp_impl_interface.h @@ -105,6 +105,9 @@ class cu_cp_ngap_handler : public cu_cp_ue_context_release_handler, public cu_cp /// \returns True if the Handover Command was successfully handled, false otherwise. virtual async_task handle_new_handover_command(ue_index_t ue_index, byte_buffer command) = 0; + /// \brief Handles UE index allocation request for N2 handover at target gNB + virtual ue_index_t handle_ue_index_allocation_request(const nr_cell_global_id_t& cgi) = 0; + /// \brief Handle N2 AMF connection drop. virtual void handle_n2_disconnection() = 0; }; @@ -269,7 +272,7 @@ class cu_cp_measurement_handler /// \param[in] current_meas_config The current meas config of the UE (if applicable). virtual std::optional handle_measurement_config_request(ue_index_t ue_index, - nr_cell_id_t nci, + nr_cell_identity nci, std::optional current_meas_config = {}) = 0; /// \brief Handle a measurement report for given UE. @@ -285,7 +288,8 @@ class cu_cp_measurement_config_handler /// \brief Handle a request to update the measurement related parameters for the given cell id. /// \param[in] nci The cell id of the serving cell to update. /// \param[in] serv_cell_cfg_ The serving cell meas config to update. - virtual bool handle_cell_config_update_request(nr_cell_id_t nci, const serving_cell_meas_config& serv_cell_cfg) = 0; + virtual bool handle_cell_config_update_request(nr_cell_identity nci, + const serving_cell_meas_config& serv_cell_cfg) = 0; }; /// Interface to request handover. diff --git a/lib/cu_cp/cu_up_processor/cu_up_processor_repository.cpp b/lib/cu_cp/cu_up_processor/cu_up_processor_repository.cpp index d4518da3d3..33d73a1a21 100644 --- a/lib/cu_cp/cu_up_processor/cu_up_processor_repository.cpp +++ b/lib/cu_cp/cu_up_processor/cu_up_processor_repository.cpp @@ -106,7 +106,7 @@ cu_up_index_t cu_up_processor_repository::add_cu_up(std::unique_ptr cu_up = create_cu_up_processor(std::move(cu_up_cfg), *cu_up_ctxt.e1ap_tx_pdu_notifier, diff --git a/lib/cu_cp/du_processor/du_configuration_manager.cpp b/lib/cu_cp/du_processor/du_configuration_manager.cpp index 08254224ec..9446020621 100644 --- a/lib/cu_cp/du_processor/du_configuration_manager.cpp +++ b/lib/cu_cp/du_processor/du_configuration_manager.cpp @@ -21,7 +21,6 @@ */ #include "du_configuration_manager.h" -#include "srsran/ran/nr_cgi_helpers.h" #include "srsran/rrc/rrc_config.h" using namespace srsran; @@ -29,21 +28,14 @@ using namespace srs_cu_cp; static error_type validate_cell_config(const cu_cp_du_served_cells_item& served_cell) { - const auto& cell_info = served_cell.served_cell_info; - - if (not config_helpers::is_valid(cell_info.nr_cgi)) { - return error_type{ - du_setup_result::rejected{cause_protocol_t::semantic_error, "Invalid NR CGI"}}; - } - if (not served_cell.served_cell_info.five_gs_tac.has_value()) { - return error_type{du_setup_result::rejected{ - cause_protocol_t::msg_not_compatible_with_receiver_state, fmt::format("Missing TAC for cell")}}; + return make_unexpected(du_setup_result::rejected{cause_protocol_t::msg_not_compatible_with_receiver_state, + fmt::format("Missing TAC for cell")}); } if (not served_cell.gnb_du_sys_info.has_value()) { - return error_type{du_setup_result::rejected{ - cause_protocol_t::semantic_error, fmt::format("Missing system information for cell")}}; + return make_unexpected(du_setup_result::rejected{cause_protocol_t::semantic_error, + fmt::format("Missing system information for cell")}); } return {}; @@ -63,28 +55,28 @@ class du_configuration_manager::du_configuration_handler_impl : public du_config validation_result handle_new_du_config(const du_setup_request& req) override { if (this->ctxt != nullptr) { - return error_type{ - du_setup_result::rejected{cause_protocol_t::msg_not_compatible_with_receiver_state, "DU already configured"}}; + return make_unexpected( + du_setup_result::rejected{cause_protocol_t::msg_not_compatible_with_receiver_state, "DU already configured"}); } auto ret = parent.add_du_config(req); if (ret.has_value()) { this->ctxt = ret.value(); return {}; } - return ret.error(); + return make_unexpected(ret.error()); } validation_result handle_du_config_update(const du_config_update_request& req) override { if (this->ctxt == nullptr) { - return error_type{du_setup_result::rejected{ - cause_protocol_t::msg_not_compatible_with_receiver_state, "DU with same gNB-DU-Id was not setup"}}; + return make_unexpected(du_setup_result::rejected{cause_protocol_t::msg_not_compatible_with_receiver_state, + "DU with same gNB-DU-Id was not setup"}); } // Reconfiguration. auto ret = parent.handle_du_config_update(*this->ctxt, req); - if (ret.is_error()) { - return ret.error(); + if (not ret.has_value()) { + return make_unexpected(ret.error()); } this->ctxt = ret.value(); return {}; @@ -134,8 +126,8 @@ du_configuration_manager::add_du_config(const du_setup_request& req) { // Validate config. auto result = validate_new_du_config(req); - if (result.is_error()) { - return result.error(); + if (not result.has_value()) { + return make_unexpected(result.error()); } // Create new DU config context. @@ -221,14 +213,14 @@ du_configuration_manager::validate_new_du_config(const du_setup_request& req) co // Ensure the DU config does not collide with other DUs. for (const auto& [du_id, du_cfg] : dus) { if (du_cfg.id == req.gnb_du_id) { - return error_type{ - du_setup_result::rejected{cause_protocol_t::msg_not_compatible_with_receiver_state, "Duplicate DU ID"}}; + return make_unexpected( + du_setup_result::rejected{cause_protocol_t::msg_not_compatible_with_receiver_state, "Duplicate DU ID"}); } } if (req.gnb_du_served_cells_list.size() > MAX_NOF_DU_CELLS) { - return error_type{ - du_setup_result::rejected{cause_protocol_t::msg_not_compatible_with_receiver_state, "Too many served cells"}}; + return make_unexpected( + du_setup_result::rejected{cause_protocol_t::msg_not_compatible_with_receiver_state, "Too many served cells"}); } // Validate served cell configurations. @@ -253,17 +245,17 @@ error_type du_configuration_manager::validate_cell_config_request(const cu_cp_du_served_cells_item& cell_req) const { auto ret = validate_cell_config(cell_req); - if (ret.is_error()) { - return ret.error(); + if (not ret.has_value()) { + return make_unexpected(ret.error()); } // Ensure NCIs match the gNB-Id. - if (not rrc_cfg.gnb_id.contains_nci(cell_req.served_cell_info.nr_cgi.nci)) { - return error_type{ + if (cell_req.served_cell_info.nr_cgi.nci.gnb_id(rrc_cfg.gnb_id.bit_length) != rrc_cfg.gnb_id) { + return make_unexpected( du_setup_result::rejected{cause_protocol_t::msg_not_compatible_with_receiver_state, fmt::format("NCI {:#x} of the served Cell does not match gNB-Id {:#x}", cell_req.served_cell_info.nr_cgi.nci, - rrc_cfg.gnb_id.id)}}; + rrc_cfg.gnb_id.id)}); } return {}; diff --git a/lib/cu_cp/du_processor/du_processor.h b/lib/cu_cp/du_processor/du_processor.h index 72ba458eff..7622b03d41 100644 --- a/lib/cu_cp/du_processor/du_processor.h +++ b/lib/cu_cp/du_processor/du_processor.h @@ -45,15 +45,12 @@ class du_processor_f1ap_interface /// \return The DU index. virtual du_index_t get_du_index() = 0; - /// \brief Allocate a new UE index. - virtual ue_index_t allocate_new_ue_index() = 0; - /// \brief Request to create a new UE RRC context. /// /// This method should be called when a C-RNTI and PCell are assigned to a UE. /// \param req Request to setup a new UE RRC context. /// \return Response to whether the request was successful or failed. - virtual ue_rrc_context_creation_response + virtual ue_rrc_context_creation_outcome handle_ue_rrc_context_creation_request(const ue_rrc_context_creation_request& req) = 0; /// \brief Handle the reception of a F1AP UE Context Release Request and notify NGAP. @@ -143,6 +140,10 @@ class du_processor_rrc_du_ue_notifier /// \return Returns true on success, false otherwise. virtual bool on_new_served_cell_list(const std::vector& served_cell_list) = 0; + /// \brief Notify RRC DU about a required RRCReject. + /// \return Returns a RRC Container containing the RRCReject. + virtual byte_buffer on_rrc_reject_required() = 0; + /// \brief Notify RRC DU to create a UE. /// \param[in] msg The UE creation message. /// \return Returns a handle to the created UE. @@ -230,16 +231,6 @@ class du_processor_mobility_handler virtual byte_buffer get_packed_sib1(nr_cell_global_id_t cgi) = 0; }; -/// Handler for an NGAP entity to communicate with the DU processor -class du_processor_ngap_interface -{ -public: - virtual ~du_processor_ngap_interface() = default; - - /// \brief Allocate a new UE index. - virtual ue_index_t allocate_new_ue_index() = 0; -}; - /// Interface to notify the F1AP about control messages. class du_processor_f1ap_control_notifier { @@ -311,9 +302,6 @@ class du_repository_ngap_handler /// \brief Handles a Paging message notification. virtual void handle_paging_message(cu_cp_paging_message& msg) = 0; - - /// \brief Handles UE index allocation request for N2 handover at target gNB - virtual ue_index_t handle_ue_index_allocation_request(const nr_cell_global_id_t& cgi) = 0; }; /// Methods to get statistics of the DU processor. @@ -333,7 +321,6 @@ class du_processor : public du_processor_cell_info_interface virtual ~du_processor() = default; virtual du_processor_f1ap_interface& get_f1ap_interface() = 0; - virtual du_processor_ngap_interface& get_ngap_interface() = 0; virtual du_processor_paging_handler& get_paging_handler() = 0; virtual du_processor_statistics_handler& get_statistics_handler() = 0; virtual du_processor_mobility_handler& get_mobility_handler() = 0; diff --git a/lib/cu_cp/du_processor/du_processor_factory.cpp b/lib/cu_cp/du_processor/du_processor_factory.cpp index 32fa811d1d..4c966cc3eb 100644 --- a/lib/cu_cp/du_processor/du_processor_factory.cpp +++ b/lib/cu_cp/du_processor/du_processor_factory.cpp @@ -32,7 +32,7 @@ using namespace srs_cu_cp; std::unique_ptr srsran::srs_cu_cp::create_du_processor(du_processor_config_t du_processor_config, du_processor_cu_cp_notifier& cu_cp_notifier_, - f1ap_message_notifier& f1ap_notifier_, + f1ap_message_notifier& f1ap_pdu_notifier_, rrc_ue_nas_notifier& rrc_ue_nas_pdu_notifier_, rrc_ue_control_notifier& rrc_ue_ngap_ctrl_notifier_, rrc_du_measurement_config_notifier& rrc_du_cu_cp_notifier, @@ -43,7 +43,7 @@ srsran::srs_cu_cp::create_du_processor(du_processor_config_t du_pr { auto du_processor = std::make_unique(std::move(du_processor_config), cu_cp_notifier_, - f1ap_notifier_, + f1ap_pdu_notifier_, rrc_ue_nas_pdu_notifier_, rrc_ue_ngap_ctrl_notifier_, rrc_du_cu_cp_notifier, diff --git a/lib/cu_cp/du_processor/du_processor_factory.h b/lib/cu_cp/du_processor/du_processor_factory.h index 11f5afeca6..2ac7efdab7 100644 --- a/lib/cu_cp/du_processor/du_processor_factory.h +++ b/lib/cu_cp/du_processor/du_processor_factory.h @@ -38,7 +38,7 @@ class common_task_scheduler; /// Creates an instance of an DU processor interface std::unique_ptr create_du_processor(du_processor_config_t du_processor_config_, du_processor_cu_cp_notifier& cu_cp_notifier_, - f1ap_message_notifier& f1ap_notifier_, + f1ap_message_notifier& f1ap_pdu_notifier_, rrc_ue_nas_notifier& rrc_ue_nas_pdu_notifier_, rrc_ue_control_notifier& rrc_ue_ngap_ctrl_notifier_, rrc_du_measurement_config_notifier& rrc_du_cu_cp_notifier, diff --git a/lib/cu_cp/du_processor/du_processor_impl.cpp b/lib/cu_cp/du_processor/du_processor_impl.cpp index eee2a6c5f3..792f84a145 100644 --- a/lib/cu_cp/du_processor/du_processor_impl.cpp +++ b/lib/cu_cp/du_processor/du_processor_impl.cpp @@ -25,7 +25,6 @@ #include "srsran/f1ap/cu_cp/f1ap_cu_factory.h" #include "srsran/ran/cause/f1ap_cause_converters.h" #include "srsran/ran/cause/ngap_cause.h" -#include "srsran/ran/nr_cgi_helpers.h" #include "srsran/rrc/rrc_du_factory.h" #include "srsran/support/async/coroutine.h" @@ -82,7 +81,7 @@ du_setup_result du_processor_impl::handle_du_setup_request(const du_setup_reques // Validate and update DU configuration. auto cfg_res = cfg.du_cfg_hdlr->handle_new_du_config(request); - if (cfg_res.is_error()) { + if (not cfg_res.has_value()) { res.result = cfg_res.error(); return res; } @@ -123,11 +122,6 @@ du_setup_result du_processor_impl::handle_du_setup_request(const du_setup_reques return res; } -ue_index_t du_processor_impl::allocate_new_ue_index() -{ - return ue_mng.add_ue(cfg.du_index); -} - bool du_processor_impl::create_rrc_ue(cu_cp_ue& ue, rnti_t c_rnti, const nr_cell_global_id_t& cgi, @@ -174,49 +168,59 @@ bool du_processor_impl::create_rrc_ue(cu_cp_ue& ue, return true; } -ue_rrc_context_creation_response +ue_rrc_context_creation_outcome du_processor_impl::handle_ue_rrc_context_creation_request(const ue_rrc_context_creation_request& req) { - srsran_assert(req.ue_index != ue_index_t::invalid, "Invalid UE index"); - srsran_assert(req.c_rnti != rnti_t::INVALID_RNTI, "ue={}: Invalid C-RNTI", req.ue_index); - srsran_assert(config_helpers::is_valid(req.cgi), "ue={}: Invalid CGI", req.ue_index); + srsran_assert(req.c_rnti != rnti_t::INVALID_RNTI, "ue={} c-rnti={}: Invalid C-RNTI", req.ue_index, req.c_rnti); // Check that creation message is valid const du_cell_configuration* pcell = cfg.du_cfg_hdlr->get_context().find_cell(req.cgi); if (pcell == nullptr) { - srsran_assert(ue_mng.find_ue_task_scheduler(req.ue_index) != nullptr, "ue={}: Could not find UE", req.ue_index); - logger.warning("ue={}: Could not find cell with NCI={}", req.ue_index, req.cgi.nci); - ue_mng.find_ue_task_scheduler(req.ue_index) - ->schedule_async_task(cu_cp_notifier.on_ue_removal_required(req.ue_index)); - return {}; + logger.warning("ue={} c-rnti={}: Could not find cell with NCI={}", req.ue_index, req.c_rnti, req.cgi.nci); + // Return the RRCReject container + return make_unexpected(rrc_du_adapter.on_rrc_reject_required()); } const pci_t pci = pcell->pci; - // Create new UE RRC context - cu_cp_ue* ue = ue_mng.set_ue_du_context(req.ue_index, cfg.du_cfg_hdlr->get_context().id, pci, req.c_rnti); - if (ue == nullptr) { - logger.warning("ue={}: Could not create UE context", req.ue_index); - return {}; + ue_index_t ue_index = req.ue_index; + cu_cp_ue* ue = nullptr; + + if (ue_index == ue_index_t::invalid) { + // Add new CU-CP UE + ue_index = ue_mng.add_ue(cfg.du_index, cfg.du_cfg_hdlr->get_context().id, pci, req.c_rnti, pcell->cell_index); + if (ue_index == ue_index_t::invalid) { + logger.warning("CU-CP UE creation failed"); + return make_unexpected(rrc_du_adapter.on_rrc_reject_required()); + } + ue = ue_mng.find_ue(ue_index); + + /// NOTE: From this point on the UE exists in the UE manager and must be removed if any error occurs. + + } else { + ue = ue_mng.set_ue_du_context(ue_index, cfg.du_cfg_hdlr->get_context().id, pci, req.c_rnti, pcell->cell_index); + if (ue == nullptr) { + logger.warning("ue={}: Could not create UE context", ue_index); + // A UE with the same PCI and RNTI already exists, so we don't remove it and only reject the new UE. + return make_unexpected(rrc_du_adapter.on_rrc_reject_required()); + } } - ue->set_pcell_index(pcell->cell_index); // Create RRC UE. If the DU-to-CU-RRC-Container is empty, the UE will be rejected. if (not create_rrc_ue(*ue, req.c_rnti, req.cgi, req.du_to_cu_rrc_container.copy(), std::move(req.prev_context))) { - logger.warning("ue={}: Could not create RRC UE object", req.ue_index); - return {}; + logger.warning("ue={}: Could not create RRC UE object", ue_index); + // Remove the UE from the UE manager + ue_mng.remove_ue(ue_index); + // Return the RRCReject container + return make_unexpected(rrc_du_adapter.on_rrc_reject_required()); } - rrc_ue_interface* rrc_ue = rrc->find_ue(req.ue_index); - f1ap_rrc_ue_adapters[req.ue_index] = {}; - f1ap_rrc_ue_adapters.at(req.ue_index) - .connect_rrc_ue(rrc_ue->get_ul_ccch_pdu_handler(), rrc_ue->get_ul_dcch_pdu_handler()); - - // Signal back to F1AP that the UE was successfully created. - ue_rrc_context_creation_response response; - response.f1ap_rrc_notifier = &f1ap_rrc_ue_adapters.at(req.ue_index); + rrc_ue_interface* rrc_ue = rrc->find_ue(ue_index); + f1ap_rrc_ue_adapters[ue_index] = {}; + f1ap_rrc_ue_adapters.at(ue_index).connect_rrc_ue(rrc_ue->get_ul_ccch_pdu_handler(), + rrc_ue->get_ul_dcch_pdu_handler()); + // Signal back that the UE was successfully created. logger.info("ue={} c-rnti={}: UE created", ue->get_ue_index(), req.c_rnti); - - return response; + return ue_rrc_context_creation_response{ue_index, &f1ap_rrc_ue_adapters.at(ue_index)}; } void du_processor_impl::handle_du_initiated_ue_context_release_request(const f1ap_ue_context_release_request& request) diff --git a/lib/cu_cp/du_processor/du_processor_impl.h b/lib/cu_cp/du_processor/du_processor_impl.h index 000cdec66a..ae20f3f71c 100644 --- a/lib/cu_cp/du_processor/du_processor_impl.h +++ b/lib/cu_cp/du_processor/du_processor_impl.h @@ -43,7 +43,6 @@ class du_processor_impl : public du_processor, public du_setup_handler, public du_metrics_handler, public du_processor_f1ap_interface, - public du_processor_ngap_interface, public du_processor_paging_handler, public du_processor_statistics_handler, public du_processor_mobility_handler @@ -51,7 +50,7 @@ class du_processor_impl : public du_processor, public: du_processor_impl(du_processor_config_t du_processor_config_, du_processor_cu_cp_notifier& cu_cp_notifier_, - f1ap_message_notifier& f1ap_notifier_, + f1ap_message_notifier& f1ap_pdu_notifier_, rrc_ue_nas_notifier& rrc_ue_nas_pdu_notifier_, rrc_ue_control_notifier& rrc_ue_ngap_ctrl_notifier_, rrc_du_measurement_config_notifier& rrc_du_cu_cp_notifier, @@ -73,8 +72,7 @@ class du_processor_impl : public du_processor, // du_processor_f1ap_interface du_setup_result handle_du_setup_request(const du_setup_request& req) override; - ue_index_t allocate_new_ue_index() override; - ue_rrc_context_creation_response + ue_rrc_context_creation_outcome handle_ue_rrc_context_creation_request(const ue_rrc_context_creation_request& req) override; void handle_du_initiated_ue_context_release_request(const f1ap_ue_context_release_request& request) override; async_task handle_ue_transaction_info_loss(const f1_ue_transaction_info_loss_event& request) override; @@ -93,7 +91,6 @@ class du_processor_impl : public du_processor, metrics_report::du_info handle_du_metrics_report_request() const override; du_processor_f1ap_interface& get_f1ap_interface() override { return *this; } - du_processor_ngap_interface& get_ngap_interface() override { return *this; } du_processor_paging_handler& get_paging_handler() override { return *this; } du_processor_statistics_handler& get_statistics_handler() override { return *this; } du_processor_mobility_handler& get_mobility_handler() override { return *this; } diff --git a/lib/cu_cp/du_processor/du_processor_repository.cpp b/lib/cu_cp/du_processor/du_processor_repository.cpp index eeb708a56f..1a730a09cd 100644 --- a/lib/cu_cp/du_processor/du_processor_repository.cpp +++ b/lib/cu_cp/du_processor/du_processor_repository.cpp @@ -126,8 +126,21 @@ du_index_t du_processor_repository::find_du(pci_t pci) { du_index_t index = du_index_t::invalid; for (const auto& du : du_db) { - if (du.second.processor->has_cell(pci)) + if (du.second.processor->has_cell(pci)) { return du.first; + } + } + + return index; +} + +du_index_t du_processor_repository::find_du(const nr_cell_global_id_t& cgi) +{ + du_index_t index = du_index_t::invalid; + for (const auto& du : du_db) { + if (du.second.processor->has_cell(cgi)) { + return du.first; + } } return index; @@ -148,17 +161,6 @@ void du_processor_repository::handle_paging_message(cu_cp_paging_message& msg) } } -ue_index_t du_processor_repository::handle_ue_index_allocation_request(const nr_cell_global_id_t& cgi) -{ - for (auto& du : du_db) { - if (du.second.processor->has_cell(cgi)) { - return du.second.processor->get_ngap_interface().allocate_new_ue_index(); - } - } - logger.debug("No DU with plmn={} and cell_id={} found.", cgi.plmn, cgi.nci); - return ue_index_t::invalid; -} - std::vector du_processor_repository::handle_du_metrics_report_request() const { std::vector du_reports; diff --git a/lib/cu_cp/du_processor/du_processor_repository.h b/lib/cu_cp/du_processor/du_processor_repository.h index 1604c08e01..ad0e35fabb 100644 --- a/lib/cu_cp/du_processor/du_processor_repository.h +++ b/lib/cu_cp/du_processor/du_processor_repository.h @@ -60,15 +60,19 @@ class du_processor_repository : public du_repository_ngap_handler, public du_rep explicit du_processor_repository(du_repository_config cfg_); /// \brief Checks whether a cell with the specified PCI is served by any of the connected DUs. - /// \param[out] The index of the DU serving the given PCI. + /// \param[in] pci The serving cell PCI. + /// \return The index of the DU serving the given PCI. du_index_t find_du(pci_t pci); + /// \brief Checks whether a cell with the specified CGI is served by any of the connected DUs. + /// \param[in] cgi The serving cell CGI. + /// \return The index of the DU serving the given CGI. + du_index_t find_du(const nr_cell_global_id_t& cgi); + du_processor& get_du_processor(du_index_t du_index); void handle_paging_message(cu_cp_paging_message& msg) override; - ue_index_t handle_ue_index_allocation_request(const nr_cell_global_id_t& nci) override; - std::vector handle_du_metrics_report_request() const override; size_t get_nof_f1ap_ues(); diff --git a/lib/cu_cp/mobility_manager/mobility_manager_impl.cpp b/lib/cu_cp/mobility_manager/mobility_manager_impl.cpp index 635d045660..9c3e41e82e 100644 --- a/lib/cu_cp/mobility_manager/mobility_manager_impl.cpp +++ b/lib/cu_cp/mobility_manager/mobility_manager_impl.cpp @@ -48,13 +48,13 @@ void mobility_manager::trigger_handover(pci_t source_pci, rnti_t rnti, pci_t tar logger.warning("Could not trigger handover, UE is invalid. rnti={} pci={}", rnti, source_pci); return; } - handle_handover(ue_index, gnb_id_t{}, nr_cell_id_t{}, target_pci); // TODO: define gNB-ID and NCI + handle_handover(ue_index, gnb_id_t{}, nr_cell_identity{}, target_pci); // TODO: define gNB-ID and NCI } -void mobility_manager::handle_neighbor_better_than_spcell(ue_index_t ue_index, - gnb_id_t neighbor_gnb_id, - nr_cell_id_t neighbor_nci, - pci_t neighbor_pci) +void mobility_manager::handle_neighbor_better_than_spcell(ue_index_t ue_index, + gnb_id_t neighbor_gnb_id, + nr_cell_identity neighbor_nci, + pci_t neighbor_pci) { if (!cfg.trigger_handover_from_measurements) { logger.debug("ue={}: Ignoring better neighbor pci={}", ue_index, neighbor_pci); @@ -63,10 +63,10 @@ void mobility_manager::handle_neighbor_better_than_spcell(ue_index_t ue_index, handle_handover(ue_index, neighbor_gnb_id, neighbor_nci, neighbor_pci); } -void mobility_manager::handle_handover(ue_index_t ue_index, - gnb_id_t neighbor_gnb_id, - nr_cell_id_t neighbor_nci, - pci_t neighbor_pci) +void mobility_manager::handle_handover(ue_index_t ue_index, + gnb_id_t neighbor_gnb_id, + nr_cell_identity neighbor_nci, + pci_t neighbor_pci) { // Find the UE context. cu_cp_ue* u = ue_mng.find_du_ue(ue_index); @@ -147,9 +147,9 @@ void mobility_manager::handle_intra_du_handover(ue_index_t source_ue_index, pci_ // TODO: prepare call } -void mobility_manager::handle_inter_cu_handover(ue_index_t source_ue_index, - gnb_id_t target_gnb_id, - nr_cell_id_t target_nci) +void mobility_manager::handle_inter_cu_handover(ue_index_t source_ue_index, + gnb_id_t target_gnb_id, + nr_cell_identity target_nci) { cu_cp_ue* u = ue_mng.find_du_ue(source_ue_index); if (u == nullptr) { diff --git a/lib/cu_cp/mobility_manager/mobility_manager_impl.h b/lib/cu_cp/mobility_manager/mobility_manager_impl.h index 291fd4c190..594148f0e2 100644 --- a/lib/cu_cp/mobility_manager/mobility_manager_impl.h +++ b/lib/cu_cp/mobility_manager/mobility_manager_impl.h @@ -40,10 +40,10 @@ class mobility_manager_measurement_handler virtual ~mobility_manager_measurement_handler() = default; /// \brief Handle event where neighbor became better than serving cell. - virtual void handle_neighbor_better_than_spcell(ue_index_t ue_index, - gnb_id_t neighbor_gnb_id, - nr_cell_id_t neighbor_nci, - pci_t neighbor_pci) = 0; + virtual void handle_neighbor_better_than_spcell(ue_index_t ue_index, + gnb_id_t neighbor_gnb_id, + nr_cell_identity neighbor_nci, + pci_t neighbor_pci) = 0; }; /// Methods used by mobility manager to signal handover events to the CU-CP. @@ -71,14 +71,15 @@ class mobility_manager final : public mobility_manager_measurement_handler, publ void trigger_handover(pci_t source_pci, rnti_t rnti, pci_t target_pci) override; - void handle_neighbor_better_than_spcell(ue_index_t ue_index, - gnb_id_t neighbor_gnb_id, - nr_cell_id_t neighbor_nci, - pci_t neighbor_pci) override; + void handle_neighbor_better_than_spcell(ue_index_t ue_index, + gnb_id_t neighbor_gnb_id, + nr_cell_identity neighbor_nci, + pci_t neighbor_pci) override; private: - void handle_handover(ue_index_t ue_index, gnb_id_t neighbor_gnb_id, nr_cell_id_t neighbor_nci, pci_t neighbor_pci); - void handle_inter_cu_handover(ue_index_t source_ue_index, gnb_id_t target_gnb_id, nr_cell_id_t target_nci); + void + handle_handover(ue_index_t ue_index, gnb_id_t neighbor_gnb_id, nr_cell_identity neighbor_nci, pci_t neighbor_pci); + void handle_inter_cu_handover(ue_index_t source_ue_index, gnb_id_t target_gnb_id, nr_cell_identity target_nci); void handle_inter_du_handover(ue_index_t source_ue_index, pci_t neighbor_pci, du_index_t source_du_index, diff --git a/lib/cu_cp/routines/mobility/inter_cu_handover_target_routine.cpp b/lib/cu_cp/routines/mobility/inter_cu_handover_target_routine.cpp index 036febe5e5..9acc3f4997 100644 --- a/lib/cu_cp/routines/mobility/inter_cu_handover_target_routine.cpp +++ b/lib/cu_cp/routines/mobility/inter_cu_handover_target_routine.cpp @@ -243,14 +243,14 @@ bool inter_cu_handover_target_routine::fill_e1ap_bearer_context_setup_request(co bearer_context_setup_request.security_info.security_algorithm.ciphering_algo = sec_info.cipher_algo; bearer_context_setup_request.security_info.security_algorithm.integrity_protection_algorithm = sec_info.integ_algo; auto k_enc_buffer = byte_buffer::create(sec_info.k_enc); - if (k_enc_buffer.is_error()) { + if (not k_enc_buffer.has_value()) { logger.warning("Unable to allocate byte_buffer"); return false; } bearer_context_setup_request.security_info.up_security_key.encryption_key = std::move(k_enc_buffer.value()); if (sec_info.k_int.has_value()) { auto k_int_buffer = byte_buffer::create(sec_info.k_int.value()); - if (k_int_buffer.is_error()) { + if (not k_int_buffer.has_value()) { logger.warning("Unable to allocate byte_buffer"); return false; } @@ -259,7 +259,7 @@ bool inter_cu_handover_target_routine::fill_e1ap_bearer_context_setup_request(co } bearer_context_setup_request.ue_dl_aggregate_maximum_bit_rate = request.ue_aggr_max_bit_rate.ue_aggr_max_bit_rate_dl; - bearer_context_setup_request.serving_plmn = request.source_to_target_transparent_container.target_cell_id.plmn; + bearer_context_setup_request.serving_plmn = request.source_to_target_transparent_container.target_cell_id.plmn_id; bearer_context_setup_request.activity_notif_level = "ue"; // TODO: Remove hardcoded value if (bearer_context_setup_request.activity_notif_level == "ue") { bearer_context_setup_request.ue_inactivity_timer = ue_mng.get_ue_config().inactivity_timer; diff --git a/lib/cu_cp/routines/mobility/inter_du_handover_routine.cpp b/lib/cu_cp/routines/mobility/inter_du_handover_routine.cpp index 439f3e87e3..42252d229d 100644 --- a/lib/cu_cp/routines/mobility/inter_du_handover_routine.cpp +++ b/lib/cu_cp/routines/mobility/inter_du_handover_routine.cpp @@ -321,14 +321,14 @@ bool inter_du_handover_routine::add_security_context_to_bearer_context_modificat bearer_context_modification_request.security_info->security_algorithm.integrity_protection_algorithm = security_cfg.integ_algo; auto k_enc_buffer = byte_buffer::create(security_cfg.k_enc); - if (k_enc_buffer.is_error()) { + if (not k_enc_buffer.has_value()) { logger.warning("Unable to allocate byte_buffer"); return false; } bearer_context_modification_request.security_info->up_security_key.encryption_key = std::move(k_enc_buffer.value()); if (security_cfg.k_int.has_value()) { auto k_int_buffer = byte_buffer::create(security_cfg.k_int.value()); - if (k_int_buffer.is_error()) { + if (not k_int_buffer.has_value()) { logger.warning("Unable to allocate byte_buffer"); return 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 b920cbd94e..0d0b570672 100644 --- a/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp +++ b/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp @@ -426,14 +426,14 @@ bool pdu_session_resource_setup_routine::fill_e1ap_bearer_context_setup_request( 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); - if (k_enc_buffer.is_error()) { + if (not k_enc_buffer.has_value()) { logger.warning("Unable to allocate byte_buffer"); return false; } e1ap_request.security_info.up_security_key.encryption_key = std::move(k_enc_buffer.value()); if (security_cfg.k_int.has_value()) { auto k_int_buffer = byte_buffer::create(security_cfg.k_int.value()); - if (k_int_buffer.is_error()) { + if (not k_int_buffer.has_value()) { logger.warning("Unable to allocate byte_buffer"); return false; } diff --git a/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp b/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp index 87813cbeeb..5885dd9168 100644 --- a/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp +++ b/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp @@ -280,14 +280,14 @@ bool reestablishment_context_modification_routine::generate_bearer_context_modif bearer_ctxt_mod_req.security_info->security_algorithm.ciphering_algo = security_cfg.cipher_algo; bearer_ctxt_mod_req.security_info->security_algorithm.integrity_protection_algorithm = security_cfg.integ_algo; auto k_enc_buffer = byte_buffer::create(security_cfg.k_enc); - if (k_enc_buffer.is_error()) { + if (not k_enc_buffer.has_value()) { logger.warning("Unable to allocate byte_buffer"); return false; } bearer_ctxt_mod_req.security_info->up_security_key.encryption_key = std::move(k_enc_buffer.value()); if (security_cfg.k_int.has_value()) { auto k_int_buffer = byte_buffer::create(security_cfg.k_int.value()); - if (k_int_buffer.is_error()) { + if (not k_int_buffer.has_value()) { logger.warning("Unable to allocate byte_buffer"); return false; } diff --git a/lib/cu_cp/ue_manager/cu_cp_ue_impl.cpp b/lib/cu_cp/ue_manager/cu_cp_ue_impl.cpp index 6f0f1406ac..9588d04dc4 100644 --- a/lib/cu_cp/ue_manager/cu_cp_ue_impl.cpp +++ b/lib/cu_cp/ue_manager/cu_cp_ue_impl.cpp @@ -25,34 +25,45 @@ using namespace srsran; using namespace srs_cu_cp; -cu_cp_ue::cu_cp_ue(const ue_index_t ue_index_, +cu_cp_ue::cu_cp_ue(ue_index_t ue_index_, + du_index_t du_index_, const up_resource_manager_cfg& up_cfg, const security_manager_config& sec_cfg, ue_task_scheduler_impl task_sched_, - const pci_t pci_, - const rnti_t c_rnti_) : + std::optional du_id_, + std::optional pci_, + std::optional c_rnti_, + std::optional pcell_index_) : ue_index(ue_index_), task_sched(std::move(task_sched_)), up_mng(up_cfg), sec_mng(sec_cfg), rrc_ue_cu_cp_ev_notifier(ue_index) { - if (pci_ != INVALID_PCI) { - pci = pci_; + if (du_id_.has_value() && du_id_.value() != gnb_du_id_t::invalid) { + ue_ctxt.du_id = du_id_.value(); } - if (c_rnti_ != rnti_t::INVALID_RNTI) { - ue_ctxt.crnti = c_rnti_; + if (pci_.has_value() && pci_.value() != INVALID_PCI) { + pci = pci_.value(); } - ue_ctxt.du_idx = get_du_index_from_ue_index(ue_index); + if (c_rnti_.has_value() && c_rnti_.value() != rnti_t::INVALID_RNTI) { + ue_ctxt.crnti = c_rnti_.value(); + } + + if (pcell_index_.has_value() && pcell_index_.value() != du_cell_index_t::invalid) { + pcell_index = pcell_index_.value(); + } + + ue_ctxt.du_idx = du_index_; rrc_ue_cu_cp_ue_ev_notifier.connect_ue(*this); ngap_cu_cp_ue_ev_notifier.connect_ue(*this); } /// \brief Update a UE with PCI and/or C-RNTI. -void cu_cp_ue::update_du_ue(gnb_du_id_t du_id_, pci_t pci_, rnti_t c_rnti_) +void cu_cp_ue::update_du_ue(gnb_du_id_t du_id_, pci_t pci_, rnti_t c_rnti_, du_cell_index_t pcell_index_) { if (du_id_ != gnb_du_id_t::invalid) { ue_ctxt.du_id = du_id_; @@ -65,6 +76,10 @@ void cu_cp_ue::update_du_ue(gnb_du_id_t du_id_, pci_t pci_, rnti_t c_rnti_) if (c_rnti_ != rnti_t::INVALID_RNTI) { ue_ctxt.crnti = c_rnti_; } + + if (pcell_index_ != du_cell_index_t::invalid) { + pcell_index = pcell_index_; + } } /// \brief Set/update the measurement context of the UE. @@ -73,13 +88,6 @@ void cu_cp_ue::update_meas_context(cell_meas_manager_ue_context meas_ctxt) meas_context = std::move(meas_ctxt); } -/// \brief Set the DU and PCell index of the UE. -/// \param[in] pcell_index PCell index of the UE. -void cu_cp_ue::set_pcell_index(du_cell_index_t pcell_index_) -{ - pcell_index = pcell_index_; -} - /// \brief Set the RRC UE control message notifier of the UE. /// \param[in] rrc_ue_notifier_ RRC UE control message notifier of the UE. void cu_cp_ue::set_rrc_ue_notifier(du_processor_rrc_ue_control_message_notifier& rrc_ue_notifier_) diff --git a/lib/cu_cp/ue_manager/cu_cp_ue_impl.h b/lib/cu_cp/ue_manager/cu_cp_ue_impl.h index 8b029fc924..c33b294147 100644 --- a/lib/cu_cp/ue_manager/cu_cp_ue_impl.h +++ b/lib/cu_cp/ue_manager/cu_cp_ue_impl.h @@ -30,6 +30,7 @@ #include "../up_resource_manager/up_resource_manager_impl.h" #include "cu_cp_ue_impl_interface.h" #include "ue_task_scheduler_impl.h" +#include #include namespace srsran { @@ -50,12 +51,15 @@ struct cu_cp_ue_context { class cu_cp_ue : public cu_cp_ue_impl_interface { public: - cu_cp_ue(const ue_index_t ue_index_, + cu_cp_ue(ue_index_t ue_index_, + du_index_t du_index_, const up_resource_manager_cfg& up_cfg, const security_manager_config& sec_cfg, ue_task_scheduler_impl task_sched_, - const pci_t pci_ = INVALID_PCI, - const rnti_t c_rnti_ = rnti_t::INVALID_RNTI); + std::optional du_id_ = std::nullopt, + std::optional pci_ = std::nullopt, + std::optional c_rnti_ = std::nullopt, + std::optional pcell_index_ = std::nullopt); /// \brief Cancel all pending UE tasks. void stop(); @@ -72,7 +76,7 @@ class cu_cp_ue : public cu_cp_ue_impl_interface [[nodiscard]] gnb_du_id_t get_du_id() const { return ue_ctxt.du_id; } /// \brief Get the DU index of the UE. - du_index_t get_du_index() { return ue_ctxt.du_idx; } + [[nodiscard]] du_index_t get_du_index() const { return ue_ctxt.du_idx; } /// \brief Get the PCell index of the UE. du_cell_index_t get_pcell_index() { return pcell_index; } @@ -93,9 +97,10 @@ class cu_cp_ue : public cu_cp_ue_impl_interface cell_meas_manager_ue_context& get_meas_context() { return meas_context; } /// \brief Update a UE with PCI and/or C-RNTI. - void update_du_ue(gnb_du_id_t du_id_ = gnb_du_id_t::invalid, - pci_t pci_ = INVALID_PCI, - rnti_t c_rnti_ = rnti_t::INVALID_RNTI); + void update_du_ue(gnb_du_id_t du_id_ = gnb_du_id_t::invalid, + pci_t pci_ = INVALID_PCI, + rnti_t c_rnti_ = rnti_t::INVALID_RNTI, + du_cell_index_t pcell_index_ = du_cell_index_t::invalid); /// \brief Set/update the measurement context of the UE. void update_meas_context(cell_meas_manager_ue_context meas_ctxt); @@ -104,10 +109,6 @@ class cu_cp_ue : public cu_cp_ue_impl_interface /// \return True if the DU UE context is created, false otherwise. [[nodiscard]] bool du_ue_created() const { return ue_ctxt.du_idx != du_index_t::invalid; } - /// \brief Set the DU and PCell index of the UE. - /// \param[in] pcell_index PCell index of the UE. - void set_pcell_index(du_cell_index_t pcell_index_); - /// \brief Set the RRC UE control message notifier of the UE. /// \param[in] rrc_ue_notifier_ RRC UE control message notifier of the UE. void set_rrc_ue_notifier(du_processor_rrc_ue_control_message_notifier& rrc_ue_notifier_); diff --git a/lib/cu_cp/ue_manager/ue_manager_impl.cpp b/lib/cu_cp/ue_manager/ue_manager_impl.cpp index ab6f6838b6..74397c5c28 100644 --- a/lib/cu_cp/ue_manager/ue_manager_impl.cpp +++ b/lib/cu_cp/ue_manager/ue_manager_impl.cpp @@ -45,15 +45,44 @@ void ue_manager::stop() ue_task_scheds.stop(); } -ue_index_t ue_manager::add_ue(du_index_t du_index) +ue_index_t ue_manager::add_ue(du_index_t du_index, + std::optional du_id, + std::optional pci, + std::optional rnti, + std::optional pcell_index) { + if (du_index == du_index_t::invalid) { + logger.warning("CU-CP UE creation Failed. Cause: Invalid DU index={}", du_index); + return ue_index_t::invalid; + } + + if (du_id.has_value() && du_id.value() == gnb_du_id_t::invalid) { + logger.warning("CU-CP UE creation Failed. Cause: Invalid gNB-DU ID={}", du_id.value()); + return ue_index_t::invalid; + } + + if (pci.has_value() && pci.value() == INVALID_PCI) { + logger.warning("CU-CP UE creation Failed. Cause: Invalid pci={}", pci.value()); + return ue_index_t::invalid; + } + + if (rnti.has_value() && rnti.value() == rnti_t::INVALID_RNTI) { + logger.warning("CU-CP UE creation Failed. Cause: Invalid rnti={}", rnti.value()); + return ue_index_t::invalid; + } + + if (pcell_index.has_value() && pcell_index.value() == du_cell_index_t::invalid) { + logger.warning("CU-CP UE creation Failed. Cause: Invalid pcell_index={}", pcell_index.value()); + return ue_index_t::invalid; + } + if (ues.size() == ue_config.max_nof_supported_ues) { logger.warning("CU-CP UE creation Failed. Cause: Maximum number of UEs {} supported by the CU-CP has been reached", ue_config.max_nof_supported_ues); return ue_index_t::invalid; } - ue_index_t new_ue_index = get_next_ue_index(du_index); + ue_index_t new_ue_index = allocate_ue_index(); if (new_ue_index == ue_index_t::invalid) { logger.warning("CU-CP UE creation Failed. Cause: No free UE index available"); return ue_index_t::invalid; @@ -65,9 +94,20 @@ ue_index_t ue_manager::add_ue(du_index_t du_index) // Create UE object ues.emplace(std::piecewise_construct, std::forward_as_tuple(new_ue_index), - std::forward_as_tuple(new_ue_index, up_config, sec_config, std::move(ue_sched))); + std::forward_as_tuple( + new_ue_index, du_index, up_config, sec_config, std::move(ue_sched), du_id, pci, rnti, pcell_index)); + + // Add PCI and RNTI to lookup. + if (pci.has_value() && rnti.has_value()) { + pci_rnti_to_ue_index.emplace(std::make_tuple(pci.value(), rnti.value()), new_ue_index); + } - logger.info("ue={}: Created new CU-CP UE", new_ue_index); + logger.info("ue={}{}{}{}{}: Created new CU-CP UE", + new_ue_index, + du_id.has_value() ? fmt::format(" gnb_du_id={}", du_id.value()) : "", + pci.has_value() ? fmt::format(" pci={}", pci.value()) : "", + rnti.has_value() ? fmt::format(" rnti={}", rnti.value()) : "", + pcell_index.has_value() ? fmt::format(" pcell_index={}", pcell_index.value()) : ""); return new_ue_index; } @@ -132,11 +172,16 @@ ue_task_scheduler* ue_manager::find_ue_task_scheduler(ue_index_t ue_index) // du processor -cu_cp_ue* ue_manager::set_ue_du_context(ue_index_t ue_index, gnb_du_id_t du_id, pci_t pci, rnti_t rnti) +cu_cp_ue* ue_manager::set_ue_du_context(ue_index_t ue_index, + gnb_du_id_t du_id, + pci_t pci, + rnti_t rnti, + du_cell_index_t pcell_index) { srsran_assert(ue_index != ue_index_t::invalid, "Invalid ue_index={}", ue_index); srsran_assert(pci != INVALID_PCI, "Invalid pci={}", pci); srsran_assert(rnti != rnti_t::INVALID_RNTI, "Invalid rnti={}", rnti); + srsran_assert(pcell_index != du_cell_index_t::invalid, "Invalid pcell_index={}", pcell_index); // check if ue_index is in db if (ues.find(ue_index) == ues.end()) { @@ -151,12 +196,13 @@ cu_cp_ue* ue_manager::set_ue_du_context(ue_index_t ue_index, gnb_du_id_t du_id, } auto& ue = ues.at(ue_index); - ue.update_du_ue(du_id, pci, rnti); + ue.update_du_ue(du_id, pci, rnti, pcell_index); // Add PCI and RNTI to lookup. pci_rnti_to_ue_index.emplace(std::make_tuple(pci, rnti), ue_index); - logger.debug("ue={}: Updated UE with pci={} and rnti={}", ue_index, pci, rnti); + logger.debug( + "ue={}: Updated UE with gnb_du_id={} pci={} rnti={} pcell_index={}", ue_index, du_id, pci, rnti, pcell_index); return &ue; } @@ -169,6 +215,19 @@ cu_cp_ue* ue_manager::find_du_ue(ue_index_t ue_index) return nullptr; } +size_t ue_manager::get_nof_du_ues(du_index_t du_index) +{ + unsigned ue_count = 0; + // Count UEs connected to the DU + for (const auto& ue : ues) { + if (ue.second.get_du_index() == du_index) { + ue_count++; + } + } + + return ue_count; +} + std::vector ue_manager::handle_ue_metrics_report_request() const { std::vector report; @@ -188,15 +247,35 @@ std::vector ue_manager::handle_ue_metrics_report_reques // private functions -ue_index_t ue_manager::get_next_ue_index(du_index_t du_index) +ue_index_t ue_manager::allocate_ue_index() { - // Search unallocated UE index - for (uint16_t i = 0; i < MAX_NOF_UES_PER_DU; i++) { - ue_index_t new_ue_index = generate_ue_index(du_index, i); - if (ues.find(new_ue_index) == ues.end()) { - logger.debug("Allocating new ue_index={} for du_index={}", new_ue_index, du_index); - return new_ue_index; + // return invalid when no UE index is available + if (ues.size() == ue_config.max_nof_supported_ues) { + return ue_index_t::invalid; + } + + // Check if the next_ue_index is available + if (ues.find(next_ue_index) == ues.end()) { + ue_index_t ret = next_ue_index; + // increase the next_ue_index + increase_next_ue_index(); + return ret; + } + + // Find holes in the allocated IDs by iterating over all ids starting with the next_ue_index to find the + // available id + while (true) { + // increase the next_ue_index and try again + increase_next_ue_index(); + + // return the id if it is not already used + if (ues.find(next_ue_index) == ues.end()) { + ue_index_t ret = next_ue_index; + // increase the next_ue_index + increase_next_ue_index(); + return ret; } } + return ue_index_t::invalid; } diff --git a/lib/cu_cp/ue_manager/ue_manager_impl.h b/lib/cu_cp/ue_manager/ue_manager_impl.h index 9f5261b167..e015b53d60 100644 --- a/lib/cu_cp/ue_manager/ue_manager_impl.h +++ b/lib/cu_cp/ue_manager/ue_manager_impl.h @@ -26,11 +26,12 @@ #include "../adapters/ngap_adapters.h" #include "../adapters/rrc_ue_adapters.h" #include "../cell_meas_manager/measurement_context.h" -#include "../ue_security_manager/ue_security_manager_impl.h" #include "cu_cp_ue_impl.h" #include "ue_metrics_handler.h" #include "ue_task_scheduler_impl.h" +#include "srsran/cu_cp/security_manager_config.h" #include "srsran/cu_cp/ue_configuration.h" +#include #include namespace srsran { @@ -82,11 +83,26 @@ class ue_manager : public ue_metrics_handler /// \brief Allocate resources for the UE in the CU-CP. /// \param[in] du_index Index of the DU the UE is connected to. + /// \param[in] du_id The gNB-DU ID of the DU the UE is connected to. + /// \param[in] pci The PCI of the cell the UE is connected to. + /// \param[in] rnti The RNTI of the UE. + /// \param[in] pcell_index The index of the PCell the UE is connected to. /// \return ue_index of the created UE or ue_index_t::invalid in case of failure. - ue_index_t add_ue(du_index_t du_index); + ue_index_t add_ue(du_index_t du_index, + std::optional du_id = std::nullopt, + std::optional pci = std::nullopt, + std::optional rnti = std::nullopt, + std::optional pcell_index = std::nullopt); /// \brief Set the DU context of the UE. - cu_cp_ue* set_ue_du_context(ue_index_t ue_index, gnb_du_id_t du_id, pci_t pci, rnti_t rnti); + /// \param[in] ue_index Index of the UE. + /// \param[in] du_id The gNB-DU ID of the DU the UE is connected to. + /// \param[in] pci The PCI of the cell the UE is connected to. + /// \param[in] rnti The RNTI of the UE. + /// \param[in] pcell_index The index of the PCell the UE is connected to. + /// \return Pointer to the DU UE if found, nullptr otherwise. + cu_cp_ue* + set_ue_du_context(ue_index_t ue_index, gnb_du_id_t du_id, pci_t pci, rnti_t rnti, du_cell_index_t pcell_index); /// \brief Find the UE with the given UE index, thats DU context is set up. /// \param[in] ue_index Index of the UE to be found. @@ -95,18 +111,7 @@ class ue_manager : public ue_metrics_handler /// \brief Get the number of UEs connected to a specific DU. /// \return Number of UEs. - size_t get_nof_du_ues(du_index_t du_index) - { - unsigned ue_count = 0; - // Search allocated UE indexes - for (uint16_t i = 0; i < MAX_NOF_UES_PER_DU; i++) { - ue_index_t new_ue_index = generate_ue_index(du_index, i); - if (ues.find(new_ue_index) != ues.end()) { - ue_count++; - } - } - return ue_count; - } + size_t get_nof_du_ues(du_index_t du_index); // ngap @@ -149,10 +154,24 @@ class ue_manager : public ue_metrics_handler return ues.at(ue_index).get_meas_context(); } +protected: + ue_index_t next_ue_index = ue_index_t::min; + private: /// \brief Get the next available UE index. /// \return The UE index. - ue_index_t get_next_ue_index(du_index_t du_index); + ue_index_t allocate_ue_index(); + + inline void increase_next_ue_index() + { + if (next_ue_index == ue_index_t::max) { + // reset cu ue f1ap id counter + next_ue_index = ue_index_t::min; + } else { + // increase cu ue f1ap id counter + next_ue_index = uint_to_ue_index(ue_index_to_uint(next_ue_index) + 1); + } + } srslog::basic_logger& logger = srslog::fetch_basic_logger("CU-UEMNG"); const ue_configuration ue_config; diff --git a/lib/cu_up/adapters/e1ap_adapters.h b/lib/cu_up/adapters/e1ap_adapters.h index 831c7d860f..25a12cab26 100644 --- a/lib/cu_up/adapters/e1ap_adapters.h +++ b/lib/cu_up/adapters/e1ap_adapters.h @@ -25,13 +25,14 @@ #include "srsran/cu_up/cu_up.h" #include "srsran/e1ap/cu_up/e1ap_cu_up.h" -namespace srsran { -namespace srs_cu_up { +namespace srsran::srs_cu_up { /// Adapter between E1AP and CU-UP class e1ap_cu_up_adapter : public e1ap_cu_up_notifier { public: + e1ap_cu_up_adapter() : logger(srslog::fetch_basic_logger("CU-UP-E1")) {} + void connect_cu_up(cu_up_e1ap_interface& cu_up_handler_) { cu_up_handler = &cu_up_handler_; } void disconnect() { cu_up_handler = nullptr; } @@ -40,15 +41,17 @@ class e1ap_cu_up_adapter : public e1ap_cu_up_notifier on_bearer_context_setup_request_received(const e1ap_bearer_context_setup_request& msg) override { if (cu_up_handler == nullptr) { + logger.warning("Could not handle context setup command, no CU-UP handler present"); return {}; // return failure to setup bearer context } return cu_up_handler->handle_bearer_context_setup_request(msg); } - e1ap_bearer_context_modification_response + async_task on_bearer_context_modification_request_received(const e1ap_bearer_context_modification_request& msg) override { if (cu_up_handler == nullptr) { + logger.warning("Could not handle context modification command, no CU-UP handler present. ue={}", msg.ue_index); return {}; // return failure to modify bearer context } return cu_up_handler->handle_bearer_context_modification_request(msg); @@ -57,14 +60,24 @@ class e1ap_cu_up_adapter : public e1ap_cu_up_notifier void on_bearer_context_release_command_received(const e1ap_bearer_context_release_command& msg) override { if (cu_up_handler == nullptr) { + logger.warning("Could not handle context release command, no CU-UP handler present. ue={}", msg.ue_index); return; } return cu_up_handler->handle_bearer_context_release_command(msg); } + void on_schedule_ue_async_task(srs_cu_up::ue_index_t ue_index, async_task task) override + { + if (cu_up_handler == nullptr) { + logger.error("Could not schedule UE task, no CU-UP handler present. ue={}", ue_index); + return; + } + cu_up_handler->schedule_ue_async_task(ue_index, std::move(task)); + } + private: cu_up_e1ap_interface* cu_up_handler = nullptr; + srslog::basic_logger& logger; }; -} // namespace srs_cu_up -} // namespace srsran +} // namespace srsran::srs_cu_up diff --git a/lib/cu_up/adapters/f1u_adapters.h b/lib/cu_up/adapters/f1u_adapters.h index 92339781cf..4fe26e17c0 100644 --- a/lib/cu_up/adapters/f1u_adapters.h +++ b/lib/cu_up/adapters/f1u_adapters.h @@ -55,11 +55,22 @@ class f1u_pdcp_adapter final : public f1u_rx_sdu_notifier, public f1u_rx_deliver { pdcp_tx_handler->handle_transmit_notification(highest_pdcp_sn); } + void on_delivery_notification(uint32_t highest_pdcp_sn) override { pdcp_tx_handler->handle_delivery_notification(highest_pdcp_sn); } + void on_retransmit_notification(uint32_t highest_pdcp_sn) override + { + pdcp_tx_handler->handle_retransmit_notification(highest_pdcp_sn); + } + + void on_delivery_retransmitted_notification(uint32_t highest_pdcp_sn) override + { + pdcp_tx_handler->handle_delivery_retransmitted_notification(highest_pdcp_sn); + } + private: pdcp_rx_lower_interface* pdcp_rx_handler = nullptr; pdcp_tx_lower_interface* pdcp_tx_handler = nullptr; diff --git a/lib/cu_up/adapters/pdcp_adapters.h b/lib/cu_up/adapters/pdcp_adapters.h index 48fe215fa1..ab5e0fdbde 100644 --- a/lib/cu_up/adapters/pdcp_adapters.h +++ b/lib/cu_up/adapters/pdcp_adapters.h @@ -87,12 +87,12 @@ class pdcp_f1u_adapter : public pdcp_tx_lower_notifier void connect_f1u(f1u_tx_sdu_handler& f1u_handler_) { f1u_handler = &f1u_handler_; } void disconnect_f1u() { f1u_handler = nullptr; } - void on_new_pdu(byte_buffer pdu) override + void on_new_pdu(byte_buffer pdu, bool is_retx) override { if (f1u_handler == nullptr) { srslog::fetch_basic_logger("PDCP").info("Dropped DL PDU. F1-U handler is not connected"); } else { - f1u_handler->handle_sdu(std::move(pdu)); + f1u_handler->handle_sdu(std::move(pdu), is_retx); } } diff --git a/lib/cu_up/cu_up_impl.cpp b/lib/cu_up/cu_up_impl.cpp index ad4c431781..1492c3b1a0 100644 --- a/lib/cu_up/cu_up_impl.cpp +++ b/lib/cu_up/cu_up_impl.cpp @@ -26,6 +26,7 @@ #include "srsran/gtpu/gtpu_demux_factory.h" #include "srsran/gtpu/gtpu_echo_factory.h" #include "srsran/gtpu/gtpu_teid_pool_factory.h" +#include "srsran/support/async/execute_on.h" #include using namespace srsran; @@ -211,6 +212,11 @@ void cu_up::disconnect() e1ap_cu_up_ev_notifier.disconnect(); } +void cu_up::schedule_ue_async_task(ue_index_t ue_index, async_task task) +{ + ue_mng->schedule_ue_async_task(ue_index, std::move(task)); +} + void process_successful_pdu_resource_setup_mod_outcome( slotted_id_vector& pdu_session_resource_setup_list, @@ -361,7 +367,7 @@ cu_up::handle_bearer_context_setup_request(const e1ap_bearer_context_setup_reque return response; } -e1ap_bearer_context_modification_response +async_task cu_up::handle_bearer_context_modification_request(const e1ap_bearer_context_modification_request& msg) { ue_context* ue_ctxt = ue_mng->find_ue(msg.ue_index); @@ -369,59 +375,66 @@ cu_up::handle_bearer_context_modification_request(const e1ap_bearer_context_modi logger.error("Could not find UE context"); return {}; } + return execute_and_continue_on_blocking( + ue_ctxt->ue_exec_mapper->ctrl_executor(), *cfg.ctrl_executor, [this, ue_ctxt, msg]() { + return handle_bearer_context_modification_request_impl(*ue_ctxt, msg); + }); +} - ue_ctxt->get_logger().log_debug("Handling BearerContextModificationRequest"); - +e1ap_bearer_context_modification_response +cu_up::handle_bearer_context_modification_request_impl(ue_context& ue_ctxt, + const e1ap_bearer_context_modification_request& msg) +{ e1ap_bearer_context_modification_response response = {}; - response.ue_index = ue_ctxt->get_index(); - response.success = true; + ue_ctxt.get_logger().log_debug("Handling BearerContextModificationRequest"); + + response.ue_index = ue_ctxt.get_index(); + response.success = true; bool new_ul_tnl_info_required = msg.new_ul_tnl_info_required == std::string("required"); if (msg.security_info.has_value()) { security::sec_as_config security_info; fill_sec_as_config(security_info, msg.security_info.value()); - ue_ctxt->set_security_config(security_info); + ue_ctxt.set_security_config(security_info); } if (msg.ng_ran_bearer_context_mod_request.has_value()) { // Traverse list of PDU sessions to be setup/modified for (const auto& pdu_session_item : msg.ng_ran_bearer_context_mod_request.value().pdu_session_res_to_setup_mod_list) { - ue_ctxt->get_logger().log_debug("Setup/Modification of {}", pdu_session_item.pdu_session_id); - pdu_session_setup_result session_result = ue_ctxt->setup_pdu_session(pdu_session_item); + ue_ctxt.get_logger().log_debug("Setup/Modification of {}", pdu_session_item.pdu_session_id); + pdu_session_setup_result session_result = ue_ctxt.setup_pdu_session(pdu_session_item); process_successful_pdu_resource_setup_mod_outcome(response.pdu_session_resource_setup_list, session_result); response.success &= session_result.success; // Update final result. } // Traverse list of PDU sessions to be modified. for (const auto& pdu_session_item : msg.ng_ran_bearer_context_mod_request.value().pdu_session_res_to_modify_list) { - ue_ctxt->get_logger().log_debug("Modifying {}", pdu_session_item.pdu_session_id); + ue_ctxt.get_logger().log_debug("Modifying {}", pdu_session_item.pdu_session_id); pdu_session_modification_result session_result = - ue_ctxt->modify_pdu_session(pdu_session_item, new_ul_tnl_info_required); + ue_ctxt.modify_pdu_session(pdu_session_item, new_ul_tnl_info_required); process_successful_pdu_resource_modification_outcome(response.pdu_session_resource_modified_list, response.pdu_session_resource_failed_to_modify_list, session_result, logger); - ue_ctxt->get_logger().log_debug("Modification {}", session_result.success ? "successful" : "failed"); + ue_ctxt.get_logger().log_debug("Modification {}", session_result.success ? "successful" : "failed"); response.success &= session_result.success; // Update final result. } // Traverse list of PDU sessions to be removed. for (const auto& pdu_session_item : msg.ng_ran_bearer_context_mod_request.value().pdu_session_res_to_rem_list) { - ue_ctxt->get_logger().log_info("Removing {}", pdu_session_item); - ue_ctxt->remove_pdu_session(pdu_session_item); + ue_ctxt.get_logger().log_info("Removing {}", pdu_session_item); + ue_ctxt.remove_pdu_session(pdu_session_item); // There is no IE to confirm successful removal. } } else { - ue_ctxt->get_logger().log_warning("Ignoring empty Bearer Context Modification Request"); + ue_ctxt.get_logger().log_warning("Ignoring empty Bearer Context Modification Request"); } // 3. Create response - response.success = true; - return response; } diff --git a/lib/cu_up/cu_up_impl.h b/lib/cu_up/cu_up_impl.h index 64ad08c34d..5a359435b9 100644 --- a/lib/cu_up/cu_up_impl.h +++ b/lib/cu_up/cu_up_impl.h @@ -49,12 +49,14 @@ class cu_up final : public cu_up_interface std::optional get_n3_bind_port() override { return ngu_session->get_bind_port(); } // cu_up_e1ap_interface + void schedule_ue_async_task(ue_index_t ue_index, async_task task) override; + e1ap_message_handler& get_e1ap_message_handler() override { return *e1ap; } e1ap_bearer_context_setup_response handle_bearer_context_setup_request(const e1ap_bearer_context_setup_request& msg) override; - e1ap_bearer_context_modification_response + async_task handle_bearer_context_modification_request(const e1ap_bearer_context_modification_request& msg) override; void handle_bearer_context_release_command(const e1ap_bearer_context_release_command& msg) override; @@ -64,14 +66,15 @@ class cu_up final : public cu_up_interface void on_e1ap_connection_drop() override; bool e1ap_is_connected() override { return e1ap_connected; } - // cu_up_ngu_interface - gtpu_demux_rx_upper_layer_interface& get_ngu_pdu_handler() override { return *ngu_demux; } - private: void disconnect(); void on_statistics_report_timer_expired(); + e1ap_bearer_context_modification_response + handle_bearer_context_modification_request_impl(ue_context& ue_ctxt, + const e1ap_bearer_context_modification_request& msg); + cu_up_configuration cfg; // logger diff --git a/lib/cu_up/pdu_session_manager_impl.cpp b/lib/cu_up/pdu_session_manager_impl.cpp index 67a89e05ba..b2306399f5 100644 --- a/lib/cu_up/pdu_session_manager_impl.cpp +++ b/lib/cu_up/pdu_session_manager_impl.cpp @@ -97,7 +97,7 @@ pdu_session_setup_result pdu_session_manager_impl::setup_pdu_session(const e1ap_ // Allocate local TEID expected local_teid = n3_teid_allocator.request_teid(); - if (local_teid.is_error()) { + if (not local_teid.has_value()) { logger.log_warning("Failed to create PDU session. Cause: could not allocate local TEID. {}", session.pdu_session_id); return pdu_session_result; diff --git a/lib/cu_up/routines/initial_cu_up_setup_routine.cpp b/lib/cu_up/routines/initial_cu_up_setup_routine.cpp index 077df5bb1c..6d6b580999 100644 --- a/lib/cu_up/routines/initial_cu_up_setup_routine.cpp +++ b/lib/cu_up/routines/initial_cu_up_setup_routine.cpp @@ -22,7 +22,7 @@ #include "initial_cu_up_setup_routine.h" #include "srsran/e1ap/common/e1_setup_messages.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/srslog/srslog.h" using namespace srsran; diff --git a/lib/cu_up/ue_context.h b/lib/cu_up/ue_context.h index 091374e4e4..eaeca02e47 100644 --- a/lib/cu_up/ue_context.h +++ b/lib/cu_up/ue_context.h @@ -29,11 +29,13 @@ #include "srsran/e1ap/cu_up/e1ap_cu_up.h" #include "srsran/f1u/cu_up/f1u_gateway.h" #include "srsran/gtpu/gtpu_teid_pool.h" +#include "srsran/support/async/fifo_async_task_scheduler.h" #include #include -namespace srsran { -namespace srs_cu_up { +namespace srsran::srs_cu_up { + +constexpr uint16_t UE_TASK_QUEUE_SIZE = 1024; /// \brief UE context setup configuration struct ue_context_cfg { @@ -62,6 +64,8 @@ class ue_context : public pdu_session_manager_ctrl gtpu_tunnel_common_tx_upper_layer_notifier& gtpu_tx_notifier_, gtpu_demux_ctrl& gtpu_rx_demux_, dlt_pcap& gtpu_pcap) : + task_sched(UE_TASK_QUEUE_SIZE), + ue_exec_mapper(std::move(ue_exec_mapper_)), index(index_), cfg(std::move(cfg_)), logger("CU-UP", {index_}), @@ -81,12 +85,11 @@ class ue_context : public pdu_session_manager_ctrl f1u_teid_allocator_, gtpu_tx_notifier_, gtpu_rx_demux_, - ue_exec_mapper_->dl_pdu_executor(), - ue_exec_mapper_->ul_pdu_executor(), - ue_exec_mapper_->ctrl_executor(), - ue_exec_mapper_->crypto_executor(), + ue_exec_mapper->dl_pdu_executor(), + ue_exec_mapper->ul_pdu_executor(), + ue_exec_mapper->ctrl_executor(), + ue_exec_mapper->crypto_executor(), gtpu_pcap), - ue_exec_mapper(std::move(ue_exec_mapper_)), ue_dl_timer_factory(ue_dl_timer_factory_), ue_ul_timer_factory(ue_ul_timer_factory_), ue_ctrl_timer_factory(ue_ctrl_timer_factory_) @@ -134,6 +137,10 @@ class ue_context : public pdu_session_manager_ctrl [[nodiscard]] const cu_up_ue_logger& get_logger() const { return logger; }; + fifo_async_task_scheduler task_sched; + + std::unique_ptr ue_exec_mapper; + private: ue_index_t index; ue_context_cfg cfg; @@ -142,8 +149,6 @@ class ue_context : public pdu_session_manager_ctrl e1ap_control_message_handler& e1ap; pdu_session_manager_impl pdu_session_manager; - std::unique_ptr ue_exec_mapper; - timer_factory ue_dl_timer_factory; timer_factory ue_ul_timer_factory; timer_factory ue_ctrl_timer_factory; @@ -166,5 +171,4 @@ class ue_context : public pdu_session_manager_ctrl } }; -} // namespace srs_cu_up -} // namespace srsran +} // namespace srsran::srs_cu_up diff --git a/lib/cu_up/ue_manager.cpp b/lib/cu_up/ue_manager.cpp index c4394febc4..5cdc9ee835 100644 --- a/lib/cu_up/ue_manager.cpp +++ b/lib/cu_up/ue_manager.cpp @@ -137,3 +137,13 @@ ue_index_t ue_manager::get_next_ue_index() } return INVALID_UE_INDEX; } + +void ue_manager::schedule_ue_async_task(ue_index_t ue_index, async_task task) +{ + ue_context* ue_ctx = find_ue(ue_index); + if (ue_ctx == nullptr) { + logger.error("Cannot schedule UE task, could not find UE. ue_index={}", ue_index); + return; + } + ue_ctx->task_sched.schedule(std::move(task)); +} diff --git a/lib/cu_up/ue_manager.h b/lib/cu_up/ue_manager.h index 63dbbfe970..ce46f88969 100644 --- a/lib/cu_up/ue_manager.h +++ b/lib/cu_up/ue_manager.h @@ -57,6 +57,8 @@ class ue_manager : public ue_manager_ctrl ue_context* find_ue(ue_index_t ue_index) override; size_t get_nof_ues() const override { return ue_db.size(); }; + void schedule_ue_async_task(ue_index_t ue_index, async_task task); + private: /// \brief Get the next available UE index. /// \return The UE index. diff --git a/lib/du/du_cell_config_validation.cpp b/lib/du/du_cell_config_validation.cpp index 602fe02e73..413b193d97 100644 --- a/lib/du/du_cell_config_validation.cpp +++ b/lib/du/du_cell_config_validation.cpp @@ -37,7 +37,7 @@ using namespace srsran; #define CHECK_TRUE(cond, ...) \ if (not(cond)) { \ - return {fmt::format(__VA_ARGS__)}; \ + return make_unexpected(fmt::format(__VA_ARGS__)); \ } #define CHECK_COMPARE_(val, expected_val, comparison, ...) \ diff --git a/lib/du_high/adapters/mac_test_mode_adapter.cpp b/lib/du_high/adapters/mac_test_mode_adapter.cpp index 513685e662..97d0e5df53 100644 --- a/lib/du_high/adapters/mac_test_mode_adapter.cpp +++ b/lib/du_high/adapters/mac_test_mode_adapter.cpp @@ -48,8 +48,8 @@ static expected create_test_pdu_with_bsr(slot_point sl_r // We pass BSR=254, which according to TS38.321 is the maximum value for the LBSR size. auto buf = byte_buffer::create({0x3e, 0x02, 0x01, 254}); - if (buf.is_error()) { - return default_error_t{}; + if (not buf.has_value()) { + return make_unexpected(default_error_t{}); } return mac_rx_data_indication{ sl_rx, to_du_cell_index(0), mac_rx_pdu_list{mac_rx_pdu{test_rnti, 0, harq_id, std::move(buf.value())}}}; @@ -339,7 +339,7 @@ void mac_test_mode_cell_adapter::forward_uci_ind_to_mac(const mac_uci_indication if (test_ue_cfg.pusch_active) { auto rx_pdu = create_test_pdu_with_bsr(uci_msg.sl_rx, pdu.rnti, to_harq_id(0)); - if (rx_pdu.is_error()) { + if (not rx_pdu.has_value()) { logger.warning("Unable to create test PDU with BSR"); continue; } @@ -371,7 +371,7 @@ void mac_test_mode_cell_adapter::forward_crc_ind_to_mac(const mac_crc_indication } auto rx_pdu = create_test_pdu_with_bsr(crc_msg.sl_rx, pdu.rnti, to_harq_id(pdu.harq_id)); - if (rx_pdu.is_error()) { + if (not rx_pdu.has_value()) { logger.warning("Unable to create test PDU with BSR"); continue; } diff --git a/lib/du_high/du_high_impl.cpp b/lib/du_high/du_high_impl.cpp index 6b5d28067b..ad621626c3 100644 --- a/lib/du_high/du_high_impl.cpp +++ b/lib/du_high/du_high_impl.cpp @@ -185,7 +185,7 @@ void du_high_impl::start() // 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 (rx_buf.is_error()) { + if (not rx_buf.has_value()) { logger.warning("Unable to allocate byte_buffer"); continue; } diff --git a/lib/du_manager/converters/f1ap_configuration_helpers.cpp b/lib/du_manager/converters/f1ap_configuration_helpers.cpp index 4bfbb30f8e..8a5fdc8624 100644 --- a/lib/du_manager/converters/f1ap_configuration_helpers.cpp +++ b/lib/du_manager/converters/f1ap_configuration_helpers.cpp @@ -24,8 +24,7 @@ #include "asn1_rrc_config_helpers.h" #include "srsran/asn1/rrc_nr/bcch_dl_sch_msg.h" #include "srsran/asn1/rrc_nr/meas_timing_cfg.h" -#include "srsran/ran/bcd_helpers.h" -#include "srsran/ran/nr_cgi_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/support/error_handling.h" using namespace srsran; @@ -300,18 +299,17 @@ static asn1::rrc_nr::sib1_s make_asn1_rrc_cell_sib1(const du_cell_config& du_cfg sib1.cell_access_related_info.plmn_id_info_list.resize(1); sib1.cell_access_related_info.plmn_id_info_list[0].plmn_id_list.resize(1); - plmn_id_s& plmn = sib1.cell_access_related_info.plmn_id_info_list[0].plmn_id_list[0]; - plmn.mcc_present = true; - plmn.mcc[0] = du_cfg.nr_cgi.plmn[0] - '0'; - plmn.mcc[1] = du_cfg.nr_cgi.plmn[1] - '0'; - plmn.mcc[2] = du_cfg.nr_cgi.plmn[2] - '0'; - plmn.mnc.resize(du_cfg.nr_cgi.plmn.size() == 5 ? 2 : 3); - for (unsigned i = 3; i < du_cfg.nr_cgi.plmn.size(); ++i) { - plmn.mnc[i - 3] = du_cfg.nr_cgi.plmn[i] - '0'; + plmn_id_s& plmn = sib1.cell_access_related_info.plmn_id_info_list[0].plmn_id_list[0]; + plmn.mcc_present = true; + plmn.mcc = du_cfg.nr_cgi.plmn_id.mcc().to_bytes(); + static_vector mnc = du_cfg.nr_cgi.plmn_id.mnc().to_bytes(); + plmn.mnc.resize(mnc.size()); + for (unsigned i = 0; i < mnc.size(); ++i) { + plmn.mnc[i] = mnc[i]; } sib1.cell_access_related_info.plmn_id_info_list[0].tac_present = true; sib1.cell_access_related_info.plmn_id_info_list[0].tac.from_number(du_cfg.tac); - sib1.cell_access_related_info.plmn_id_info_list[0].cell_id.from_number(du_cfg.nr_cgi.nci); + sib1.cell_access_related_info.plmn_id_info_list[0].cell_id.from_number(du_cfg.nr_cgi.nci.value()); sib1.cell_access_related_info.plmn_id_info_list[0].cell_reserved_for_oper.value = plmn_id_info_s::cell_reserved_for_oper_opts::not_reserved; diff --git a/lib/du_manager/du_cell_manager.cpp b/lib/du_manager/du_cell_manager.cpp index 322e282fe9..4fe230d144 100644 --- a/lib/du_manager/du_cell_manager.cpp +++ b/lib/du_manager/du_cell_manager.cpp @@ -39,7 +39,7 @@ void du_cell_manager::add_cell(const du_cell_config& cell_cfg) { // Verify that DU cell configuration is valid. Abort application otherwise. auto ret = is_du_cell_config_valid(cell_cfg); - if (ret.is_error()) { + if (not ret.has_value()) { report_error("ERROR: Invalid DU Cell Configuration. Cause: {}.\n", ret.error()); } diff --git a/lib/du_manager/du_ue/du_ue_adapters.cpp b/lib/du_manager/du_ue/du_ue_adapters.cpp index 2cdd08551e..0aa772b306 100644 --- a/lib/du_manager/du_ue/du_ue_adapters.cpp +++ b/lib/du_manager/du_ue/du_ue_adapters.cpp @@ -51,6 +51,8 @@ class null_sink_f1u_bearer : public f1u_bearer, void handle_pdu(nru_dl_message msg) override {} void handle_transmit_notification(uint32_t highest_pdcp_sn) override {} void handle_delivery_notification(uint32_t highest_pdcp_sn) override {} + void handle_retransmit_notification(uint32_t highest_pdcp_sn) override {} + void handle_delivery_retransmitted_notification(uint32_t highest_pdcp_sn) override {} void handle_sdu(byte_buffer_chain sdu) override {} void stop() override {} } null_f1u_bearer; @@ -58,7 +60,7 @@ class null_sink_f1u_bearer : public f1u_bearer, class null_sink_rlc_bearer : public rlc_tx_upper_layer_data_interface, public rlc_rx_lower_layer_interface { public: - void handle_sdu(byte_buffer sdu_buf) override {} + void handle_sdu(byte_buffer sdu_buf, bool is_retx) override {} void discard_sdu(uint32_t pdcp_sn) override {} void handle_pdu(byte_buffer_slice pdu) override {} } null_rlc_bearer; diff --git a/lib/du_manager/du_ue/du_ue_adapters.h b/lib/du_manager/du_ue/du_ue_adapters.h index dc7e3d6a6d..77ab1c5da1 100644 --- a/lib/du_manager/du_ue/du_ue_adapters.h +++ b/lib/du_manager/du_ue/du_ue_adapters.h @@ -48,7 +48,7 @@ class f1c_rx_sdu_rlc_adapter final : public f1c_rx_sdu_notifier { srsran_assert(rlc_tx != nullptr, "RLC Tx PDU notifier is disconnected"); - rlc_tx->handle_sdu(std::move(pdu)); + rlc_tx->handle_sdu(std::move(pdu), /* is_retx = */ false); } private: @@ -66,10 +66,10 @@ class f1u_rx_rlc_sdu_adapter final : public f1u_rx_sdu_notifier /// \brief Stop forwarding SDUs to the RLC layer. void disconnect(); - void on_new_sdu(byte_buffer sdu) override + void on_new_sdu(byte_buffer sdu, bool is_retx) override { srsran_assert(rlc_tx != nullptr, "RLC Tx SDU notifier is disconnected"); - rlc_tx->handle_sdu(std::move(sdu)); + rlc_tx->handle_sdu(std::move(sdu), is_retx); } void on_discard_sdu(uint32_t pdcp_sn) override { rlc_tx->discard_sdu(pdcp_sn); } @@ -153,6 +153,17 @@ class rlc_f1c_tx_data_notifier : public rlc_tx_upper_layer_data_notifier b->handle_delivery_notification(max_deliv_pdcp_sn); } + void on_retransmitted_sdu(uint32_t max_retx_pdcp_sn) override + { + srsran_assertion_failure("Unexpected call of on_retransmitted_sdu on SRB. max_retx_pdcp_sn={}", max_retx_pdcp_sn); + } + + void on_delivered_retransmitted_sdu(uint32_t max_deliv_retx_pdcp_sn) override + { + srsran_assertion_failure("Unexpected call of on_delivered_retransmitted_sdu on SRB. max_deliv_retx_pdcp_sn={}", + max_deliv_retx_pdcp_sn); + } + private: /// An atomic pointer to the handler. This pointer may be changed by \c disconnect from UE thread while the cell /// thread still uses this to notify F1 of transmitted/delivered PDCP SNs. @@ -181,6 +192,20 @@ class rlc_f1u_tx_data_notifier : public rlc_tx_upper_layer_data_notifier h->handle_delivery_notification(max_deliv_pdcp_sn); } + void on_retransmitted_sdu(uint32_t max_retx_pdcp_sn) override + { + f1u_tx_delivery_handler* h = handler.load(std::memory_order_relaxed); + srsran_assert(h != nullptr, "RLC to F1-U TX data notifier is disconnected"); + h->handle_retransmit_notification(max_retx_pdcp_sn); + } + + void on_delivered_retransmitted_sdu(uint32_t max_deliv_retx_pdcp_sn) override + { + f1u_tx_delivery_handler* h = handler.load(std::memory_order_relaxed); + srsran_assert(h != nullptr, "RLC to F1-U TX data notifier is disconnected"); + h->handle_delivery_retransmitted_notification(max_deliv_retx_pdcp_sn); + } + private: /// An atomic pointer to the handler. This pointer may be changed by \c disconnect from UE thread while the cell /// thread still uses this to notify F1 of transmitted/delivered PDCP SNs. diff --git a/lib/du_manager/du_ue/du_ue_manager.cpp b/lib/du_manager/du_ue/du_ue_manager.cpp index 0a4e8c35b3..5aa968dbf5 100644 --- a/lib/du_manager/du_ue/du_ue_manager.cpp +++ b/lib/du_manager/du_ue/du_ue_manager.cpp @@ -242,12 +242,12 @@ expected du_ue_manager::add_ue(const du_ue_context& if (not is_du_ue_index_valid(ue_ctx.ue_index) or (not is_crnti(ue_ctx.rnti) and ue_ctx.rnti != rnti_t::INVALID_RNTI)) { // UE identifiers are invalid. - return std::string("Invalid UE identifiers"); + return make_unexpected(std::string("Invalid UE identifiers")); } if (ue_db.contains(ue_ctx.ue_index) or (is_crnti(ue_ctx.rnti) and rnti_to_ue_index.count(ue_ctx.rnti) > 0)) { // UE already existed with same ue_index or C-RNTI. - return std::string("UE already existed with same ue_index or C-RNTI"); + return make_unexpected(std::string("UE already existed with same ue_index or C-RNTI")); } // Create UE context object diff --git a/lib/du_manager/procedures/initial_du_setup_procedure.cpp b/lib/du_manager/procedures/initial_du_setup_procedure.cpp index ea353a0826..544c80b82c 100644 --- a/lib/du_manager/procedures/initial_du_setup_procedure.cpp +++ b/lib/du_manager/procedures/initial_du_setup_procedure.cpp @@ -62,7 +62,7 @@ void initial_du_setup_procedure::operator()(coro_context>& ctx) auto sched_cfg = srs_du::make_sched_cell_config_req(cell_index, du_cfg, bcch_msg_payload_lens); error_type result = config_validators::validate_sched_cell_configuration_request_message(sched_cfg, params.mac.sched_cfg); - if (result.is_error()) { + if (not result.has_value()) { report_error("Invalid cell={} configuration. Cause: {}", cell_index, result.error()); } params.mac.cell_mng.add_cell(make_mac_cell_config(cell_index, du_cfg, std::move(bcch_msgs), sched_cfg)); diff --git a/lib/du_manager/procedures/ue_configuration_procedure.cpp b/lib/du_manager/procedures/ue_configuration_procedure.cpp index 59ce98341c..6892721c56 100644 --- a/lib/du_manager/procedures/ue_configuration_procedure.cpp +++ b/lib/du_manager/procedures/ue_configuration_procedure.cpp @@ -61,8 +61,11 @@ void ue_configuration_procedure::operator()(coro_context Stop traffic in the DRBs that need to be removed. + CORO_AWAIT(stop_drbs_to_rem()); + // > Update DU UE bearers. - CORO_AWAIT(update_ue_context()); + update_ue_context(); // > Update MAC bearers. CORO_AWAIT_VALUE(mac_ue_reconfiguration_response mac_res, update_mac_mux_and_demux()); @@ -75,7 +78,13 @@ void ue_configuration_procedure::operator()(coro_context ue_configuration_procedure::update_ue_context() +async_task ue_configuration_procedure::stop_drbs_to_rem() +{ + // Request traffic to stop for DRBs that are going to be removed. + return ue->handle_drb_traffic_stop_request(request.drbs_to_rem); +} + +void ue_configuration_procedure::update_ue_context() { // > Create DU UE SRB objects. for (srb_id_t srbid : request.srbs_to_setup) { @@ -183,9 +192,6 @@ async_task ue_configuration_procedure::update_ue_context() } ue->bearers.add_drb(std::move(drb)); } - - // Request traffic to stop for DRBs that are going to be removed. - return ue->handle_drb_traffic_stop_request(request.drbs_to_rem); } void ue_configuration_procedure::clear_old_ue_context() diff --git a/lib/du_manager/procedures/ue_configuration_procedure.h b/lib/du_manager/procedures/ue_configuration_procedure.h index 0585f3a946..316974bfa4 100644 --- a/lib/du_manager/procedures/ue_configuration_procedure.h +++ b/lib/du_manager/procedures/ue_configuration_procedure.h @@ -41,10 +41,12 @@ class ue_configuration_procedure const char* name() const { return "UE Configuration"; } private: - /// \brief Update DU UE bearers. This stage includes the creation/modification/removal of SRBs/DRBs, creation of RLC - /// and F1-U bearers. - async_task update_ue_context(); - void clear_old_ue_context(); + // Stop activity in DRBs that need to be replaced. + async_task stop_drbs_to_rem(); + // Update DU UE bearers. This stage includes the creation/modification/removal of SRBs/DRBs, creation of RLC + // and F1-U bearers. + void update_ue_context(); + void clear_old_ue_context(); /// \brief Update MAC MUX and DEMUX tables of the respective UE, given the newly added/modified/removed bearers. async_task update_mac_mux_and_demux(); diff --git a/lib/du_manager/procedures/ue_creation_procedure.cpp b/lib/du_manager/procedures/ue_creation_procedure.cpp index dfe442e4ab..58863e46d5 100644 --- a/lib/du_manager/procedures/ue_creation_procedure.cpp +++ b/lib/du_manager/procedures/ue_creation_procedure.cpp @@ -50,7 +50,7 @@ void ue_creation_procedure::operator()(coro_context>& ctx) // > Check if UE context was created in the DU manager. ue_ctx_creation_outcome = create_du_ue_context(); - if (ue_ctx_creation_outcome.is_error()) { + if (not ue_ctx_creation_outcome.has_value()) { proc_logger.log_proc_failure("Failed to create DU UE context. Cause: {}", ue_ctx_creation_outcome.error().data()); CORO_AWAIT(clear_ue()); CORO_EARLY_RETURN(); @@ -107,7 +107,7 @@ expected ue_creation_procedure::create_du_ue_context() // Create a DU UE resource manager, which will be responsible for managing bearer and PUCCH resources. ue_ran_resource_configurator ue_res = du_res_alloc.create_ue_resource_configurator(req.ue_index, req.pcell_index); if (ue_res.empty()) { - return ue_res.get_error(); + return make_unexpected(ue_res.get_error()); } // Create the DU UE context. diff --git a/lib/du_manager/ran_resource_management/du_ran_resource_manager.h b/lib/du_manager/ran_resource_management/du_ran_resource_manager.h index efe6fa19a4..64778ad64d 100644 --- a/lib/du_manager/ran_resource_management/du_ran_resource_manager.h +++ b/lib/du_manager/ran_resource_management/du_ran_resource_manager.h @@ -37,7 +37,7 @@ struct du_ue_resource_update_response { std::vector failed_drbs; std::vector failed_scells; - bool release_required() const { return procedure_error.is_error(); } + bool release_required() const { return not procedure_error.has_value(); } }; /// \brief This class manages the PHY (e.g. RB and symbols used for PUCCH), MAC (e.g. LCIDs) and RLC resources used diff --git a/lib/du_manager/ran_resource_management/du_ran_resource_manager_impl.cpp b/lib/du_manager/ran_resource_management/du_ran_resource_manager_impl.cpp index 69ef09a470..85b0f73caf 100644 --- a/lib/du_manager/ran_resource_management/du_ran_resource_manager_impl.cpp +++ b/lib/du_manager/ran_resource_management/du_ran_resource_manager_impl.cpp @@ -109,7 +109,7 @@ ue_ran_resource_configurator du_ran_resource_manager_impl::create_ue_resource_co // UE initialized PCell. error_type err = allocate_cell_resources(ue_index, pcell_index, SERVING_CELL_PCELL_IDX); - if (err.is_error()) { + if (not err.has_value()) { ue_res_pool.erase(ue_index); return ue_ran_resource_configurator{std::unique_ptr{nullptr}, err.error()}; } @@ -244,14 +244,14 @@ du_ran_resource_manager_impl::update_context(du_ue_index_t if (not ue_mcg.cells.contains(0) or ue_mcg.cells[0].serv_cell_cfg.cell_index != pcell_idx) { // >> PCell changed. Allocate new PCell resources. error_type outcome = allocate_cell_resources(ue_index, pcell_idx, SERVING_CELL_PCELL_IDX); - if (outcome.is_error()) { + if (not outcome.has_value()) { resp.procedure_error = outcome; return resp; } } for (const f1ap_scell_to_setup& sc : upd_req.scells_to_setup) { // >> SCells Added/Modified. Allocate new SCell resources. - if (allocate_cell_resources(ue_index, sc.cell_index, sc.serv_cell_index).is_error()) { + if (not allocate_cell_resources(ue_index, sc.cell_index, sc.serv_cell_index).has_value()) { resp.failed_scells.push_back(sc.serv_cell_index); } } @@ -293,8 +293,7 @@ error_type du_ran_resource_manager_impl::allocate_cell_resources(du ue_res.pcg_cfg.pdsch_harq_codebook = pdsch_harq_ack_codebook::dynamic; if (not pucch_res_mng.alloc_resources(ue_res)) { - return error_type( - fmt::format("Unable to allocate dedicated PUCCH resources for cell={}", cell_index)); + return make_unexpected(fmt::format("Unable to allocate dedicated PUCCH resources for cell={}", cell_index)); } } else { srsran_assert(not ue_res.cells.contains(serv_cell_index), "Reallocation of SCell detected"); diff --git a/lib/du_manager/ran_resource_management/pucch_resource_generator.cpp b/lib/du_manager/ran_resource_management/pucch_resource_generator.cpp index 540e5df593..27696fd3d9 100644 --- a/lib/du_manager/ran_resource_management/pucch_resource_generator.cpp +++ b/lib/du_manager/ran_resource_management/pucch_resource_generator.cpp @@ -331,7 +331,7 @@ error_type srsran::srs_du::pucch_parameters_validator(unsigned { // > If intraslot_freq_hopping is enabled, check if PUCCH Format 2 has more than symbol. if (f2_params.intraslot_freq_hopping and f2_params.nof_symbols == 1) { - return {"Intra-slot frequency hopping for PUCCH Format 2 requires 2 symbols"}; + return make_unexpected("Intra-slot frequency hopping for PUCCH Format 2 requires 2 symbols"); } // > Compute the number of RBs required for the PUCCH Format 1 and 2 resources. @@ -354,7 +354,7 @@ error_type srsran::srs_du::pucch_parameters_validator(unsigned : f2_params.max_nof_rbs; if (f2_max_rbs > pucch_constants::FORMAT2_MAX_NPRB) { - return {"The number of PRBs for PUCCH Format 2 exceeds the limit of 16"}; + return make_unexpected("The number of PRBs for PUCCH Format 2 exceeds the limit of 16"); } const unsigned nof_f2_blocks = NOF_OFDM_SYM_PER_SLOT_NORMAL_CP / f2_params.nof_symbols.to_uint(); @@ -369,7 +369,7 @@ error_type srsran::srs_du::pucch_parameters_validator(unsigned // [Implementation-defined] We do not allow the PUCCH resources to occupy more than 60% of the BWP. const float max_allowed_prbs_usage = 0.6F; if (static_cast(nof_f1_rbs + nof_f2_rbs) / static_cast(bwp_size_rbs) >= max_allowed_prbs_usage) { - return {"With the given parameters, the number of PRBs for PUCCH exceeds the 60% of the BWP PRBs"}; + return make_unexpected("With the given parameters, the number of PRBs for PUCCH exceeds the 60% of the BWP PRBs"); } return {}; @@ -488,7 +488,7 @@ std::vector srsran::srs_du::generate_cell_pucch_res_list(unsigne unsigned bwp_size_rbs) { auto outcome = pucch_parameters_validator(nof_res_f1, nof_res_f2, f1_params, f2_params, bwp_size_rbs); - if (outcome.is_error()) { + if (not outcome.has_value()) { srsran_assertion_failure("The cell list could not be generated due to: {}", outcome.error()); return {}; } diff --git a/lib/e1ap/common/e1ap_asn1_converters.h b/lib/e1ap/common/e1ap_asn1_converters.h index 96aa64e061..51fb4967f2 100644 --- a/lib/e1ap/common/e1ap_asn1_converters.h +++ b/lib/e1ap/common/e1ap_asn1_converters.h @@ -26,7 +26,7 @@ #include "srsran/asn1/e1ap/e1ap_ies.h" #include "srsran/e1ap/common/e1ap_types.h" #include "srsran/pdcp/pdcp_config.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/ran/cause/e1ap_cause.h" #include "srsran/ran/cu_types.h" #include "srsran/ran/nr_cgi.h" @@ -189,8 +189,8 @@ inline asn1::e1ap::nr_cgi_s nr_cgi_to_e1ap_asn1(const nr_cell_global_id_t& nr_cg { asn1::e1ap::nr_cgi_s asn1_nr_cgi; - asn1_nr_cgi.nr_cell_id.from_number(nr_cgi.nci); - asn1_nr_cgi.plmn_id.from_string(nr_cgi.plmn_hex); + asn1_nr_cgi.nr_cell_id.from_number(nr_cgi.nci.value()); + asn1_nr_cgi.plmn_id = nr_cgi.plmn_id.to_bytes(); return asn1_nr_cgi; } @@ -1052,21 +1052,9 @@ inline e1ap_pdcp_count e1ap_asn1_pdcp_count_to_pdcp_count(asn1::e1ap::pdcp_count /// \brief Convert E1AP ASN.1 CGI to \c nr_cell_global_id_t type. inline nr_cell_global_id_t e1ap_asn1_to_cgi(const asn1::e1ap::nr_cgi_s& asn1_cgi) { - nr_cell_global_id_t cgi = {}; - uint32_t encoded_plmn = asn1_cgi.plmn_id.to_number(); - ngap_plmn_to_mccmnc(encoded_plmn, &cgi.mcc, &cgi.mnc); - - std::string mcc_string; - std::string mnc_string; - mcc_to_string(cgi.mcc, &mcc_string); - mnc_to_string(cgi.mnc, &mnc_string); - cgi.plmn = mcc_string + mnc_string; - - // Set PLMN hex string. - cgi.plmn_hex = asn1_cgi.plmn_id.to_string(); - - cgi.nci = asn1_cgi.nr_cell_id.to_number(); - + nr_cell_global_id_t cgi = {}; + cgi.nci = nr_cell_identity::create(asn1_cgi.nr_cell_id.to_number()).value(); + cgi.plmn_id = plmn_identity::from_bytes(asn1_cgi.plmn_id.to_bytes()).value(); return cgi; } diff --git a/lib/e1ap/common/e1ap_asn1_helpers.h b/lib/e1ap/common/e1ap_asn1_helpers.h index 8b121cb536..fd297fefc4 100644 --- a/lib/e1ap/common/e1ap_asn1_helpers.h +++ b/lib/e1ap/common/e1ap_asn1_helpers.h @@ -24,7 +24,7 @@ #include "e1ap_asn1_converters.h" #include "srsran/asn1/e1ap/e1ap_pdu_contents.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/ran/qos_prio_level.h" namespace srsran { diff --git a/lib/e1ap/cu_cp/e1ap_cu_cp_asn1_helpers.h b/lib/e1ap/cu_cp/e1ap_cu_cp_asn1_helpers.h index 956790776f..a9bcfebdef 100644 --- a/lib/e1ap/cu_cp/e1ap_cu_cp_asn1_helpers.h +++ b/lib/e1ap/cu_cp/e1ap_cu_cp_asn1_helpers.h @@ -25,7 +25,7 @@ #include "../common/e1ap_asn1_converters.h" #include "srsran/asn1/asn1_utils.h" #include "srsran/asn1/e1ap/e1ap_pdu_contents.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/ran/qos_prio_level.h" namespace srsran { @@ -263,7 +263,7 @@ inline void fill_asn1_bearer_context_setup_request(asn1::e1ap::bearer_context_se asn1_request->ue_dl_aggr_max_bit_rate = request.ue_dl_aggregate_maximum_bit_rate; // serving plmn - asn1_request->serving_plmn.from_number(plmn_string_to_bcd(request.serving_plmn)); + asn1_request->serving_plmn = request.serving_plmn.to_bytes(); // activity notification level asn1::string_to_enum(asn1_request->activity_notif_level, request.activity_notif_level); diff --git a/lib/e1ap/cu_cp/e1ap_cu_cp_impl.cpp b/lib/e1ap/cu_cp/e1ap_cu_cp_impl.cpp index f2f2e40fb5..197690781e 100644 --- a/lib/e1ap/cu_cp/e1ap_cu_cp_impl.cpp +++ b/lib/e1ap/cu_cp/e1ap_cu_cp_impl.cpp @@ -341,7 +341,7 @@ void e1ap_cu_cp_impl::handle_unsuccessful_outcome(const asn1::e1ap::unsuccessful } // Set transaction result and resume suspended procedure. - if (not ev_mng.transactions.set_response(transaction_id.value(), outcome)) { + if (not ev_mng.transactions.set_response(transaction_id.value(), make_unexpected(outcome))) { logger.warning("Ignoring message. Cause: Transaction with id={} has already completed", transaction_id.value()); } } diff --git a/lib/e1ap/cu_up/CMakeLists.txt b/lib/e1ap/cu_up/CMakeLists.txt index 163b585c5a..277b17de90 100644 --- a/lib/e1ap/cu_up/CMakeLists.txt +++ b/lib/e1ap/cu_up/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(srsran_e1ap_cu_up e1ap_cu_up_factory.cpp e1ap_cu_up_impl.cpp e1ap_cu_up_connection_handler.cpp - procedures/e1ap_cu_up_setup_procedure.cpp) + procedures/e1ap_cu_up_setup_procedure.cpp + procedures/bearer_context_modification_procedure.cpp) target_include_directories(srsran_e1ap_cu_up PRIVATE ..) target_link_libraries(srsran_e1ap_cu_up e1ap_asn1 srsran_e1ap_common) diff --git a/lib/e1ap/cu_up/e1ap_cu_up_asn1_helpers.h b/lib/e1ap/cu_up/e1ap_cu_up_asn1_helpers.h index 4fea85582d..164e707e4b 100644 --- a/lib/e1ap/cu_up/e1ap_cu_up_asn1_helpers.h +++ b/lib/e1ap/cu_up/e1ap_cu_up_asn1_helpers.h @@ -25,8 +25,9 @@ #include "../common/e1ap_asn1_converters.h" #include "srsran/asn1/e1ap/e1ap.h" #include "srsran/asn1/e1ap/e1ap_pdu_contents.h" +#include "srsran/e1ap/common/e1_setup_messages.h" #include "srsran/e1ap/cu_up/e1ap_cu_up_bearer_context_update.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/ran/five_qi.h" #include "srsran/ran/qos_prio_level.h" @@ -54,7 +55,7 @@ inline asn1::e1ap::gnb_cu_up_e1_setup_request_s cu_up_e1_setup_request_to_asn1(c asn1::e1ap::supported_plmns_item_s asn1_plmn_item; // plmn id - asn1_plmn_item.plmn_id.from_number(plmn_string_to_bcd(plmn_item.plmn_id)); + asn1_plmn_item.plmn_id.from_number(bcd_helper::plmn_string_to_bcd(plmn_item.plmn_id)); // slice support list for (const auto& slice_item : plmn_item.slice_support_list) { @@ -203,7 +204,7 @@ inline void fill_e1ap_bearer_context_setup_request(e1ap_bearer_context_setup_req request.ue_dl_aggregate_maximum_bit_rate = asn1_request->ue_dl_aggr_max_bit_rate; // serving plmn - request.serving_plmn = plmn_bcd_to_string(asn1_request->serving_plmn.to_number()); + request.serving_plmn = bcd_helper::plmn_bcd_to_string(asn1_request->serving_plmn.to_number()); // activity notification level request.activity_notif_level = asn1_to_activity_notification_level(asn1_request->activity_notif_level.value); diff --git a/lib/e1ap/cu_up/e1ap_cu_up_impl.cpp b/lib/e1ap/cu_up/e1ap_cu_up_impl.cpp index f8fe5b589e..9f96eaa977 100644 --- a/lib/e1ap/cu_up/e1ap_cu_up_impl.cpp +++ b/lib/e1ap/cu_up/e1ap_cu_up_impl.cpp @@ -22,11 +22,12 @@ #include "e1ap_cu_up_impl.h" #include "../common/log_helpers.h" +#include "cu_up/procedures/bearer_context_modification_procedure.h" #include "cu_up/procedures/e1ap_cu_up_event_manager.h" #include "e1ap_cu_up_asn1_helpers.h" #include "procedures/e1ap_cu_up_setup_procedure.h" #include "srsran/e1ap/common/e1ap_message.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/support/timers.h" #include @@ -65,7 +66,7 @@ e1ap_cu_up_impl::e1ap_cu_up_impl(e1_connection_client& e1_client_handler_, } // Note: For fwd declaration of member types, dtor cannot be trivial. -e1ap_cu_up_impl::~e1ap_cu_up_impl() {} +e1ap_cu_up_impl::~e1ap_cu_up_impl() = default; bool e1ap_cu_up_impl::connect_to_cu_cp() { @@ -179,7 +180,8 @@ void e1ap_cu_up_impl::handle_bearer_context_setup_request(const asn1::e1ap::bear return; } - logger.debug("Received BearerContextSetupRequest (plmn={})", plmn_bcd_to_string(msg->serving_plmn.to_number())); + logger.debug("Received BearerContextSetupRequest (plmn={})", + bcd_helper::plmn_bcd_to_string(msg->serving_plmn.to_number())); gnb_cu_up_ue_e1ap_id_t cu_up_ue_e1ap_id = ue_ctxt_list.next_gnb_cu_up_ue_e1ap_id(); if (cu_up_ue_e1ap_id == gnb_cu_up_ue_e1ap_id_t::invalid) { @@ -251,8 +253,6 @@ void e1ap_cu_up_impl::handle_bearer_context_modification_request(const asn1::e1a e1ap_msg.pdu.unsuccessful_outcome().value.bearer_context_mod_fail()->gnb_cu_up_ue_e1ap_id = msg->gnb_cu_up_ue_e1ap_id; e1ap_msg.pdu.unsuccessful_outcome().value.bearer_context_mod_fail()->cause.set_protocol(); - e1ap_bearer_context_modification_request bearer_context_mod = {}; - if (!ue_ctxt_list.contains(int_to_gnb_cu_up_ue_e1ap_id(msg->gnb_cu_up_ue_e1ap_id))) { logger.warning("Sending BearerContextModificationFailure. UE context not available"); pdu_notifier->on_new_message(e1ap_msg); @@ -261,71 +261,9 @@ void e1ap_cu_up_impl::handle_bearer_context_modification_request(const asn1::e1a e1ap_ue_context& ue_ctxt = ue_ctxt_list[int_to_gnb_cu_up_ue_e1ap_id(msg->gnb_cu_up_ue_e1ap_id)]; - bearer_context_mod.ue_index = ue_ctxt.ue_ids.ue_index; - - // security info - if (msg->security_info_present) { - bearer_context_mod.security_info.emplace(); - bearer_context_mod.security_info->security_algorithm.ciphering_algo = - e1ap_asn1_to_ciphering_algorithm(msg->security_info.security_algorithm.ciphering_algorithm); - - if (msg->security_info.security_algorithm.integrity_protection_algorithm_present) { - bearer_context_mod.security_info->security_algorithm.integrity_protection_algorithm = - e1ap_asn1_to_integrity_algorithm(msg->security_info.security_algorithm.integrity_protection_algorithm); - } - bearer_context_mod.security_info->up_security_key.encryption_key = - msg->security_info.up_securitykey.encryption_key.copy(); - bearer_context_mod.security_info->up_security_key.integrity_protection_key = - msg->security_info.up_securitykey.integrity_protection_key.copy(); - } - - // sys bearer context mod request - if (msg->sys_bearer_context_mod_request_present) { - // We only support NG-RAN Bearer - if (msg->sys_bearer_context_mod_request.type() != - asn1::e1ap::sys_bearer_context_mod_request_c::types::ng_ran_bearer_context_mod_request) { - ue_ctxt.logger.log_error("Sending BearerContextModificationFailure. Cause: Not handling E-UTRAN Bearers"); - - // send response - pdu_notifier->on_new_message(e1ap_msg); - return; - } - - fill_e1ap_bearer_context_modification_request(bearer_context_mod, msg); - } - - // Forward message to CU-UP - e1ap_bearer_context_modification_response bearer_context_mod_response_msg = - cu_up_notifier.on_bearer_context_modification_request_received(bearer_context_mod); - - if (bearer_context_mod_response_msg.ue_index == INVALID_UE_INDEX) { - ue_ctxt.logger.log_debug("Sending BearerContextModificationFailure: Cause: Invalid UE index"); - - // send response - pdu_notifier->on_new_message(e1ap_msg); - return; - } - - if (bearer_context_mod_response_msg.success) { - e1ap_msg.pdu.set_successful_outcome(); - e1ap_msg.pdu.successful_outcome().load_info_obj(ASN1_E1AP_ID_BEARER_CONTEXT_MOD); - e1ap_msg.pdu.successful_outcome().value.bearer_context_mod_resp()->gnb_cu_cp_ue_e1ap_id = msg->gnb_cu_cp_ue_e1ap_id; - e1ap_msg.pdu.successful_outcome().value.bearer_context_mod_resp()->gnb_cu_up_ue_e1ap_id = msg->gnb_cu_up_ue_e1ap_id; - e1ap_msg.pdu.successful_outcome().value.bearer_context_mod_resp()->sys_bearer_context_mod_resp_present = true; - fill_asn1_bearer_context_modification_response( - e1ap_msg.pdu.successful_outcome().value.bearer_context_mod_resp()->sys_bearer_context_mod_resp, - bearer_context_mod_response_msg); - ue_ctxt.logger.log_debug("Sending BearerContextModificationResponse"); - // send response - pdu_notifier->on_new_message(e1ap_msg); - } else { - e1ap_msg.pdu.unsuccessful_outcome().value.bearer_context_mod_fail()->cause = - cause_to_asn1(bearer_context_mod_response_msg.cause.value()); - - // send response - ue_ctxt.logger.log_debug("Sending BearerContextModificationFailure"); - pdu_notifier->on_new_message(e1ap_msg); - } + cu_up_notifier.on_schedule_ue_async_task( + ue_ctxt.ue_ids.ue_index, + launch_async(ue_ctxt, msg, *pdu_notifier, cu_up_notifier)); } void e1ap_cu_up_impl::handle_bearer_context_release_command(const asn1::e1ap::bearer_context_release_cmd_s& msg) @@ -387,7 +325,7 @@ void e1ap_cu_up_impl::handle_unsuccessful_outcome(const asn1::e1ap::unsuccessful } // Set transaction result and resume suspended procedure. - if (not ev_mng->transactions.set_response(transaction_id.value(), outcome)) { + if (not ev_mng->transactions.set_response(transaction_id.value(), make_unexpected(outcome))) { logger.warning("Unexpected transaction id={}", transaction_id.value()); } } diff --git a/lib/e1ap/cu_up/procedures/bearer_context_modification_procedure.cpp b/lib/e1ap/cu_up/procedures/bearer_context_modification_procedure.cpp new file mode 100644 index 0000000000..9510fa85b0 --- /dev/null +++ b/lib/e1ap/cu_up/procedures/bearer_context_modification_procedure.cpp @@ -0,0 +1,113 @@ +/* + * + * 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 "bearer_context_modification_procedure.h" +#include "common/e1ap_asn1_converters.h" +#include "cu_up/e1ap_cu_up_asn1_helpers.h" +#include "srsran/e1ap/common/e1ap_message.h" +#include "srsran/e1ap/cu_up/e1ap_cu_up_bearer_context_update.h" + +using namespace srsran; +using namespace srsran::srs_cu_up; + +bearer_context_modification_procedure::bearer_context_modification_procedure( + const e1ap_ue_context& ue_ctxt_, + const asn1::e1ap::bearer_context_mod_request_s& request_, + e1ap_message_notifier& pdu_notifier_, + e1ap_cu_up_notifier& cu_up_notifier_) : + ue_ctxt(ue_ctxt_), request(request_), pdu_notifier(pdu_notifier_), cu_up_notifier(cu_up_notifier_) +{ + prepare_failure_message(); +} + +void bearer_context_modification_procedure::operator()(coro_context>& ctx) +{ + CORO_BEGIN(ctx); + + bearer_context_mod.ue_index = ue_ctxt.ue_ids.ue_index; + + if (not validate_request()) { + pdu_notifier.on_new_message(e1ap_msg); + CORO_EARLY_RETURN(); + } + + fill_e1ap_bearer_context_modification_request(bearer_context_mod, request); + + // Here, we transfer to the UE control executor to safely delete PDU sessions, + // change keys, perform PDCP retransmissions, etc. + CORO_AWAIT_VALUE(bearer_context_mod_response_msg, + cu_up_notifier.on_bearer_context_modification_request_received(bearer_context_mod)); + + // Could not find UE + if (bearer_context_mod_response_msg.ue_index == INVALID_UE_INDEX) { + ue_ctxt.logger.log_error("Sending BearerContextModificationFailure: Cause: Invalid UE index"); + pdu_notifier.on_new_message(e1ap_msg); + CORO_EARLY_RETURN(); + } + + // PDU sessions failed to setup + if (not bearer_context_mod_response_msg.success) { + e1ap_msg.pdu.unsuccessful_outcome().value.bearer_context_mod_fail()->cause = + cause_to_asn1(bearer_context_mod_response_msg.cause.value()); + ue_ctxt.logger.log_warning("Sending BearerContextModificationFailure"); + pdu_notifier.on_new_message(e1ap_msg); + CORO_EARLY_RETURN(); + } + + // Bearer modification successful + e1ap_msg.pdu.set_successful_outcome(); + e1ap_msg.pdu.successful_outcome().load_info_obj(ASN1_E1AP_ID_BEARER_CONTEXT_MOD); + e1ap_msg.pdu.successful_outcome().value.bearer_context_mod_resp()->gnb_cu_cp_ue_e1ap_id = + request->gnb_cu_cp_ue_e1ap_id; + e1ap_msg.pdu.successful_outcome().value.bearer_context_mod_resp()->gnb_cu_up_ue_e1ap_id = + request->gnb_cu_up_ue_e1ap_id; + e1ap_msg.pdu.successful_outcome().value.bearer_context_mod_resp()->sys_bearer_context_mod_resp_present = true; + fill_asn1_bearer_context_modification_response( + e1ap_msg.pdu.successful_outcome().value.bearer_context_mod_resp()->sys_bearer_context_mod_resp, + bearer_context_mod_response_msg); + ue_ctxt.logger.log_debug("Sending BearerContextModificationResponse"); + pdu_notifier.on_new_message(e1ap_msg); + CORO_RETURN(); +} + +void bearer_context_modification_procedure::prepare_failure_message() +{ + e1ap_msg.pdu.set_unsuccessful_outcome(); + e1ap_msg.pdu.unsuccessful_outcome().load_info_obj(ASN1_E1AP_ID_BEARER_CONTEXT_MOD); + e1ap_msg.pdu.unsuccessful_outcome().value.bearer_context_mod_fail()->gnb_cu_cp_ue_e1ap_id = + request->gnb_cu_cp_ue_e1ap_id; + e1ap_msg.pdu.unsuccessful_outcome().value.bearer_context_mod_fail()->gnb_cu_up_ue_e1ap_id = + request->gnb_cu_up_ue_e1ap_id; + e1ap_msg.pdu.unsuccessful_outcome().value.bearer_context_mod_fail()->cause.set_protocol(); +} + +bool bearer_context_modification_procedure::validate_request() +{ + // We only support NG-RAN bearers + if (request->sys_bearer_context_mod_request_present && + request->sys_bearer_context_mod_request.type() != + asn1::e1ap::sys_bearer_context_mod_request_c::types::ng_ran_bearer_context_mod_request) { + ue_ctxt.logger.log_error("Sending BearerContextModificationFailure. Cause: Not handling E-UTRAN Bearers"); + return false; + } + return true; +} diff --git a/lib/e1ap/cu_up/procedures/bearer_context_modification_procedure.h b/lib/e1ap/cu_up/procedures/bearer_context_modification_procedure.h new file mode 100644 index 0000000000..3aeb39f44d --- /dev/null +++ b/lib/e1ap/cu_up/procedures/bearer_context_modification_procedure.h @@ -0,0 +1,63 @@ +/* + * + * 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 "cu_up/ue_context/e1ap_cu_up_ue_context.h" +#include "srsran/asn1/e1ap/e1ap.h" +#include "srsran/asn1/e1ap/e1ap_pdu_contents.h" +#include "srsran/e1ap/common/e1ap_common.h" +#include "srsran/e1ap/common/e1ap_message.h" +#include "srsran/e1ap/cu_up/e1ap_cu_up.h" +#include "srsran/support/async/async_task.h" + +namespace srsran::srs_cu_up { + +/// E1 Setup Procedure for the CU-UP as per TS 38.463, section TODO. +class bearer_context_modification_procedure +{ +public: + bearer_context_modification_procedure(const e1ap_ue_context& ue_ctxt_, + const asn1::e1ap::bearer_context_mod_request_s& request_, + e1ap_message_notifier& pdu_notifier_, + e1ap_cu_up_notifier& cu_up_notifier_); + + void operator()(coro_context>& ctx); + + static const char* name() { return "E1AP CU-UP Bearer Context Modification Procedure"; } + +private: + const e1ap_ue_context ue_ctxt; + const asn1::e1ap::bearer_context_mod_request_s request; + e1ap_message_notifier& pdu_notifier; + e1ap_cu_up_notifier& cu_up_notifier; + + // local variables + e1ap_message e1ap_msg = {}; + e1ap_bearer_context_modification_request bearer_context_mod = {}; + e1ap_bearer_context_modification_response bearer_context_mod_response_msg = {}; + + void prepare_failure_message(); + bool validate_request(); +}; + +} // namespace srsran::srs_cu_up diff --git a/lib/e1ap/cu_up/procedures/e1ap_cu_up_setup_procedure.cpp b/lib/e1ap/cu_up/procedures/e1ap_cu_up_setup_procedure.cpp index 07b3669e6c..bd99c683c1 100644 --- a/lib/e1ap/cu_up/procedures/e1ap_cu_up_setup_procedure.cpp +++ b/lib/e1ap/cu_up/procedures/e1ap_cu_up_setup_procedure.cpp @@ -23,7 +23,7 @@ #include "e1ap_cu_up_setup_procedure.h" #include "../e1ap_cu_up_asn1_helpers.h" #include "srsran/e1ap/common/e1ap_message.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/support/async/async_timer.h" using namespace srsran; diff --git a/lib/e2/common/e2_impl.cpp b/lib/e2/common/e2_impl.cpp index 87ed349107..ea716f3328 100644 --- a/lib/e2/common/e2_impl.cpp +++ b/lib/e2/common/e2_impl.cpp @@ -205,7 +205,7 @@ void e2_impl::handle_successful_outcome(const asn1::e2ap::successful_outcome_s& case asn1::e2ap::e2ap_elem_procs_o::successful_outcome_c::types_opts::options::e2setup_resp: { // Handle successful outcomes with transaction id expected transaction_id = get_transaction_id(outcome); - if (transaction_id.is_error()) { + if (not transaction_id.has_value()) { logger.error("Successful outcome of type {} is not supported", outcome.value.type().to_string()); return; } @@ -227,12 +227,12 @@ void e2_impl::handle_unsuccessful_outcome(const asn1::e2ap::unsuccessful_outcome case asn1::e2ap::e2ap_elem_procs_o::unsuccessful_outcome_c::types_opts::options::e2setup_fail: { // Handle successful outcomes with transaction id expected transaction_id = get_transaction_id(outcome); - if (transaction_id.is_error()) { + if (not transaction_id.has_value()) { logger.error("Unsuccessful outcome of type {} is not supported", outcome.value.type().to_string()); return; } // Set transaction result and resume suspended procedure. - if (not events->transactions.set_response(transaction_id.value(), outcome)) { + if (not events->transactions.set_response(transaction_id.value(), make_unexpected(outcome))) { logger.warning("Unrecognized transaction id={}", transaction_id.value()); } handle_e2_setup_failure({{}, outcome.value.e2setup_fail(), false}); diff --git a/lib/e2/common/e2_subscription_manager_impl.cpp b/lib/e2/common/e2_subscription_manager_impl.cpp index 100078a400..5687d759ca 100644 --- a/lib/e2/common/e2_subscription_manager_impl.cpp +++ b/lib/e2/common/e2_subscription_manager_impl.cpp @@ -136,7 +136,7 @@ bool e2_subscription_manager_impl::action_supported(const ric_action_to_be_setup } auto action_def_buf = action.ric_action_definition.deep_copy(); - if (action_def_buf.is_error()) { + if (not action_def_buf.has_value()) { logger.warning("Failed to deep copy a byte_buffer"); return false; } diff --git a/lib/e2/common/e2ap_asn1_helpers.h b/lib/e2/common/e2ap_asn1_helpers.h index 042427ee90..026f5a283c 100644 --- a/lib/e2/common/e2ap_asn1_helpers.h +++ b/lib/e2/common/e2ap_asn1_helpers.h @@ -31,7 +31,7 @@ #include "srsran/e2/e2.h" #include "srsran/e2/e2ap_configuration.h" #include "srsran/e2/e2sm/e2sm_manager.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/ran/gnb_du_id.h" #include "srsran/security/security.h" #include @@ -79,7 +79,7 @@ inline void fill_asn1_e2ap_setup_request(asn1::e2ap::e2setup_request_s& setup, auto& gnb_id = setup->global_e2node_id.set_gnb(); gnb_id.global_gnb_id.gnb_id.gnb_id().from_number(e2ap_config.gnb_id.id, e2ap_config.gnb_id.bit_length); // convert PLMN to BCD - uint32_t plmn_bcd = plmn_string_to_bcd(e2ap_config.plmn); + uint32_t plmn_bcd = bcd_helper::plmn_string_to_bcd(e2ap_config.plmn); gnb_id.global_gnb_id.plmn_id.from_number(plmn_bcd); if (e2ap_config.gnb_du_id.has_value()) { diff --git a/lib/e2/common/e2ap_asn1_utils.h b/lib/e2/common/e2ap_asn1_utils.h index 97c43d2f12..6ef5ba7718 100644 --- a/lib/e2/common/e2ap_asn1_utils.h +++ b/lib/e2/common/e2ap_asn1_utils.h @@ -38,7 +38,7 @@ inline expected get_transaction_id(const asn1::e2ap::init_msg_s& out) default: break; } - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } /// Extracts transaction id of Successful Outcome message. @@ -52,7 +52,7 @@ inline expected get_transaction_id(const asn1::e2ap::successful_outcome default: break; } - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } /// Extracts transaction id of Unsuccessful Outcome message. @@ -65,7 +65,7 @@ inline expected get_transaction_id(const asn1::e2ap::unsuccessful_outco default: break; } - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } /// Extracts transaction id of E2AP PDU. @@ -82,7 +82,7 @@ inline expected get_transaction_id(const asn1::e2ap::e2ap_pdu_c& pdu) default: break; } - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } /// Extracts message type. diff --git a/lib/f1ap/common/asn1_helpers.cpp b/lib/f1ap/common/asn1_helpers.cpp index 28d0b00512..ffbbbea8cd 100644 --- a/lib/f1ap/common/asn1_helpers.cpp +++ b/lib/f1ap/common/asn1_helpers.cpp @@ -21,27 +21,21 @@ */ #include "asn1_helpers.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" using namespace srsran; -nr_cell_global_id_t srsran::cgi_from_asn1(const asn1::f1ap::nr_cgi_s& asn1_cgi) +expected srsran::cgi_from_asn1(const asn1::f1ap::nr_cgi_s& asn1_cgi) { - nr_cell_global_id_t cgi = {}; - uint32_t encoded_plmn = asn1_cgi.plmn_id.to_number(); - ngap_plmn_to_mccmnc(encoded_plmn, &cgi.mcc, &cgi.mnc); - - std::string mcc_string, mnc_string; - mcc_to_string(cgi.mcc, &mcc_string); - mnc_to_string(cgi.mnc, &mnc_string); - cgi.plmn = mcc_string + mnc_string; - - // Set PLMN hex string - cgi.plmn_hex = asn1_cgi.plmn_id.to_string(); - - cgi.nci = asn1_cgi.nr_cell_id.to_number(); - - return cgi; + auto plmn = plmn_identity::from_bytes(asn1_cgi.plmn_id.to_bytes()); + if (not plmn.has_value()) { + return make_unexpected(plmn.error()); + } + auto nci = nr_cell_identity::create(asn1_cgi.nr_cell_id.to_number()); + if (not nci.has_value()) { + return make_unexpected(nci.error()); + } + return nr_cell_global_id_t{plmn.value(), nci.value()}; } pdcp_sn_size srsran::pdcp_sn_size_from_f1ap_asn1(const asn1::f1ap::pdcp_sn_len_e& asn1_pdcp_sn_size) diff --git a/lib/f1ap/common/asn1_helpers.h b/lib/f1ap/common/asn1_helpers.h index 3dc04b1fc5..f85f95ee79 100644 --- a/lib/f1ap/common/asn1_helpers.h +++ b/lib/f1ap/common/asn1_helpers.h @@ -31,7 +31,7 @@ namespace srsran { /// \brief Converts ASN.1 CGI typo into internal struct. It also performs the byte to MCC/MNC conversion. /// \param[in] asn1_cgi The ASN.1 encoded NR-CGI. /// \return The CGI converted to flat internal struct. -nr_cell_global_id_t cgi_from_asn1(const asn1::f1ap::nr_cgi_s& asn1_cgi); +expected cgi_from_asn1(const asn1::f1ap::nr_cgi_s& asn1_cgi); pdcp_sn_size pdcp_sn_size_from_f1ap_asn1(const asn1::f1ap::pdcp_sn_len_e& asn1_pdcp_sn_size); asn1::f1ap::pdcp_sn_len_e pdcp_sn_size_to_f1ap_asn1(pdcp_sn_size sn_size); diff --git a/lib/f1ap/common/f1ap_asn1_utils.h b/lib/f1ap/common/f1ap_asn1_utils.h index d54a911f48..3b5d8db13f 100644 --- a/lib/f1ap/common/f1ap_asn1_utils.h +++ b/lib/f1ap/common/f1ap_asn1_utils.h @@ -354,7 +354,7 @@ inline expected get_paging_ue_identity_index_value(const asn1::f1ap::p default: break; } - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } inline expected get_paging_identity(const asn1::f1ap::paging_s& pdu) @@ -370,7 +370,7 @@ inline expected get_paging_identity(const asn1::f1ap::paging_s& pdu) default: break; } - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } inline expected get_paging_identity_type(const asn1::f1ap::paging_s& pdu) @@ -386,7 +386,7 @@ inline expected get_paging_identity_type(const asn1::f1ap: default: break; } - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } inline expected get_paging_drx_in_nof_rf(const asn1::f1ap::paging_s& pdu) @@ -404,7 +404,7 @@ inline expected get_paging_drx_in_nof_rf(const asn1::f1ap::paging_s& p default: break; } - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } inline expected get_paging_priority(const asn1::f1ap::paging_s& pdu) @@ -430,7 +430,7 @@ inline expected get_paging_priority(const asn1::f1ap::paging_s& pdu) default: break; } - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } } // namespace srsran diff --git a/lib/f1ap/cu_cp/f1ap_asn1_converters.h b/lib/f1ap/cu_cp/f1ap_asn1_converters.h index 35727e6511..020e93424e 100644 --- a/lib/f1ap/cu_cp/f1ap_asn1_converters.h +++ b/lib/f1ap/cu_cp/f1ap_asn1_converters.h @@ -95,9 +95,9 @@ inline asn1::f1ap::cause_c cause_to_asn1(f1ap_cause_t cause) /// \brief Convert F1AP NRCGI to NR Cell Identity. /// \param f1ap_cgi The F1AP NRCGI. /// \return The NR Cell Identity. -inline nr_cell_id_t f1ap_asn1_to_nr_cell_identity(asn1::f1ap::nr_cgi_s& f1ap_cgi) +inline nr_cell_identity f1ap_asn1_to_nr_cell_identity(asn1::f1ap::nr_cgi_s& f1ap_cgi) { - return f1ap_cgi.nr_cell_id.to_number(); + return nr_cell_identity::create(f1ap_cgi.nr_cell_id.to_number()).value(); } /// \brief Convert \c rlc_mode to F1AP ASN.1. @@ -374,8 +374,8 @@ inline void f1ap_scell_to_be_setup_mod_item_to_asn1(template_asn1_item& asn1_sce const f1ap_scell_to_be_setup_mod_item& scell_to_be_setup_mod_item) { // scell id - asn1_scell_to_be_setup_mod_item.scell_id.nr_cell_id.from_number(scell_to_be_setup_mod_item.scell_id.nci); - asn1_scell_to_be_setup_mod_item.scell_id.plmn_id.from_string(scell_to_be_setup_mod_item.scell_id.plmn_hex); + asn1_scell_to_be_setup_mod_item.scell_id.nr_cell_id.from_number(scell_to_be_setup_mod_item.scell_id.nci.value()); + asn1_scell_to_be_setup_mod_item.scell_id.plmn_id = scell_to_be_setup_mod_item.scell_id.plmn_id.to_bytes(); // scell idx asn1_scell_to_be_setup_mod_item.scell_idx = scell_to_be_setup_mod_item.scell_idx; @@ -708,10 +708,10 @@ inline asn1::f1ap::nr_cgi_s nr_cgi_to_f1ap_asn1(const nr_cell_global_id_t& nr_cg asn1::f1ap::nr_cgi_s asn1_nr_cgi; // nr cell id - asn1_nr_cgi.nr_cell_id.from_number(nr_cgi.nci); + asn1_nr_cgi.nr_cell_id.from_number(nr_cgi.nci.value()); // plmn id - asn1_nr_cgi.plmn_id.from_string(nr_cgi.plmn_hex); + asn1_nr_cgi.plmn_id = nr_cgi.plmn_id.to_bytes(); return asn1_nr_cgi; } @@ -724,10 +724,10 @@ inline nr_cell_global_id_t f1ap_asn1_to_nr_cgi(const asn1::f1ap::nr_cgi_s& asn1_ nr_cell_global_id_t nr_cgi; // nr cell id - nr_cgi.nci = asn1_nr_cgi.nr_cell_id.to_number(); + nr_cgi.nci = nr_cell_identity::create(asn1_nr_cgi.nr_cell_id.to_number()).value(); // plmn id - nr_cgi.plmn_hex = asn1_nr_cgi.plmn_id.to_string(); + nr_cgi.plmn_id = plmn_identity::from_bytes(asn1_nr_cgi.plmn_id.to_bytes()).value(); return nr_cgi; } diff --git a/lib/f1ap/cu_cp/f1ap_asn1_helpers.h b/lib/f1ap/cu_cp/f1ap_asn1_helpers.h index c3d293ca87..62d3d6a953 100644 --- a/lib/f1ap/cu_cp/f1ap_asn1_helpers.h +++ b/lib/f1ap/cu_cp/f1ap_asn1_helpers.h @@ -29,7 +29,7 @@ #include "srsran/asn1/f1ap/f1ap_pdu_contents.h" #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/f1ap/cu_cp/f1ap_cu_ue_context_update.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/ran/lcid.h" #include "srsran/ran/rnti.h" #include "srsran/srslog/srslog.h" @@ -46,8 +46,8 @@ inline void fill_asn1_ue_context_modification_request(asn1::f1ap::ue_context_mod // sp cell id if (request.sp_cell_id.has_value()) { asn1_request->sp_cell_id_present = true; - asn1_request->sp_cell_id.nr_cell_id.from_number(request.sp_cell_id.value().nci); - asn1_request->sp_cell_id.plmn_id.from_string(request.sp_cell_id.value().plmn_hex); + asn1_request->sp_cell_id.nr_cell_id.from_number(request.sp_cell_id.value().nci.value()); + asn1_request->sp_cell_id.plmn_id = request.sp_cell_id.value().plmn_id.to_bytes(); } // serv cell idx @@ -138,8 +138,8 @@ inline void fill_asn1_ue_context_modification_request(asn1::f1ap::ue_context_mod asn1_scell_to_be_remd_item_container.load_info_obj(ASN1_F1AP_ID_SCELL_TO_BE_REMD_ITEM); auto& asn1_scell_to_be_remd_item = asn1_scell_to_be_remd_item_container.value().scell_to_be_remd_item(); - asn1_scell_to_be_remd_item.scell_id.nr_cell_id.from_number(scell_to_be_remd_item.scell_id.nci); - asn1_scell_to_be_remd_item.scell_id.plmn_id.from_string(scell_to_be_remd_item.scell_id.plmn_hex); + asn1_scell_to_be_remd_item.scell_id.nr_cell_id.from_number(scell_to_be_remd_item.scell_id.nci.value()); + asn1_scell_to_be_remd_item.scell_id.plmn_id = scell_to_be_remd_item.scell_id.plmn_id.to_bytes(); asn1_request->scell_to_be_remd_list.push_back(asn1_scell_to_be_remd_item_container); } @@ -302,7 +302,7 @@ inline void fill_asn1_ue_context_modification_request(asn1::f1ap::ue_context_mod asn1_request->res_coordination_transfer_info_present = true; asn1_request->res_coordination_transfer_info.res_coordination_eutra_cell_info_present = false; asn1_request->res_coordination_transfer_info.m_enb_cell_id.from_number( - request.res_coordination_transfer_info.value().m_enb_cell_id); + request.res_coordination_transfer_info.value().m_enb_cell_id.value()); } // serving cell mo @@ -391,7 +391,7 @@ inline void fill_f1ap_ue_context_modification_response(f1ap_ue_context_modificat auto& asn1_scell_failed_item = asn1_scell_failed_setup_mod_list_item.value().scell_failedto_setup_mod_item(); f1ap_scell_failed_to_setup_mod_item scell_failed_item; - scell_failed_item.scell_id = cgi_from_asn1(asn1_scell_failed_item.scell_id); + scell_failed_item.scell_id = cgi_from_asn1(asn1_scell_failed_item.scell_id).value(); if (asn1_scell_failed_item.cause_present) { scell_failed_item.cause = asn1_to_cause(asn1_scell_failed_item.cause); } @@ -507,8 +507,8 @@ inline void fill_asn1_paging_message(asn1::f1ap::paging_s& asn1_paging, const cu asn1::protocol_ie_single_container_s asn1_paging_cell_item_container; auto& asn1_paging_cell_item = asn1_paging_cell_item_container->paging_cell_item(); - asn1_paging_cell_item.nr_cgi.nr_cell_id.from_number(cell_item.ngran_cgi.nci); - asn1_paging_cell_item.nr_cgi.plmn_id.from_string(cell_item.ngran_cgi.plmn_hex); + asn1_paging_cell_item.nr_cgi.nr_cell_id.from_number(cell_item.ngran_cgi.nci.value()); + asn1_paging_cell_item.nr_cgi.plmn_id = cell_item.ngran_cgi.plmn_id.to_bytes(); asn1_paging->paging_cell_list.push_back(asn1_paging_cell_item_container); } diff --git a/lib/f1ap/cu_cp/f1ap_cu_factory.cpp b/lib/f1ap/cu_cp/f1ap_cu_factory.cpp index 3330bf1dce..cf6679ecb6 100644 --- a/lib/f1ap/cu_cp/f1ap_cu_factory.cpp +++ b/lib/f1ap/cu_cp/f1ap_cu_factory.cpp @@ -30,11 +30,11 @@ using namespace srs_cu_cp; std::unique_ptr srsran::srs_cu_cp::create_f1ap(const f1ap_configuration& f1ap_cfg_, f1ap_message_notifier& f1ap_pdu_notifier_, - f1ap_du_processor_notifier& f1ap_du_processor_notifier_, + f1ap_du_processor_notifier& du_processor_notifier_, timer_manager& timers_, task_executor& ctrl_exec_) { auto f1ap_cu = - std::make_unique(f1ap_cfg_, f1ap_pdu_notifier_, f1ap_du_processor_notifier_, timers_, ctrl_exec_); + std::make_unique(f1ap_cfg_, f1ap_pdu_notifier_, du_processor_notifier_, timers_, ctrl_exec_); return f1ap_cu; } diff --git a/lib/f1ap/cu_cp/f1ap_cu_impl.cpp b/lib/f1ap/cu_cp/f1ap_cu_impl.cpp index bb90197ced..2f7d367f7f 100644 --- a/lib/f1ap/cu_cp/f1ap_cu_impl.cpp +++ b/lib/f1ap/cu_cp/f1ap_cu_impl.cpp @@ -34,7 +34,6 @@ #include "srsran/asn1/f1ap/f1ap.h" #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/f1ap/common/f1ap_message.h" -#include "srsran/ran/nr_cgi_helpers.h" using namespace srsran; using namespace asn1::f1ap; @@ -236,8 +235,8 @@ void f1ap_cu_impl::handle_initial_ul_rrc_message(const init_ul_rrc_msg_transfer_ { const gnb_du_ue_f1ap_id_t du_ue_id = int_to_gnb_du_ue_f1ap_id(msg->gnb_du_ue_f1ap_id); - nr_cell_global_id_t cgi = cgi_from_asn1(msg->nr_cgi); - if (not config_helpers::is_valid(cgi)) { + expected cgi = cgi_from_asn1(msg->nr_cgi); + if (not cgi.has_value()) { logger.warning("du_ue={}: Dropping InitialULRRCMessageTransfer. Invalid CGI", du_ue_id); return; } @@ -252,25 +251,22 @@ void f1ap_cu_impl::handle_initial_ul_rrc_message(const init_ul_rrc_msg_transfer_ logger.debug("du_ue={}: Ignoring SUL access indicator", du_ue_id); } - const gnb_cu_ue_f1ap_id_t cu_ue_f1ap_id = ue_ctxt_list.next_gnb_cu_ue_f1ap_id(); - if (cu_ue_f1ap_id == gnb_cu_ue_f1ap_id_t::invalid) { - logger.warning("du_ue={}: Dropping InitialULRRCMessageTransfer. Cause: Failed to allocate CU-UE-F1AP-ID", du_ue_id); - return; + if (msg->rrc_container_rrc_setup_complete_present) { + logger.warning("du_ue={}: Ignoring RRC Container RRCSetupComplete. Cause: Network Sharing with multiple cell-ID " + "broadcast is not supported", + du_ue_id); } - // Create CU-CP UE instance. - const ue_index_t ue_index = du_processor_notifier.on_new_cu_cp_ue_required(); - if (ue_index == ue_index_t::invalid) { - logger.warning("du_ue={}: Dropping InitialULRRCMessageTransfer. Cause: CU-CP UE creation failed", - msg->gnb_du_ue_f1ap_id); + const gnb_cu_ue_f1ap_id_t cu_ue_f1ap_id = ue_ctxt_list.allocate_gnb_cu_ue_f1ap_id(); + if (cu_ue_f1ap_id == gnb_cu_ue_f1ap_id_t::invalid) { + logger.warning("du_ue={}: Dropping InitialULRRCMessageTransfer. Cause: Failed to allocate CU-UE-F1AP-ID", du_ue_id); return; } - // Update the UE RRC context (e.g. C-RNTI, PCell) in the CU-CP. + // Request RRC UE creation in the DU processor. ue_rrc_context_creation_request req; - req.ue_index = ue_index; - req.c_rnti = crnti; - req.cgi = cgi; + req.c_rnti = crnti; + req.cgi = cgi.value(); if (msg->du_to_cu_rrc_container_present) { req.du_to_cu_rrc_container = byte_buffer(msg->du_to_cu_rrc_container); } else { @@ -281,33 +277,37 @@ void f1ap_cu_impl::handle_initial_ul_rrc_message(const init_ul_rrc_msg_transfer_ du_ue_id); req.du_to_cu_rrc_container = byte_buffer{}; } - const ue_rrc_context_creation_response resp = du_processor_notifier.on_ue_rrc_context_creation_request(req); - - // Remove the UE if the creation was not successful - if (resp.f1ap_rrc_notifier == nullptr) { - logger.warning("du_ue={}: Dropping InitialULRRCMessageTransfer. Cause: UE RRC context creation failed", - msg->gnb_du_ue_f1ap_id); + ue_rrc_context_creation_outcome resp = du_processor_notifier.on_ue_rrc_context_creation_request(req); + + // Reject the UE if the creation was not successful + if (not resp.has_value()) { + 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(cu_ue_f1ap_id); + dl_rrc_msg->gnb_du_ue_f1ap_id = gnb_du_ue_f1ap_id_to_uint(du_ue_id); + dl_rrc_msg->srb_id = srb_id_to_uint(srb_id_t::srb0); + dl_rrc_msg->rrc_container = resp.error().copy(); + + // Pack message into PDU + f1ap_message f1ap_dl_rrc_msg; + f1ap_dl_rrc_msg.pdu.set_init_msg(); + f1ap_dl_rrc_msg.pdu.init_msg().load_info_obj(ASN1_F1AP_ID_DL_RRC_MSG_TRANSFER); + f1ap_dl_rrc_msg.pdu.init_msg().value.dl_rrc_msg_transfer() = std::move(dl_rrc_msg); + + // send DL RRC message + tx_pdu_notifier.on_new_message(f1ap_dl_rrc_msg); return; } // Create UE context and store it - ue_ctxt_list.add_ue(ue_index, cu_ue_f1ap_id); + ue_ctxt_list.add_ue(resp->ue_index, cu_ue_f1ap_id); ue_ctxt_list.add_du_ue_f1ap_id(cu_ue_f1ap_id, du_ue_id); - ue_ctxt_list.add_rrc_notifier(ue_index, resp.f1ap_rrc_notifier); + ue_ctxt_list.add_rrc_notifier(resp->ue_index, resp->f1ap_rrc_notifier); f1ap_ue_context& ue_ctxt = ue_ctxt_list[cu_ue_f1ap_id]; ue_ctxt.logger.log_info("Added UE context"); - // Forward RRC container - if (msg->rrc_container_rrc_setup_complete_present) { - // RRC setup complete over SRB1 - ue_ctxt_list[cu_ue_f1ap_id].rrc_notifier->on_ul_dcch_pdu(srb_id_t::srb1, - msg->rrc_container_rrc_setup_complete.copy()); - return; - } - - // Pass RRC container to RRC - ue_ctxt_list[cu_ue_f1ap_id].rrc_notifier->on_ul_ccch_pdu(msg->rrc_container.copy()); + // Forward RRC container to RRC UE + ue_ctxt.rrc_notifier->on_ul_ccch_pdu(byte_buffer(msg->rrc_container)); } void f1ap_cu_impl::handle_ul_rrc_message(const ul_rrc_msg_transfer_s& msg) diff --git a/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp b/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp index 71ebe4ab07..c3c0c3490d 100644 --- a/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp +++ b/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp @@ -28,7 +28,7 @@ #include "srsran/f1ap/common/f1ap_message.h" #include "srsran/f1ap/cu_cp/du_setup_notifier.h" #include "srsran/f1ap/cu_cp/f1ap_du_context.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/ran/cause/f1ap_cause.h" using namespace srsran; @@ -40,10 +40,10 @@ validate_f1_setup_request(const asn1::f1ap::f1_setup_request_s& request) asn1::f1ap::cause_c cause; cause.set_protocol().value = asn1::f1ap::cause_protocol_opts::unspecified; if (not request->gnb_du_name_present) { - return {std::make_pair(cause, "Missing gNB-DU name")}; + return make_unexpected(std::make_pair(cause, "Missing gNB-DU name")); } if (not request->gnb_du_served_cells_list_present or request->gnb_du_served_cells_list.size() == 0) { - return {std::make_pair(cause, "DU has no served cells")}; + return make_unexpected(std::make_pair(cause, "DU has no served cells")); } return {}; @@ -70,7 +70,7 @@ du_setup_request srsran::srs_cu_cp::create_du_setup_request(const asn1::f1ap::f1 // served cell info // NR CGI - served_cell.served_cell_info.nr_cgi = cgi_from_asn1(asn1_served_cell.served_cell_info.nr_cgi); + served_cell.served_cell_info.nr_cgi = cgi_from_asn1(asn1_served_cell.served_cell_info.nr_cgi).value(); // NR PCI served_cell.served_cell_info.nr_pci = asn1_served_cell.served_cell_info.nr_pci; @@ -148,8 +148,8 @@ static f1ap_message create_f1_setup_response(const asn1::f1ap::f1_setup_request_ resp->cells_to_be_activ_list_present = true; for (const auto& du_cell : cu_response.cells_to_be_activ_list) { asn1::protocol_ie_single_container_s resp_cell; - resp_cell->cells_to_be_activ_list_item().nr_cgi.plmn_id.from_number(plmn_string_to_bcd(du_cell.nr_cgi.plmn)); - resp_cell->cells_to_be_activ_list_item().nr_cgi.nr_cell_id.from_number(du_cell.nr_cgi.nci); + resp_cell->cells_to_be_activ_list_item().nr_cgi.plmn_id = du_cell.nr_cgi.plmn_id.to_bytes(); + resp_cell->cells_to_be_activ_list_item().nr_cgi.nr_cell_id.from_number(du_cell.nr_cgi.nci.value()); if (du_cell.nr_pci.has_value()) { resp_cell->cells_to_be_activ_list_item().nr_pci_present = true; @@ -185,7 +185,7 @@ void srsran::srs_cu_cp::handle_f1_setup_procedure(const asn1::f1ap::f1_setup_req { // Message content validation. auto msgerr = validate_f1_setup_request(request); - if (msgerr.is_error()) { + if (not msgerr.has_value()) { logger.info("Rejecting F1 Setup Request. Cause: {}", msgerr.error().second); pdu_notifier.on_new_message(create_f1_setup_reject(request, msgerr.error().first)); return; 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 be637aad34..5c77ae4634 100644 --- a/lib/f1ap/cu_cp/procedures/ue_context_setup_procedure.cpp +++ b/lib/f1ap/cu_cp/procedures/ue_context_setup_procedure.cpp @@ -106,7 +106,7 @@ bool ue_context_setup_procedure::find_or_create_f1ap_ue_context() // F1AP UE context does not yet exist. // Allocate gNB-CU-UE-F1AP-ID. - gnb_cu_ue_f1ap_id_t tmp_cu_ue_f1ap_id = ue_ctxt_list.next_gnb_cu_ue_f1ap_id(); + gnb_cu_ue_f1ap_id_t tmp_cu_ue_f1ap_id = ue_ctxt_list.allocate_gnb_cu_ue_f1ap_id(); if (tmp_cu_ue_f1ap_id == gnb_cu_ue_f1ap_id_t::invalid) { logger.warning("ue={} proc=\"{}\": No CU UE F1AP ID available", request.ue_index, name()); return false; @@ -136,16 +136,16 @@ bool ue_context_setup_procedure::create_ue_rrc_context(const f1ap_ue_context_set req.du_to_cu_rrc_container = ue_ctxt_setup_resp.du_to_cu_rrc_info.cell_group_cfg.copy(); req.prev_context = std::move(rrc_context); - ue_rrc_context_creation_response resp = du_processor_notifier.on_ue_rrc_context_creation_request(req); - if (resp.f1ap_rrc_notifier == nullptr) { + ue_rrc_context_creation_outcome outcome = du_processor_notifier.on_ue_rrc_context_creation_request(req); + if (not outcome.has_value()) { logger.warning("Couldn't create UE RRC context in target cell"); return false; } // Add RRC notifier to F1AP UE context. - ue_ctxt_list.add_rrc_notifier(req.ue_index, resp.f1ap_rrc_notifier); + ue_ctxt_list.add_rrc_notifier(outcome->ue_index, outcome->f1ap_rrc_notifier); - logger.debug("ue={} Added RRC UE notifier", req.ue_index); + logger.debug("ue={} Added RRC UE notifier", outcome->ue_index); } return true; @@ -370,7 +370,7 @@ static void fill_asn1_ue_context_setup_request(asn1::f1ap::ue_context_setup_requ asn1_request->res_coordination_transfer_info_present = true; asn1_request->res_coordination_transfer_info.res_coordination_eutra_cell_info_present = false; asn1_request->res_coordination_transfer_info.m_enb_cell_id.from_number( - request.res_coordination_transfer_info.value().m_enb_cell_id); + request.res_coordination_transfer_info.value().m_enb_cell_id.value()); } // serving cell mo 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 e32d52ed68..d24839b178 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 @@ -171,10 +171,10 @@ class f1ap_ue_context_list size_t size() const { return ues.size(); } /// \brief Get the next available GNB-CU-F1AP-UE-ID. - gnb_cu_ue_f1ap_id_t next_gnb_cu_ue_f1ap_id() + gnb_cu_ue_f1ap_id_t allocate_gnb_cu_ue_f1ap_id() { // return invalid when no cu ue f1ap id is available - if (ue_index_to_ue_f1ap_id.size() == MAX_NOF_UES_PER_DU) { + if (ue_index_to_ue_f1ap_id.size() == MAX_NOF_CU_F1AP_UES) { return gnb_cu_ue_f1ap_id_t::invalid; } @@ -189,8 +189,7 @@ class f1ap_ue_context_list // Find holes in the allocated IDs by iterating over all ids starting with the next_cu_ue_f1ap_id to find the // available id while (true) { - // Only iterate over ue_index_to_ue_f1ap_id (size=MAX_NOF_UES_PER_DU) - // to avoid iterating over all possible values of gnb_cu_ue_f1ap_id_t (size=2^32-1) + // Iterate over ue_index_to_ue_f1ap_id auto it = std::find_if(ue_index_to_ue_f1ap_id.begin(), ue_index_to_ue_f1ap_id.end(), [this](auto& u) { return u.second == next_cu_ue_f1ap_id; }); diff --git a/lib/f1ap/du/f1ap_du_impl.cpp b/lib/f1ap/du/f1ap_du_impl.cpp index d91231e6d9..749993c542 100644 --- a/lib/f1ap/du/f1ap_du_impl.cpp +++ b/lib/f1ap/du/f1ap_du_impl.cpp @@ -417,7 +417,7 @@ void f1ap_du_impl::handle_unsuccessful_outcome(const asn1::f1ap::unsuccessful_ou } // Set transaction result and resume suspended procedure. - if (not events->transactions.set_response(transaction_id.value(), outcome)) { + if (not events->transactions.set_response(transaction_id.value(), make_unexpected(outcome))) { logger.warning("Unexpected transaction id={}", transaction_id.value()); } } @@ -502,14 +502,19 @@ void f1ap_du_impl::handle_paging_request(const asn1::f1ap::paging_s& msg) info.is_paging_origin_non_3gpp_access = true; } for (const auto& asn_nr_cgi : msg->paging_cell_list) { - const auto paging_cell_cgi = cgi_from_asn1(asn_nr_cgi->paging_cell_item().nr_cgi); + const auto ret = cgi_from_asn1(asn_nr_cgi->paging_cell_item().nr_cgi); + if (not ret.has_value()) { + logger.error("Invalid CGI in paging cell list"); + continue; + } + auto paging_cell_cgi = ret.value(); const auto du_cell_it = std::find_if(ctxt.served_cells.cbegin(), ctxt.served_cells.cend(), [&paging_cell_cgi](const f1ap_du_cell_context& cell) { return paging_cell_cgi == cell.nr_cgi; }); // Cell not served by this DU. if (du_cell_it == ctxt.served_cells.cend()) { - logger.error("Cell with PLMN={} and NCI={} not handled by DU", paging_cell_cgi.plmn, paging_cell_cgi.nci); + logger.error("Cell with PLMN={} and NCI={} not handled by DU", paging_cell_cgi.plmn_id, paging_cell_cgi.nci); continue; } info.paging_cells.push_back(to_du_cell_index(std::distance(ctxt.served_cells.cbegin(), du_cell_it))); diff --git a/lib/f1ap/du/procedures/f1ap_du_setup_procedure.cpp b/lib/f1ap/du/procedures/f1ap_du_setup_procedure.cpp index ae81622d99..1c92880c08 100644 --- a/lib/f1ap/du/procedures/f1ap_du_setup_procedure.cpp +++ b/lib/f1ap/du/procedures/f1ap_du_setup_procedure.cpp @@ -25,7 +25,7 @@ #include "srsran/asn1/f1ap/common.h" #include "srsran/f1ap/common/f1ap_message.h" #include "srsran/ran/band_helper.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/support/async/async_timer.h" using namespace srsran; @@ -110,12 +110,13 @@ void f1ap_du_setup_procedure::send_f1_setup_request() // Fill Served PLMNs f1ap_cell.served_cell_info.served_plmns.resize(1); - f1ap_cell.served_cell_info.served_plmns[0].plmn_id.from_number(plmn_string_to_bcd(cell_cfg.nr_cgi.plmn)); + auto plmn_bytes = cell_cfg.nr_cgi.plmn_id.to_bytes(); + f1ap_cell.served_cell_info.served_plmns[0].plmn_id = plmn_bytes; // Fill Served Cell Information. - f1ap_cell.served_cell_info.nr_pci = cell_cfg.pci; - f1ap_cell.served_cell_info.nr_cgi.plmn_id.from_number(plmn_string_to_bcd(cell_cfg.nr_cgi.plmn)); - f1ap_cell.served_cell_info.nr_cgi.nr_cell_id.from_number(cell_cfg.nr_cgi.nci); + f1ap_cell.served_cell_info.nr_pci = cell_cfg.pci; + f1ap_cell.served_cell_info.nr_cgi.plmn_id = plmn_bytes; + f1ap_cell.served_cell_info.nr_cgi.nr_cell_id.from_number(cell_cfg.nr_cgi.nci.value()); f1ap_cell.served_cell_info.five_gs_tac_present = true; f1ap_cell.served_cell_info.five_gs_tac.from_number(cell_cfg.tac); if (cell_cfg.duplx_mode == duplex_mode::TDD) { diff --git a/lib/f1ap/du/ue_context/f1c_du_bearer_impl.cpp b/lib/f1ap/du/ue_context/f1c_du_bearer_impl.cpp index a49e163756..634a9e31ad 100644 --- a/lib/f1ap/du/ue_context/f1c_du_bearer_impl.cpp +++ b/lib/f1ap/du/ue_context/f1c_du_bearer_impl.cpp @@ -25,7 +25,7 @@ #include "srsran/asn1/f1ap/common.h" #include "srsran/asn1/f1ap/f1ap_pdu_contents.h" #include "srsran/f1ap/common/f1ap_message.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" using namespace srsran::srs_du; @@ -61,8 +61,8 @@ void f1c_srb0_du_bearer::handle_sdu(byte_buffer_chain sdu) msg.pdu.set_init_msg().load_info_obj(ASN1_F1AP_ID_INIT_UL_RRC_MSG_TRANSFER); asn1::f1ap::init_ul_rrc_msg_transfer_s& init_msg = msg.pdu.init_msg().value.init_ul_rrc_msg_transfer(); init_msg->gnb_du_ue_f1ap_id = gnb_du_ue_f1ap_id_to_uint(ue_ctxt.gnb_du_ue_f1ap_id); - init_msg->nr_cgi.plmn_id.from_number(plmn_string_to_bcd(nr_cgi.plmn)); - init_msg->nr_cgi.nr_cell_id.from_number(nr_cgi.nci); + init_msg->nr_cgi.plmn_id = nr_cgi.plmn_id.to_bytes(); + init_msg->nr_cgi.nr_cell_id.from_number(nr_cgi.nci.value()); init_msg->c_rnti = to_value(ue_ctxt.rnti); if (not init_msg->rrc_container.append(sdu.begin(), sdu.end())) { logger.error("UL {} SRB0 Tx PDU: Discarding Tx PDU. Cause: Failed to append SDU to RRC container.", ue_ctxt); diff --git a/lib/f1u/cu_up/f1u_bearer_impl.cpp b/lib/f1u/cu_up/f1u_bearer_impl.cpp index 93e126c5d8..af3c30d4d3 100644 --- a/lib/f1u/cu_up/f1u_bearer_impl.cpp +++ b/lib/f1u/cu_up/f1u_bearer_impl.cpp @@ -89,29 +89,32 @@ void f1u_bearer_impl::handle_pdu_impl(nru_ul_message msg) logger.log_debug("Notifying highest successfully delivered pdcp_sn={}", pdcp_sn); rx_delivery_notifier.on_delivery_notification(pdcp_sn); } - // Highest successfully delivered retransmitted PDCP SN - if (status.highest_delivered_retransmitted_pdcp_sn.has_value()) { - uint32_t pdcp_sn = status.highest_delivered_retransmitted_pdcp_sn.value(); - logger.log_warning("Unhandled highest successfully delivered retransmitted pdcp_sn={}", pdcp_sn); - // TODO - } // Highest retransmitted PDCP SN if (status.highest_retransmitted_pdcp_sn.has_value()) { uint32_t pdcp_sn = status.highest_retransmitted_pdcp_sn.value(); - logger.log_warning("Unhandled highest retransmitted pdcp_sn={}", pdcp_sn); - // TODO + logger.log_debug("Notifying highest retransmitted pdcp_sn={}", pdcp_sn); + rx_delivery_notifier.on_retransmit_notification(pdcp_sn); + } + // Highest successfully delivered retransmitted PDCP SN + if (status.highest_delivered_retransmitted_pdcp_sn.has_value()) { + uint32_t pdcp_sn = status.highest_delivered_retransmitted_pdcp_sn.value(); + logger.log_debug("Notifying highest successfully delivered retransmitted pdcp_sn={}", pdcp_sn); + rx_delivery_notifier.on_delivery_retransmitted_notification(pdcp_sn); } } } -void f1u_bearer_impl::handle_sdu(byte_buffer sdu) +void f1u_bearer_impl::handle_sdu(byte_buffer sdu, bool is_retx) { - logger.log_debug("F1-U bearer received SDU. size={}", sdu.length()); + logger.log_debug("F1-U bearer received SDU. size={} is_retx={}", sdu.length(), is_retx); nru_dl_message msg = {}; // attach the SDU msg.t_pdu = std::move(sdu); + // set retransmission flag + msg.dl_user_data.retransmission_flag = is_retx; + // attach discard blocks (if any) fill_discard_blocks(msg); diff --git a/lib/f1u/cu_up/f1u_bearer_impl.h b/lib/f1u/cu_up/f1u_bearer_impl.h index 812fe25b64..9942f55c0c 100644 --- a/lib/f1u/cu_up/f1u_bearer_impl.h +++ b/lib/f1u/cu_up/f1u_bearer_impl.h @@ -58,7 +58,7 @@ class f1u_bearer_impl final : public f1u_bearer, public f1u_rx_pdu_handler, publ void stop() override { dl_notif_timer.stop(); } void handle_pdu(nru_ul_message msg) override; - void handle_sdu(byte_buffer sdu) override; + void handle_sdu(byte_buffer sdu, bool is_retx) override; void discard_sdu(uint32_t pdcp_sn) override; /// \brief Returns the UL tunnel info that was assigned upon construction. diff --git a/lib/f1u/du/f1u_bearer_impl.cpp b/lib/f1u/du/f1u_bearer_impl.cpp index 17c6d01f02..851e30b2b7 100644 --- a/lib/f1u/du/f1u_bearer_impl.cpp +++ b/lib/f1u/du/f1u_bearer_impl.cpp @@ -87,7 +87,7 @@ void f1u_bearer_impl::handle_pdu_impl(nru_dl_message msg) // handle T-PDU if (!msg.t_pdu.empty()) { logger.log_debug("Delivering T-PDU. size={}", msg.t_pdu.length()); - rx_sdu_notifier.on_new_sdu(std::move(msg.t_pdu)); + rx_sdu_notifier.on_new_sdu(std::move(msg.t_pdu), msg.dl_user_data.retransmission_flag); } // handle discard notifications if (msg.dl_user_data.discard_blocks.has_value()) { @@ -118,6 +118,20 @@ void f1u_bearer_impl::handle_delivery_notification(uint32_t highest_pdcp_sn) highest_delivered_pdcp_sn.store(highest_pdcp_sn, std::memory_order_relaxed); } +void f1u_bearer_impl::handle_retransmit_notification(uint32_t highest_pdcp_sn) +{ + // This function may be called from pcell_executor, since it only writes to an atomic variable + logger.log_debug("Storing highest retransmitted pdcp_sn={}", highest_pdcp_sn); + highest_retransmitted_pdcp_sn.store(highest_pdcp_sn, std::memory_order_relaxed); +} + +void f1u_bearer_impl::handle_delivery_retransmitted_notification(uint32_t highest_pdcp_sn) +{ + // This function may be called from pcell_executor, since it only writes to an atomic variable + logger.log_debug("Storing highest successfully delivered retransmitted pdcp_sn={}", highest_pdcp_sn); + highest_delivered_retransmitted_pdcp_sn.store(highest_pdcp_sn, std::memory_order_relaxed); +} + bool f1u_bearer_impl::fill_highest_transmitted_pdcp_sn(nru_dl_data_delivery_status& status) { uint32_t cur_highest_transmitted_pdcp_sn = highest_transmitted_pdcp_sn.load(std::memory_order_relaxed); @@ -142,6 +156,31 @@ bool f1u_bearer_impl::fill_highest_delivered_pdcp_sn(nru_dl_data_delivery_status return false; } +bool f1u_bearer_impl::fill_highest_retransmitted_pdcp_sn(nru_dl_data_delivery_status& status) +{ + uint32_t cur_highest_retransmitted_pdcp_sn = highest_retransmitted_pdcp_sn.load(std::memory_order_relaxed); + if (cur_highest_retransmitted_pdcp_sn != notif_highest_retransmitted_pdcp_sn) { + logger.log_debug("Adding highest retransmitted pdcp_sn={}", cur_highest_retransmitted_pdcp_sn); + notif_highest_retransmitted_pdcp_sn = cur_highest_retransmitted_pdcp_sn; + status.highest_retransmitted_pdcp_sn = cur_highest_retransmitted_pdcp_sn; + return true; + } + return false; +} + +bool f1u_bearer_impl::fill_highest_delivered_retransmitted_pdcp_sn(nru_dl_data_delivery_status& status) +{ + uint32_t cur_highest_delivered_retransmitted_pdcp_sn = + highest_delivered_retransmitted_pdcp_sn.load(std::memory_order_relaxed); + if (cur_highest_delivered_retransmitted_pdcp_sn != notif_highest_delivered_retransmitted_pdcp_sn) { + logger.log_debug("Adding highest delivered retransmitted pdcp_sn={}", cur_highest_delivered_retransmitted_pdcp_sn); + notif_highest_delivered_retransmitted_pdcp_sn = cur_highest_delivered_retransmitted_pdcp_sn; + status.highest_delivered_retransmitted_pdcp_sn = cur_highest_delivered_retransmitted_pdcp_sn; + return true; + } + return false; +} + void f1u_bearer_impl::fill_data_delivery_status(nru_ul_message& msg) { nru_dl_data_delivery_status status = {}; @@ -149,6 +188,8 @@ void f1u_bearer_impl::fill_data_delivery_status(nru_ul_message& msg) status_changed |= fill_highest_transmitted_pdcp_sn(status); status_changed |= fill_highest_delivered_pdcp_sn(status); + status_changed |= fill_highest_retransmitted_pdcp_sn(status); + status_changed |= fill_highest_delivered_retransmitted_pdcp_sn(status); if (status_changed) { logger.log_debug("Adding data delivery status to NR-U message"); diff --git a/lib/f1u/du/f1u_bearer_impl.h b/lib/f1u/du/f1u_bearer_impl.h index 4401aad7b0..fd30fa8d32 100644 --- a/lib/f1u/du/f1u_bearer_impl.h +++ b/lib/f1u/du/f1u_bearer_impl.h @@ -58,6 +58,8 @@ class f1u_bearer_impl final : public f1u_bearer, void handle_sdu(byte_buffer_chain sdu) override; void handle_transmit_notification(uint32_t highest_pdcp_sn) override; void handle_delivery_notification(uint32_t highest_pdcp_sn) override; + void handle_retransmit_notification(uint32_t highest_pdcp_sn) override; + void handle_delivery_retransmitted_notification(uint32_t highest_pdcp_sn) override; void handle_pdu(nru_dl_message msg) override; void stop() override; @@ -82,17 +84,30 @@ class f1u_bearer_impl final : public f1u_bearer, /// layers. The purpose of this timer is to avoid excessive uplink notifications for every PDCP SN that is notified by /// lower layers. unique_timer ul_notif_timer; + /// Holds the most recent highest transmitted PDCP SN that is frequently updated by lower layers (i.e. by RLC AM/UM) std::atomic highest_transmitted_pdcp_sn{unset_pdcp_sn}; /// Holds the most recent highest delivered PDCP SN that is frequently updated by lower layers (i.e. by RLC AM) std::atomic highest_delivered_pdcp_sn{unset_pdcp_sn}; + /// Holds the most recent highest retransmitted PDCP SN that is frequently updated by lower layers (i.e. by RLC AM) + std::atomic highest_retransmitted_pdcp_sn{unset_pdcp_sn}; + /// Holds the most recent highest delivered retransmitted PDCP SN that is frequently updated by lower layers (i.e. by + /// RLC AM) + std::atomic highest_delivered_retransmitted_pdcp_sn{unset_pdcp_sn}; + /// Holds the last highest transmitted PDCP SN that was reported to upper layers (i.e. towards CU-UP) uint32_t notif_highest_transmitted_pdcp_sn = unset_pdcp_sn; /// Holds the last highest delivered PDCP SN that was reported to upper layers (i.e. towards CU-UP) uint32_t notif_highest_delivered_pdcp_sn = unset_pdcp_sn; + /// Holds the last highest retransmitted PDCP SN that was reported to upper layers (i.e. towards CU-UP) + uint32_t notif_highest_retransmitted_pdcp_sn = unset_pdcp_sn; + /// Holds the last highest delivered retransmitted PDCP SN that was reported to upper layers (i.e. towards CU-UP) + uint32_t notif_highest_delivered_retransmitted_pdcp_sn = unset_pdcp_sn; bool fill_highest_transmitted_pdcp_sn(nru_dl_data_delivery_status& status); bool fill_highest_delivered_pdcp_sn(nru_dl_data_delivery_status& status); + bool fill_highest_retransmitted_pdcp_sn(nru_dl_data_delivery_status& status); + bool fill_highest_delivered_retransmitted_pdcp_sn(nru_dl_data_delivery_status& status); void fill_data_delivery_status(nru_ul_message& msg); void handle_pdu_impl(nru_dl_message msg); diff --git a/lib/f1u/du/split_connector/f1u_split_connector.cpp b/lib/f1u/du/split_connector/f1u_split_connector.cpp index 36441a55bf..7e945e4c88 100644 --- a/lib/f1u/du/split_connector/f1u_split_connector.cpp +++ b/lib/f1u/du/split_connector/f1u_split_connector.cpp @@ -90,7 +90,7 @@ expected f1u_split_connector::get_du_bind_address(gnb_du_id_t gnb_d { std::string ip_address; if (not udp_session->get_bind_address(ip_address)) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } return ip_address; } diff --git a/lib/fapi/validators/message_validators.cpp b/lib/fapi/validators/message_validators.cpp index 5e7a41332b..24093d2af7 100644 --- a/lib/fapi/validators/message_validators.cpp +++ b/lib/fapi/validators/message_validators.cpp @@ -96,7 +96,7 @@ error_type srsran::fapi::validate_dl_tti_request(const dl_tti_ // Build the result. if (!success) { - return {std::move(report)}; + return make_unexpected(std::move(report)); } return {}; @@ -125,7 +125,7 @@ error_type srsran::fapi::validate_ul_dci_request(const ul_dci_ // Build the result. if (!success) { - return {std::move(report)}; + return make_unexpected(std::move(report)); } return {}; @@ -167,7 +167,7 @@ error_type srsran::fapi::validate_tx_data_request(const tx_dat // Build the result. if (!success) { - return {std::move(report)}; + return make_unexpected(std::move(report)); } return {}; @@ -291,7 +291,7 @@ error_type srsran::fapi::validate_crc_indication(const crc_ind // Build the result. if (!success) { - return {std::move(report)}; + return make_unexpected(std::move(report)); } return {}; @@ -440,7 +440,7 @@ error_type srsran::fapi::validate_rach_indication(const rach_i // Build the result. if (!success) { - return {std::move(report)}; + return make_unexpected(std::move(report)); } return {}; @@ -487,7 +487,7 @@ error_type srsran::fapi::validate_srs_indication(const srs_ind // Build the result. if (!success) { - return {std::move(report)}; + return make_unexpected(std::move(report)); } return {}; @@ -522,7 +522,7 @@ error_type srsran::fapi::validate_uci_indication(const uci_ind // Build the result. if (!success) { - return {std::move(report)}; + return make_unexpected(std::move(report)); } return {}; @@ -628,7 +628,7 @@ error_type srsran::fapi::validate_error_indication(const error // Build the result. if (!success) { - return {std::move(report)}; + return make_unexpected(std::move(report)); } return {}; @@ -688,7 +688,7 @@ error_type srsran::fapi::validate_rx_data_indication(const rx_ // Build the result. if (!success) { - return {std::move(report)}; + return make_unexpected(std::move(report)); } return {}; @@ -707,7 +707,7 @@ error_type srsran::fapi::validate_slot_indication(const slot_i // Build the result. if (!success) { - return {std::move(report)}; + return make_unexpected(std::move(report)); } return {}; @@ -744,7 +744,7 @@ error_type srsran::fapi::validate_ul_tti_request(const ul_tti_ // Build the result. if (!success) { - return {std::move(report)}; + return make_unexpected(std::move(report)); } return {}; diff --git a/lib/fapi_adaptor/mac/fapi_to_mac_data_msg_translator.cpp b/lib/fapi_adaptor/mac/fapi_to_mac_data_msg_translator.cpp index dae7221c63..79fb186aba 100644 --- a/lib/fapi_adaptor/mac/fapi_to_mac_data_msg_translator.cpp +++ b/lib/fapi_adaptor/mac/fapi_to_mac_data_msg_translator.cpp @@ -116,7 +116,7 @@ void fapi_to_mac_data_msg_translator::on_rx_data_indication(const fapi::rx_data_ } auto pdu_buffer = byte_buffer::create(span(fapi_pdu.data, fapi_pdu.pdu_length)); - if (pdu_buffer.is_error()) { + if (not pdu_buffer.has_value()) { srslog::fetch_basic_logger("FAPI").warning("Unable to allocate memory for MAC RX PDU"); // Avoid new buffer allocations for the same FAPI PDU. break; diff --git a/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp b/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp index 63c48391df..b185f7d560 100644 --- a/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp +++ b/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp @@ -229,7 +229,7 @@ static expected translate_dl_tti_pdus_to_phy_pdus(const fapi::dl_ if (pdu.csi_rs_pdu.type != csi_rs_type::CSI_RS_NZP && pdu.csi_rs_pdu.type != csi_rs_type::CSI_RS_ZP) { logger.warning("Only NZP-CSI-RS and ZP-CSI-RS PDU types are supported. Skipping DL_TTI.request"); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } // ZP-CSI does not need any further work to do. if (pdu.csi_rs_pdu.type == csi_rs_type::CSI_RS_ZP) { @@ -240,7 +240,7 @@ static expected translate_dl_tti_pdus_to_phy_pdus(const fapi::dl_ if (!dl_pdu_validator.is_valid(csi_pdu)) { logger.warning("Upper PHY flagged a CSI-RS PDU as having an invalid configuration. Skipping DL_TTI.request"); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } break; } @@ -254,7 +254,7 @@ static expected translate_dl_tti_pdus_to_phy_pdus(const fapi::dl_ "Skipping DL_TTI.request", i_dci); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } } break; @@ -265,7 +265,7 @@ static expected translate_dl_tti_pdus_to_phy_pdus(const fapi::dl_ if (!dl_pdu_validator.is_valid(pdsch_pdu)) { logger.warning("Upper PHY flagged a PDSCH PDU as having an invalid configuration. Skipping DL_TTI.request"); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } break; } @@ -275,7 +275,7 @@ static expected translate_dl_tti_pdus_to_phy_pdus(const fapi::dl_ if (!dl_pdu_validator.is_valid(ssb_pdu)) { logger.warning("Upper PHY flagged a SSB PDU as having an invalid configuration. Skipping DL_TTI.request"); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } break; } @@ -418,7 +418,7 @@ static expected translate_ul_tti_pdus_to_phy_pdus(const fapi::ul_tt logger.warning( "Upper PHY flagged a PRACH PDU as having an invalid configuration. Skipping UL_TTI.request in slot"); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } break; @@ -429,7 +429,7 @@ static expected translate_ul_tti_pdus_to_phy_pdus(const fapi::ul_tt if (!is_pucch_pdu_valid(ul_pdu_validator, ul_pdu)) { logger.warning("Upper PHY flagged a PUCCH PDU as having an invalid configuration. Skipping UL_TTI.request"); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } break; @@ -440,7 +440,7 @@ static expected translate_ul_tti_pdus_to_phy_pdus(const fapi::ul_tt if (!ul_pdu_validator.is_valid(ul_pdu.pdu)) { logger.warning("Upper PHY flagged a PUSCH PDU as having an invalid configuration. Skipping UL_TTI.request"); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } break; } @@ -450,7 +450,7 @@ static expected translate_ul_tti_pdus_to_phy_pdus(const fapi::ul_tt 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"); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } break; } diff --git a/lib/gateways/sctp_network_client_impl.cpp b/lib/gateways/sctp_network_client_impl.cpp index 49396b046e..bdb39d8a63 100644 --- a/lib/gateways/sctp_network_client_impl.cpp +++ b/lib/gateways/sctp_network_client_impl.cpp @@ -196,7 +196,7 @@ sctp_network_client_impl::connect_to(const std::string& if (not reuse_socket) { // Create SCTP socket only if not created earlier through bind or another connection. expected outcome = create_socket(candidate->ai_family, candidate->ai_socktype); - if (outcome.is_error()) { + if (not outcome.has_value()) { if (errno == ESOCKTNOSUPPORT) { // Stop the search. break; diff --git a/lib/gateways/sctp_network_gateway_common_impl.cpp b/lib/gateways/sctp_network_gateway_common_impl.cpp index 63f92589d9..8d0ddd2fc8 100644 --- a/lib/gateways/sctp_network_gateway_common_impl.cpp +++ b/lib/gateways/sctp_network_gateway_common_impl.cpp @@ -112,7 +112,7 @@ bool sctp_network_gateway_common_impl::create_and_bind_common() for (result = searcher.next(); result != nullptr; result = searcher.next()) { // create SCTP socket auto outcome = this->create_socket(result->ai_family, result->ai_socktype); - if (outcome.is_error()) { + if (not outcome.has_value()) { if (errno == ESOCKTNOSUPPORT) { // There is no support for this type of socket. Stop search. break; diff --git a/lib/gateways/sctp_network_gateway_impl.cpp b/lib/gateways/sctp_network_gateway_impl.cpp index 61f0f3b629..a481ff8f0d 100644 --- a/lib/gateways/sctp_network_gateway_impl.cpp +++ b/lib/gateways/sctp_network_gateway_impl.cpp @@ -74,7 +74,7 @@ bool sctp_network_gateway_impl::create_and_connect() // Create SCTP socket only if not created in create_and_bind function. if (not socket.is_open()) { expected outcome = create_socket(result->ai_family, result->ai_socktype); - if (outcome.is_error()) { + if (not outcome.has_value()) { if (errno == ESOCKTNOSUPPORT) { break; } diff --git a/lib/gtpu/gtpu_demux_impl.cpp b/lib/gtpu/gtpu_demux_impl.cpp index f6c4d073c4..5289569782 100644 --- a/lib/gtpu/gtpu_demux_impl.cpp +++ b/lib/gtpu/gtpu_demux_impl.cpp @@ -89,7 +89,7 @@ void gtpu_demux_impl::handle_pdu_impl(gtpu_teid_t teid, byte_buffer pdu, const s { if (gtpu_pcap.is_write_enabled()) { auto pdu_copy = pdu.deep_copy(); - if (pdu_copy.is_error()) { + if (not pdu_copy.has_value()) { logger.warning("Unable to deep copy PDU for PCAP writer"); } else { gtpu_pcap.push_pdu(std::move(pdu_copy.value())); diff --git a/lib/gtpu/gtpu_teid_pool_impl.h b/lib/gtpu/gtpu_teid_pool_impl.h index 8ae38e8148..e2de35a4c1 100644 --- a/lib/gtpu/gtpu_teid_pool_impl.h +++ b/lib/gtpu/gtpu_teid_pool_impl.h @@ -40,8 +40,7 @@ class gtpu_teid_pool_impl final : public gtpu_teid_pool SRSRAN_NODISCARD expected request_teid() override { - expected teid; - teid.set_error(default_error_t{}); + expected teid = make_unexpected(default_error_t{}); if (full()) { return teid; diff --git a/lib/gtpu/gtpu_tunnel_base_tx.h b/lib/gtpu/gtpu_tunnel_base_tx.h index d29ac8e1b2..897381d307 100644 --- a/lib/gtpu/gtpu_tunnel_base_tx.h +++ b/lib/gtpu/gtpu_tunnel_base_tx.h @@ -65,7 +65,7 @@ class gtpu_tunnel_base_tx { if (gtpu_pcap.is_write_enabled()) { auto buf_copy = buf.deep_copy(); - if (buf_copy.is_error()) { + if (not buf_copy.has_value()) { logger.log_warning("Unable to deep copy buffer for PCAP writer"); } else { gtpu_pcap.push_pdu(std::move(buf_copy.value())); diff --git a/lib/gtpu/gtpu_tunnel_nru_rx_impl.h b/lib/gtpu/gtpu_tunnel_nru_rx_impl.h index 67a27bb444..70790b26fd 100644 --- a/lib/gtpu/gtpu_tunnel_nru_rx_impl.h +++ b/lib/gtpu/gtpu_tunnel_nru_rx_impl.h @@ -110,7 +110,7 @@ class gtpu_tunnel_nru_rx_impl : public gtpu_tunnel_base_rx nru_ul_message ul_message = {}; expected buf = byte_buffer_chain::create(gtpu_extract_msg(std::move(pdu))); // header is invalidated after extraction; - if (buf.is_error()) { + if (not buf.has_value()) { logger.log_error("Dropped PDU: Failed to create byte_buffer_chain. pdu_len={}", pdu_len); return; } diff --git a/lib/hal/dpdk/bbdev/bbdev.cpp b/lib/hal/dpdk/bbdev/bbdev.cpp index 6b65dc78a9..770210b49d 100644 --- a/lib/hal/dpdk/bbdev/bbdev.cpp +++ b/lib/hal/dpdk/bbdev/bbdev.cpp @@ -44,7 +44,7 @@ expected<::rte_bbdev_info> dpdk::bbdev_start(const bbdev_acc_configuration& cfg, info.drv.num_queues[RTE_BBDEV_OP_LDPC_ENC], info.drv.num_queues[RTE_BBDEV_OP_LDPC_DEC], info.drv.num_queues[RTE_BBDEV_OP_FFT]); - return default_error_t{}; + return make_unexpected(default_error_t{}); } // Basic checking of hardware-accelerator capabilities. @@ -66,13 +66,13 @@ expected<::rte_bbdev_info> dpdk::bbdev_start(const bbdev_acc_configuration& cfg, if ((cfg.nof_ldpc_enc_lcores > 0 && !ldpc_enc_capable) || (cfg.nof_ldpc_dec_lcores > 0 && !ldpc_dec_capable) || (cfg.nof_fft_lcores > 0 && !fft_capable)) { logger.error("[bbdev] device {} does not provide the requested acceleration functions.", cfg.id); - return default_error_t{}; + return make_unexpected(default_error_t{}); } // Enable interruptions. if (::rte_bbdev_intr_enable(cfg.id) < 0) { logger.error("[bbdev] interrupts for device {} not setup properly.", cfg.id); - return default_error_t{}; + return make_unexpected(default_error_t{}); } // Configure the queues (only those required). @@ -96,7 +96,7 @@ expected<::rte_bbdev_info> dpdk::bbdev_start(const bbdev_acc_configuration& cfg, cfg.id, queue_id, queue_conf.priority); - return default_error_t{}; + return make_unexpected(default_error_t{}); } ++queue_id; } @@ -115,7 +115,7 @@ expected<::rte_bbdev_info> dpdk::bbdev_start(const bbdev_acc_configuration& cfg, cfg.id, queue_id, queue_conf.priority); - return default_error_t{}; + return make_unexpected(default_error_t{}); } ++queue_id; } @@ -134,7 +134,7 @@ expected<::rte_bbdev_info> dpdk::bbdev_start(const bbdev_acc_configuration& cfg, cfg.id, queue_id, queue_conf.priority); - return default_error_t{}; + return make_unexpected(default_error_t{}); } ++queue_id; } @@ -142,7 +142,7 @@ expected<::rte_bbdev_info> dpdk::bbdev_start(const bbdev_acc_configuration& cfg, if (::rte_bbdev_start(cfg.id) < 0) { logger.error("[bbdev] device {} not started.", cfg.id); - return default_error_t{}; + return make_unexpected(default_error_t{}); } return info; diff --git a/lib/hal/dpdk/bbdev/bbdev_acc_factory.cpp b/lib/hal/dpdk/bbdev/bbdev_acc_factory.cpp index 3d9cd0e3a3..7404ff50e9 100644 --- a/lib/hal/dpdk/bbdev/bbdev_acc_factory.cpp +++ b/lib/hal/dpdk/bbdev/bbdev_acc_factory.cpp @@ -31,7 +31,7 @@ std::shared_ptr srsran::dpdk::create_bbdev_acc(const bbdev_acc_config { // bbdev device start procedure. expected<::rte_bbdev_info> info = bbdev_start(cfg, logger); - if (info.is_error()) { + if (not info.has_value()) { return nullptr; } diff --git a/lib/hal/phy/upper/channel_processors/hw_accelerator_pdsch_enc_acc100_impl.cpp b/lib/hal/phy/upper/channel_processors/hw_accelerator_pdsch_enc_acc100_impl.cpp index a61fdc2c97..7d84c903db 100644 --- a/lib/hal/phy/upper/channel_processors/hw_accelerator_pdsch_enc_acc100_impl.cpp +++ b/lib/hal/phy/upper/channel_processors/hw_accelerator_pdsch_enc_acc100_impl.cpp @@ -77,8 +77,8 @@ void hw_accelerator_pdsch_enc_acc100_impl::hw_reserve_queue() void hw_accelerator_pdsch_enc_acc100_impl::hw_free_queue() { - // Verify that the hardware queue won't be requrired anymore. - if (!dedicated_queue || (dedicated_queue && queue_id > 0)) { + // Free the queue in case of non-dedicated use or upon object destruction. + if (!dedicated_queue) { bbdev_accelerator->free_queue(RTE_BBDEV_OP_LDPC_ENC, queue_id); // HAL logging. diff --git a/lib/hal/phy/upper/channel_processors/hw_accelerator_pdsch_enc_acc100_impl.h b/lib/hal/phy/upper/channel_processors/hw_accelerator_pdsch_enc_acc100_impl.h index 3cf6a35229..4d087594c9 100644 --- a/lib/hal/phy/upper/channel_processors/hw_accelerator_pdsch_enc_acc100_impl.h +++ b/lib/hal/phy/upper/channel_processors/hw_accelerator_pdsch_enc_acc100_impl.h @@ -141,6 +141,7 @@ class hw_accelerator_pdsch_enc_acc100_impl : public hw_accelerator_pdsch_enc_imp ~hw_accelerator_pdsch_enc_acc100_impl() { // Free the reserved hardware queue in case of dedicated use. + dedicated_queue = false; hw_free_queue(); // HAL logging. diff --git a/lib/hal/phy/upper/channel_processors/pusch/hw_accelerator_pusch_dec_acc100_impl.cpp b/lib/hal/phy/upper/channel_processors/pusch/hw_accelerator_pusch_dec_acc100_impl.cpp index bd631c4d2b..be9ff6a159 100644 --- a/lib/hal/phy/upper/channel_processors/pusch/hw_accelerator_pusch_dec_acc100_impl.cpp +++ b/lib/hal/phy/upper/channel_processors/pusch/hw_accelerator_pusch_dec_acc100_impl.cpp @@ -85,8 +85,8 @@ void hw_accelerator_pusch_dec_acc100_impl::hw_reserve_queue() void hw_accelerator_pusch_dec_acc100_impl::hw_free_queue() { - // Verify that the hardware queue won't be requrired anymore. - if (!dedicated_queue || (dedicated_queue && queue_id > 0)) { + // Free the queue in case of non-dedicated use or upon object destruction. + if (!dedicated_queue) { bbdev_accelerator->free_queue(RTE_BBDEV_OP_LDPC_DEC, queue_id); // HAL logging. diff --git a/lib/hal/phy/upper/channel_processors/pusch/hw_accelerator_pusch_dec_acc100_impl.h b/lib/hal/phy/upper/channel_processors/pusch/hw_accelerator_pusch_dec_acc100_impl.h index 5a65445ebe..ccd2ff6fee 100644 --- a/lib/hal/phy/upper/channel_processors/pusch/hw_accelerator_pusch_dec_acc100_impl.h +++ b/lib/hal/phy/upper/channel_processors/pusch/hw_accelerator_pusch_dec_acc100_impl.h @@ -154,6 +154,7 @@ class hw_accelerator_pusch_dec_acc100_impl : public hw_accelerator_pusch_dec_imp ~hw_accelerator_pusch_dec_acc100_impl() { // Free the reserved hardware queue in case of dedicated use. + dedicated_queue = false; hw_free_queue(); // HAL logging. diff --git a/lib/instrumentation/CMakeLists.txt b/lib/instrumentation/CMakeLists.txt index 9e6ce702f1..cba4c9746c 100644 --- a/lib/instrumentation/CMakeLists.txt +++ b/lib/instrumentation/CMakeLists.txt @@ -25,5 +25,4 @@ add_library(srsran_instrumentation traces/ofh_traces.cpp) target_link_libraries(srsran_instrumentation srsran_support) -install(TARGETS srsran_instrumentation - EXPORT srsran_export) +add_to_exported_libs(srsran_instrumentation) diff --git a/lib/mac/mac_ul/mac_ul_sch_pdu.cpp b/lib/mac/mac_ul/mac_ul_sch_pdu.cpp index b7417611a0..6e2a39f159 100644 --- a/lib/mac/mac_ul/mac_ul_sch_pdu.cpp +++ b/lib/mac/mac_ul/mac_ul_sch_pdu.cpp @@ -35,7 +35,7 @@ error_type mac_ul_sch_subpdu::unpack(byte_buffer_reader& subpdu_rea { unsigned subpdu_len = subpdu_reader.length(); if (subpdu_len == 0) { - return std::string{"Empty subPDU."}; + return make_unexpected(std::string{"Empty subPDU."}); } payload_view = {}; @@ -46,7 +46,7 @@ error_type mac_ul_sch_subpdu::unpack(byte_buffer_reader& subpdu_rea header_length = 1; if (not lcid_val.is_valid_lcid()) { - return fmt::format("Unrecognized lcid={}.", lcid_val); + return make_unexpected(fmt::format("Unrecognized lcid={}.", lcid_val)); } uint32_t sdu_length = 0; @@ -54,7 +54,7 @@ error_type mac_ul_sch_subpdu::unpack(byte_buffer_reader& subpdu_rea // Variable-sized MAC CEs or SDUs if (subpdu_len < (F_bit ? 3 : 2)) { - return fmt::format("Not enough bytes remaining in PDU to decode length prefix."); + return make_unexpected(fmt::format("Not enough bytes remaining in PDU to decode length prefix.")); } // Read first length byte @@ -70,8 +70,9 @@ error_type mac_ul_sch_subpdu::unpack(byte_buffer_reader& subpdu_rea } if (subpdu_len < header_length + sdu_length) { - return fmt::format( - "Not enough bytes remaining in PDU to decode SDU payload ({} < {}).", subpdu_len - header_length, sdu_length); + return make_unexpected(fmt::format("Not enough bytes remaining in PDU to decode SDU payload ({} < {}).", + subpdu_len - header_length, + sdu_length)); } payload_view = subpdu_reader.split_and_advance(sdu_length); } else { @@ -85,9 +86,9 @@ error_type mac_ul_sch_subpdu::unpack(byte_buffer_reader& subpdu_rea sdu_length = lcid_val.sizeof_ce(); if (subpdu_len < header_length + sdu_length) { - return fmt::format("Not enough bytes remaining in PDU to decode CE payload ({} < {}).", - subpdu_len - header_length, - sdu_length); + return make_unexpected(fmt::format("Not enough bytes remaining in PDU to decode CE payload ({} < {}).", + subpdu_len - header_length, + sdu_length)); } payload_view = subpdu_reader.split_and_advance(sdu_length); } @@ -112,7 +113,7 @@ error_type mac_ul_sch_pdu::unpack(const byte_buffer& payload) mac_ul_sch_subpdu& subpdu = subpdus.emplace_back(); error_type ret = subpdu.unpack(reader); - if (ret.is_error()) { + if (not ret.has_value()) { // Discard all decoded subPDUs. clear(); return ret; diff --git a/lib/mac/mac_ul/pdu_rx_handler.cpp b/lib/mac/mac_ul/pdu_rx_handler.cpp index 1df75be0e0..ee10d31dd0 100644 --- a/lib/mac/mac_ul/pdu_rx_handler.cpp +++ b/lib/mac/mac_ul/pdu_rx_handler.cpp @@ -92,7 +92,7 @@ bool pdu_rx_handler::handle_rx_pdu(slot_point sl_rx, du_cell_index_t cell_index, // > Decode MAC UL PDU. decoded_mac_rx_pdu ctx{sl_rx, cell_index, std::move(pdu), ue_index}; error_type ret = ctx.decoded_subpdus.unpack(ctx.pdu_rx.pdu); - if (ret.is_error()) { + if (not ret.has_value()) { logger.info("{}: Failed to decode MAC UL PDU. Cause: {}", create_prefix(ctx), ret.error()); return false; } @@ -239,7 +239,7 @@ bool pdu_rx_handler::handle_mac_ce(const decoded_mac_rx_pdu& ctx, const mac_ul_s } else { bsr_ind.bsr_fmt = subpdu.lcid() == lcid_ul_sch_t::LONG_BSR ? bsr_format::LONG_BSR : bsr_format::LONG_TRUNC_BSR; expected lbsr_report = decode_lbsr(bsr_ind.bsr_fmt, subpdu.payload()); - if (lbsr_report.is_error()) { + if (not lbsr_report.has_value()) { logger.warning("{}: Discarding BSR MAC CE. Cause: BSR is invalid", create_prefix(ctx, subpdu)); return false; } diff --git a/lib/mac/mac_ul/ul_bsr.cpp b/lib/mac/mac_ul/ul_bsr.cpp index bda1865f52..c0cd1f7e2e 100644 --- a/lib/mac/mac_ul/ul_bsr.cpp +++ b/lib/mac/mac_ul/ul_bsr.cpp @@ -136,7 +136,7 @@ expected srsran::decode_lbsr(bsr_format format, byte_buffer_vie if (bsr.buffer_size == 255) { srslog::fetch_basic_logger("MAC").warning("lcg={}: Discarding BSR. Cause: BSR=255 is invalid.", i); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } } else if (format == bsr_format::LONG_TRUNC_BSR) { // In the case of Long truncated BSR, some LCG buffer sizes may not be present. Assume BSR > 0 in that case. @@ -146,7 +146,7 @@ expected srsran::decode_lbsr(bsr_format format, byte_buffer_vie srslog::fetch_basic_logger("MAC").error("Error parsing LongBSR CE: sdu_length={} but there are {} active bsr\n", payload.length(), lbsr.list.size()); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } lbsr.list.push_back(bsr); } else if (format == bsr_format::LONG_TRUNC_BSR) { diff --git a/lib/ngap/ngap_asn1_converters.h b/lib/ngap/ngap_asn1_converters.h index 4489603fd1..d99475dd9e 100644 --- a/lib/ngap/ngap_asn1_converters.h +++ b/lib/ngap/ngap_asn1_converters.h @@ -26,7 +26,7 @@ #include "srsran/asn1/ngap/ngap_ies.h" #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/ngap/ngap_handover.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/ran/cause/ngap_cause.h" #include "srsran/ran/cu_types.h" #include "srsran/ran/lcid.h" @@ -200,9 +200,9 @@ inline asn1::ngap::qos_flow_with_cause_item_s cu_cp_qos_flow_failed_to_setup_ite /// \brief Convert CU-CP NRCGI to NR Cell Identity. /// \param ngap_cgi The NGAP NRCGI. /// \return The NR Cell Identity. -inline nr_cell_id_t cu_cp_nrcgi_to_nr_cell_identity(asn1::ngap::nr_cgi_s& ngap_cgi) +inline nr_cell_identity cu_cp_nrcgi_to_nr_cell_identity(asn1::ngap::nr_cgi_s& ngap_cgi) { - return ngap_cgi.nr_cell_id.to_number(); + return nr_cell_identity::create(ngap_cgi.nr_cell_id.to_number()).value(); } /// \brief Convert CU-CP NRCGI to NR Cell Identity. @@ -214,10 +214,10 @@ cu_cp_user_location_info_to_asn1(const cu_cp_user_location_info_nr& cu_cp_user_l asn1::ngap::user_location_info_nr_s asn1_user_location_info; // add nr cgi - asn1_user_location_info.nr_cgi.nr_cell_id.from_number(cu_cp_user_location_info.nr_cgi.nci); - asn1_user_location_info.nr_cgi.plmn_id.from_string(cu_cp_user_location_info.nr_cgi.plmn_hex); + asn1_user_location_info.nr_cgi.nr_cell_id.from_number(cu_cp_user_location_info.nr_cgi.nci.value()); + asn1_user_location_info.nr_cgi.plmn_id = cu_cp_user_location_info.nr_cgi.plmn_id.to_bytes(); // add tai - asn1_user_location_info.tai.plmn_id.from_string(cu_cp_user_location_info.tai.plmn_id); + asn1_user_location_info.tai.plmn_id = cu_cp_user_location_info.tai.plmn_id.to_bytes(); asn1_user_location_info.tai.tac.from_number(cu_cp_user_location_info.tai.tac); // add timestamp if (cu_cp_user_location_info.time_stamp.has_value()) { @@ -404,7 +404,7 @@ inline bool pdu_session_res_setup_failed_item_to_asn1(template_asn1_item& inline guami_t asn1_to_guami(const asn1::ngap::guami_s& asn1_guami) { guami_t guami; - guami.plmn = asn1_guami.plmn_id.to_string(); + guami.plmn = plmn_identity::from_bytes(asn1_guami.plmn_id.to_bytes()).value(); guami.amf_region_id = asn1_guami.amf_region_id.to_number(); guami.amf_set_id = asn1_guami.amf_set_id.to_number(); guami.amf_pointer = asn1_guami.amf_pointer.to_number(); @@ -516,7 +516,7 @@ inline asn1::ngap::s_nssai_s s_nssai_to_asn1(const s_nssai_t& s_nssai) inline cu_cp_tai ngap_asn1_to_tai(const asn1::ngap::tai_s& asn1_tai) { cu_cp_tai tai; - tai.plmn_id = asn1_tai.plmn_id.to_string(); + tai.plmn_id = plmn_identity::from_bytes(asn1_tai.plmn_id.to_bytes()).value(); tai.tac = asn1_tai.tac.to_number(); return tai; @@ -545,12 +545,10 @@ inline nr_cell_global_id_t ngap_asn1_to_nr_cgi(const asn1::ngap::nr_cgi_s& asn1_ nr_cell_global_id_t nr_cgi; // nr cell id - nr_cgi.nci = asn1_nr_cgi.nr_cell_id.to_number(); + nr_cgi.nci = nr_cell_identity::create(asn1_nr_cgi.nr_cell_id.to_number()).value(); // plmn id - nr_cgi.plmn_hex = asn1_nr_cgi.plmn_id.to_string(); - nr_cgi.plmn = plmn_bcd_to_string(asn1_nr_cgi.plmn_id.to_number()); - ngap_plmn_to_mccmnc(asn1_nr_cgi.plmn_id.to_number(), &nr_cgi.mcc, &nr_cgi.mnc); + nr_cgi.plmn_id = plmn_identity::from_bytes(asn1_nr_cgi.plmn_id.to_bytes()).value(); return nr_cgi; } @@ -563,10 +561,10 @@ inline asn1::ngap::nr_cgi_s nr_cgi_to_ngap_asn1(const nr_cell_global_id_t& nr_cg asn1::ngap::nr_cgi_s asn1_nr_cgi; // nr cell id - asn1_nr_cgi.nr_cell_id.from_number(nr_cgi.nci); + asn1_nr_cgi.nr_cell_id.from_number(nr_cgi.nci.value()); // plmn id - asn1_nr_cgi.plmn_id.from_string(nr_cgi.plmn_hex); + asn1_nr_cgi.plmn_id = nr_cgi.plmn_id.to_bytes(); return asn1_nr_cgi; } @@ -712,7 +710,7 @@ inline cu_cp_global_gnb_id ngap_asn1_to_global_gnb_id(const asn1::ngap::global_g cu_cp_global_gnb_id gnb_id; // plmn id - gnb_id.plmn_id = asn1_gnb_id.plmn_id.to_string(); + gnb_id.plmn_id = plmn_identity::from_bytes(asn1_gnb_id.plmn_id.to_bytes()).value(); // gnb id gnb_id.gnb_id.id = asn1_gnb_id.gnb_id.gnb_id().to_number(); diff --git a/lib/ngap/ngap_asn1_helpers.h b/lib/ngap/ngap_asn1_helpers.h index d444756d8e..88aa668c3f 100644 --- a/lib/ngap/ngap_asn1_helpers.h +++ b/lib/ngap/ngap_asn1_helpers.h @@ -51,12 +51,11 @@ inline void fill_asn1_ng_setup_request(asn1::ngap::ng_setup_request_s& asn1_requ const ngap_ng_setup_request& request) { // fill global ran node id - asn1_request->global_ran_node_id.set_global_gnb_id(); - asn1_request->global_ran_node_id.global_gnb_id().gnb_id.set_gnb_id(); - asn1_request->global_ran_node_id.global_gnb_id().gnb_id.gnb_id().from_number( - request.global_ran_node_id.gnb_id.id, request.global_ran_node_id.gnb_id.bit_length); - asn1_request->global_ran_node_id.global_gnb_id().plmn_id.from_number( - plmn_string_to_bcd(request.global_ran_node_id.plmn_id)); + auto& global_gnb = asn1_request->global_ran_node_id.set_global_gnb_id(); + global_gnb.gnb_id.set_gnb_id(); + global_gnb.gnb_id.gnb_id().from_number(request.global_ran_node_id.gnb_id.id, + request.global_ran_node_id.gnb_id.bit_length); + global_gnb.plmn_id = request.global_ran_node_id.plmn_id.to_bytes(); // fill ran node name asn1_request->ran_node_name_present = true; @@ -74,7 +73,7 @@ inline void fill_asn1_ng_setup_request(asn1::ngap::ng_setup_request_s& asn1_requ asn1::ngap::broadcast_plmn_item_s asn1_broadcast_plmn_item = {}; // fill plmn id - asn1_broadcast_plmn_item.plmn_id.from_number(plmn_string_to_bcd(broadcast_plmn_item.plmn_id)); + asn1_broadcast_plmn_item.plmn_id = broadcast_plmn_item.plmn_id.to_bytes(); // fill tai slice support list for (const auto& slice_support_item : broadcast_plmn_item.tai_slice_support_list) { @@ -785,9 +784,9 @@ inline void fill_asn1_ue_context_release_complete(asn1::ngap::ue_context_release // add ngran cgi asn1_recommended_cell_item.ngran_cgi.set_nr_cgi().nr_cell_id.from_number( - cu_cp_recommended_cell_item.ngran_cgi.nci); - asn1_recommended_cell_item.ngran_cgi.set_nr_cgi().plmn_id.from_string( - cu_cp_recommended_cell_item.ngran_cgi.plmn_hex); + cu_cp_recommended_cell_item.ngran_cgi.nci.value()); + asn1_recommended_cell_item.ngran_cgi.set_nr_cgi().plmn_id = + cu_cp_recommended_cell_item.ngran_cgi.plmn_id.to_bytes(); // add time stayed in cell if (cu_cp_recommended_cell_item.time_stayed_in_cell.has_value()) { @@ -807,15 +806,16 @@ inline void fill_asn1_ue_context_release_complete(asn1::ngap::ue_context_release if (cu_cp_recommended_ran_node_item.amf_paging_target.is_global_ran_node_id) { // add global gnb id auto& asn1_global_ran_node_id = asn1_recommended_ran_node_item.amf_paging_target.set_global_ran_node_id(); - asn1_global_ran_node_id.set_global_gnb_id().plmn_id.from_string( - cu_cp_recommended_ran_node_item.amf_paging_target.global_ran_node_id.value().plmn_id); - asn1_global_ran_node_id.global_gnb_id().gnb_id.set_gnb_id().from_number( + auto& global_gnb = asn1_global_ran_node_id.set_global_gnb_id(); + global_gnb.plmn_id = + cu_cp_recommended_ran_node_item.amf_paging_target.global_ran_node_id.value().plmn_id.to_bytes(); + global_gnb.gnb_id.set_gnb_id().from_number( cu_cp_recommended_ran_node_item.amf_paging_target.global_ran_node_id.value().gnb_id.id, cu_cp_recommended_ran_node_item.amf_paging_target.global_ran_node_id.value().gnb_id.bit_length); } else if (cu_cp_recommended_ran_node_item.amf_paging_target.is_tai) { // add tai - auto& asn1_tai = asn1_recommended_ran_node_item.amf_paging_target.set_tai(); - asn1_tai.plmn_id.from_string(cu_cp_recommended_ran_node_item.amf_paging_target.tai.value().plmn_id); + auto& asn1_tai = asn1_recommended_ran_node_item.amf_paging_target.set_tai(); + asn1_tai.plmn_id = cu_cp_recommended_ran_node_item.amf_paging_target.tai.value().plmn_id.to_bytes(); asn1_tai.tac.from_number(cu_cp_recommended_ran_node_item.amf_paging_target.tai.value().tac); } else { asn1_recommended_ran_node_item.amf_paging_target.set(asn1::ngap::amf_paging_target_c::types_opts::nulltype); @@ -861,7 +861,7 @@ inline void fill_cu_cp_paging_message(cu_cp_paging_message& paging, const asn1:: // add tai list for paging for (const auto& asn1_tai_item : asn1_paging->tai_list_for_paging) { cu_cp_tai_list_for_paging_item tai_item; - tai_item.tai.plmn_id = asn1_tai_item.tai.plmn_id.to_string(); + tai_item.tai.plmn_id = plmn_identity::from_bytes(asn1_tai_item.tai.plmn_id.to_bytes()).value(); tai_item.tai.tac = asn1_tai_item.tai.tac.to_number(); paging.tai_list_for_paging.push_back(tai_item); @@ -899,8 +899,10 @@ inline void fill_cu_cp_paging_message(cu_cp_paging_message& paging, const asn1:: cu_cp_recommended_cell_item recommended_cell_item; // add ngran cgi - recommended_cell_item.ngran_cgi.nci = asn1_recommended_cell.ngran_cgi.nr_cgi().nr_cell_id.to_number(); - recommended_cell_item.ngran_cgi.plmn_hex = asn1_recommended_cell.ngran_cgi.nr_cgi().plmn_id.to_string(); + recommended_cell_item.ngran_cgi.nci = + nr_cell_identity::create(asn1_recommended_cell.ngran_cgi.nr_cgi().nr_cell_id.to_number()).value(); + recommended_cell_item.ngran_cgi.plmn_id = + plmn_identity::from_bytes(asn1_recommended_cell.ngran_cgi.nr_cgi().plmn_id.to_bytes()).value(); // add time stayed in cell if (asn1_recommended_cell.time_stayed_in_cell_present) { @@ -1102,9 +1104,9 @@ fill_asn1_handover_resource_allocation_response(asn1::ngap::ho_fail_s& inline void fill_asn1_handover_notify(asn1::ngap::ho_notify_s& asn1_msg, const nr_cell_global_id_t& cgi, const unsigned tac) { - auto& user_loc_info_nr = asn1_msg->user_location_info.set_user_location_info_nr(); - user_loc_info_nr.nr_cgi = nr_cgi_to_ngap_asn1(cgi); - user_loc_info_nr.tai.plmn_id.from_string(cgi.plmn_hex); + auto& user_loc_info_nr = asn1_msg->user_location_info.set_user_location_info_nr(); + user_loc_info_nr.nr_cgi = nr_cgi_to_ngap_asn1(cgi); + user_loc_info_nr.tai.plmn_id = cgi.plmn_id.to_bytes(); user_loc_info_nr.tai.tac.from_number(tac); } diff --git a/lib/ngap/ngap_context.h b/lib/ngap/ngap_context.h index e89f7f3a10..53d8abfee1 100644 --- a/lib/ngap/ngap_context.h +++ b/lib/ngap/ngap_context.h @@ -24,6 +24,7 @@ #include "srsran/ngap/ngap_types.h" #include "srsran/ran/gnb_id.h" +#include "srsran/ran/plmn_identity.h" #include #include @@ -35,7 +36,7 @@ namespace srs_cu_cp { struct ngap_context_t { gnb_id_t gnb_id = {0, 22}; std::string ran_node_name; - std::string plmn; /// Full PLMN as string (without possible filler digit) e.g. "00101" + plmn_identity plmn = plmn_identity::test_value(); unsigned tac; std::vector served_guami_list; std::chrono::seconds pdu_session_setup_timeout; // timeout for PDU context setup in seconds diff --git a/lib/ngap/ngap_impl.cpp b/lib/ngap/ngap_impl.cpp index d55934fdba..6d1b74e32c 100644 --- a/lib/ngap/ngap_impl.cpp +++ b/lib/ngap/ngap_impl.cpp @@ -706,7 +706,7 @@ void ngap_impl::handle_handover_request(const asn1::ngap::ho_request_s& msg) } logger.info("HandoverRequest - extracted target cell. plmn={}, target cell_id={}", - ho_request.source_to_target_transparent_container.target_cell_id.plmn, + ho_request.source_to_target_transparent_container.target_cell_id.plmn_id, ho_request.source_to_target_transparent_container.target_cell_id.nci); // Create UE in target cell diff --git a/lib/ngap/procedures/ngap_handover_preparation_procedure.cpp b/lib/ngap/procedures/ngap_handover_preparation_procedure.cpp index 3f32958a1e..0bb3cf4f6d 100644 --- a/lib/ngap/procedures/ngap_handover_preparation_procedure.cpp +++ b/lib/ngap/procedures/ngap_handover_preparation_procedure.cpp @@ -23,7 +23,7 @@ #include "ngap_handover_preparation_procedure.h" #include "srsran/asn1/ngap/common.h" #include "srsran/ngap/ngap_message.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" using namespace srsran; using namespace srsran::srs_cu_cp; @@ -133,14 +133,12 @@ void ngap_handover_preparation_procedure::send_handover_required() void ngap_handover_preparation_procedure::fill_asn1_target_ran_node_id(target_id_c& target_id) { - target_id.set_target_ran_node_id(); - target_id.target_ran_node_id(); - target_id.target_ran_node_id().global_ran_node_id.set(global_ran_node_id_c::types::global_gnb_id); - target_id.target_ran_node_id().global_ran_node_id.global_gnb_id().plmn_id.from_number( - plmn_string_to_bcd(context.plmn)); // cross-PLMN handover not supported - target_id.target_ran_node_id().global_ran_node_id.global_gnb_id().gnb_id.set_gnb_id(); - target_id.target_ran_node_id().global_ran_node_id.global_gnb_id().gnb_id.gnb_id().from_number( - request.gnb_id.id, request.gnb_id.bit_length); + auto& target_node = target_id.set_target_ran_node_id(); + target_node.global_ran_node_id.set(global_ran_node_id_c::types::global_gnb_id); + auto& global_gnb = target_node.global_ran_node_id.global_gnb_id(); + global_gnb.plmn_id = context.plmn.to_bytes(); + global_gnb.gnb_id.set_gnb_id(); + global_gnb.gnb_id.gnb_id().from_number(request.gnb_id.id, request.gnb_id.bit_length); } void ngap_handover_preparation_procedure::fill_asn1_pdu_session_res_list( @@ -180,8 +178,8 @@ byte_buffer ngap_handover_preparation_procedure::fill_asn1_source_to_target_tran } nr_cgi_s& target_nr_cgi = transparent_container.target_cell_id.set_nr_cgi(); - target_nr_cgi.plmn_id.from_number(plmn_string_to_bcd(context.plmn)); // cross-PLMN handover not supported - target_nr_cgi.nr_cell_id.from_number(request.nci); + target_nr_cgi.plmn_id = context.plmn.to_bytes(); + target_nr_cgi.nr_cell_id.from_number(request.nci.value()); last_visited_cell_item_s last_visited_cell_item; last_visited_ngran_cell_info_s& ngran_cell = last_visited_cell_item.last_visited_cell_info.set_ngran_cell(); diff --git a/lib/ngap/ue_context/ngap_ue_context.h b/lib/ngap/ue_context/ngap_ue_context.h index 00bc2bf520..8792a5ebfc 100644 --- a/lib/ngap/ue_context/ngap_ue_context.h +++ b/lib/ngap/ue_context/ngap_ue_context.h @@ -251,8 +251,7 @@ class ngap_ue_context_list // iterate over all ids starting with the next_ran_ue_id to find the available id while (true) { - // Only iterate over ue_index_to_ran_ue_id (size=MAX_NOF_UES_PER_DU) - // to avoid iterating over all possible values of ran_ue_id_t (size=2^32-1) + // Iterate over ue_index_to_ran_ue_id auto it = std::find_if(ue_index_to_ran_ue_id.begin(), ue_index_to_ran_ue_id.end(), [this](auto& u) { return u.second == next_ran_ue_id; }); diff --git a/lib/ofh/ethernet/ethernet_receiver_impl.cpp b/lib/ofh/ethernet/ethernet_receiver_impl.cpp index aaeb5a22f3..0cc6b59abf 100644 --- a/lib/ofh/ethernet/ethernet_receiver_impl.cpp +++ b/lib/ofh/ethernet/ethernet_receiver_impl.cpp @@ -165,7 +165,7 @@ void receiver_impl::receive() trace_point tp = ofh_tracer.now(); auto exp_buffer = buffer_pool.reserve(); - if (exp_buffer.is_error()) { + if (not exp_buffer.has_value()) { logger.warning("No buffer is available for receiving an Ethernet packet"); return; } diff --git a/lib/ofh/ethernet/ethernet_rx_buffer_pool.h b/lib/ofh/ethernet/ethernet_rx_buffer_pool.h index 319115770f..435a12849b 100644 --- a/lib/ofh/ethernet/ethernet_rx_buffer_pool.h +++ b/lib/ofh/ethernet/ethernet_rx_buffer_pool.h @@ -68,7 +68,7 @@ class ethernet_rx_buffer_pool { auto buffer_id = free_list.try_pop(); if (!buffer_id.has_value()) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } return {{*this, buffer_id.value()}}; diff --git a/lib/ofh/receiver/ofh_uplane_prach_data_flow_notifier.cpp b/lib/ofh/receiver/ofh_uplane_prach_data_flow_notifier.cpp index 3facb7a8bc..c3ed06195f 100644 --- a/lib/ofh/receiver/ofh_uplane_prach_data_flow_notifier.cpp +++ b/lib/ofh/receiver/ofh_uplane_prach_data_flow_notifier.cpp @@ -31,7 +31,7 @@ void uplane_prach_data_flow_notifier::notify_prach(slot_point slot) expected context = prach_context_repo->get(slot).try_getting_complete_prach_buffer(); - if (context.is_error()) { + if (not context.has_value()) { return; } diff --git a/lib/ofh/receiver/ofh_uplane_rx_symbol_data_flow_notifier.cpp b/lib/ofh/receiver/ofh_uplane_rx_symbol_data_flow_notifier.cpp index c7d3729743..9b02b0bbeb 100644 --- a/lib/ofh/receiver/ofh_uplane_rx_symbol_data_flow_notifier.cpp +++ b/lib/ofh/receiver/ofh_uplane_rx_symbol_data_flow_notifier.cpp @@ -31,7 +31,7 @@ void uplane_rx_symbol_data_flow_notifier::notify_received_symbol(slot_point slot expected context = ul_context_repo->get(slot, symbol).try_getting_complete_resource_grid(); - if (context.is_error()) { + if (not context.has_value()) { return; } diff --git a/lib/ofh/support/prach_context_repository.h b/lib/ofh/support/prach_context_repository.h index dd075ffc83..4174f0265d 100644 --- a/lib/ofh/support/prach_context_repository.h +++ b/lib/ofh/support/prach_context_repository.h @@ -154,13 +154,13 @@ class prach_context expected try_getting_complete_prach_buffer() const { if (!context_info.buffer) { - return default_error_t({}); + return make_unexpected(default_error_t{}); } if (!std::all_of(buffer_stats.begin(), buffer_stats.end(), [&](const auto& symbol) { return symbol.have_all_res_been_written(); })) { - return default_error_t({}); + return make_unexpected(default_error_t{}); } return {context_info}; diff --git a/lib/ofh/support/uplink_context_repository.h b/lib/ofh/support/uplink_context_repository.h index c89de8cabb..f17e09a0d9 100644 --- a/lib/ofh/support/uplink_context_repository.h +++ b/lib/ofh/support/uplink_context_repository.h @@ -95,11 +95,11 @@ class uplink_context expected try_getting_complete_resource_grid() const { if (!grid.grid) { - return default_error_t({}); + return make_unexpected(default_error_t{}); } if (!have_all_prbs_been_written()) { - return default_error_t({}); + return make_unexpected(default_error_t{}); } return {grid}; diff --git a/lib/ofh/support/uplink_cplane_context_repository.h b/lib/ofh/support/uplink_cplane_context_repository.h index 117dc13fa1..4f4bf05556 100644 --- a/lib/ofh/support/uplink_cplane_context_repository.h +++ b/lib/ofh/support/uplink_cplane_context_repository.h @@ -96,7 +96,7 @@ class uplink_cplane_context_repository return cp_param; } - return default_error_t{}; + return make_unexpected(default_error_t{}); } }; diff --git a/lib/pcap/backend_pcap_writer.h b/lib/pcap/backend_pcap_writer.h index 3a72bfb91d..1d7f8df119 100644 --- a/lib/pcap/backend_pcap_writer.h +++ b/lib/pcap/backend_pcap_writer.h @@ -39,7 +39,7 @@ class pcap_pdu_data pcap_pdu_data(span context_header, byte_buffer payload) { auto header = byte_buffer::create(context_header); - if (header.is_error()) { + if (not header.has_value()) { return; } if (not header_buf.append(std::move(header.value()))) { diff --git a/lib/pcap/dlt_pcap_impl.cpp b/lib/pcap/dlt_pcap_impl.cpp index 09779a30aa..d4fc094882 100644 --- a/lib/pcap/dlt_pcap_impl.cpp +++ b/lib/pcap/dlt_pcap_impl.cpp @@ -58,7 +58,7 @@ void dlt_pcap_impl::push_pdu(const_span pdu) return; } auto pdu_buffer = byte_buffer::create(pdu); - if (pdu_buffer.is_error()) { + if (not pdu_buffer.has_value()) { return; } writer.write_pdu(std::move(pdu_buffer.value())); diff --git a/lib/pcap/mac_pcap_impl.cpp b/lib/pcap/mac_pcap_impl.cpp index 8abfdbc6ed..3840427717 100644 --- a/lib/pcap/mac_pcap_impl.cpp +++ b/lib/pcap/mac_pcap_impl.cpp @@ -50,7 +50,7 @@ void mac_pcap_impl::close() void mac_pcap_impl::push_pdu(const mac_nr_context_info& context, const_span pdu) { auto pdu_buffer = byte_buffer::create(pdu); - if (pdu_buffer.is_error()) { + if (not pdu_buffer.has_value()) { return; } push_pdu(context, std::move(pdu_buffer.value())); diff --git a/lib/pdcp/pdcp_entity_rx.cpp b/lib/pdcp/pdcp_entity_rx.cpp index f4adb28bc7..a7905658af 100644 --- a/lib/pdcp/pdcp_entity_rx.cpp +++ b/lib/pdcp/pdcp_entity_rx.cpp @@ -82,7 +82,7 @@ void pdcp_entity_rx::handle_pdu(byte_buffer_chain buf) } auto pdu_copy = buf.deep_copy(); - if (pdu_copy.is_error()) { + if (not pdu_copy.has_value()) { metrics_add_dropped_pdus(1); logger.log_error("Dropping PDU: Copy failed. pdu_len={}", buf.length()); return; diff --git a/lib/pdcp/pdcp_entity_tx.cpp b/lib/pdcp/pdcp_entity_tx.cpp index 8f710b3846..265e893ed4 100644 --- a/lib/pdcp/pdcp_entity_tx.cpp +++ b/lib/pdcp/pdcp_entity_tx.cpp @@ -85,7 +85,7 @@ void pdcp_entity_tx::handle_sdu(byte_buffer buf) byte_buffer sdu; if (cfg.discard_timer.has_value() && is_am()) { auto sdu_copy = buf.deep_copy(); - if (sdu_copy.is_error()) { + if (not sdu_copy.has_value()) { logger.log_error("Unable to deep copy SDU"); upper_cn.on_protocol_failure(); return; @@ -109,7 +109,7 @@ void pdcp_entity_tx::handle_sdu(byte_buffer buf) // Apply ciphering and integrity protection expected exp_buf = apply_ciphering_and_integrity_protection(std::move(buf), st.tx_next); - if (exp_buf.is_error()) { + if (not exp_buf.has_value()) { logger.log_error("Could not apply ciphering and integrity protection, dropping SDU and notifying RRC. count={}", st.tx_next); upper_cn.on_protocol_failure(); @@ -145,7 +145,7 @@ void pdcp_entity_tx::handle_sdu(byte_buffer buf) } // Write to lower layers - write_data_pdu_to_lower_layers(st.tx_next, std::move(protected_buf)); + write_data_pdu_to_lower_layers(st.tx_next, std::move(protected_buf), /* is_retx = */ false); // Increment TX_NEXT st.tx_next++; @@ -206,25 +206,30 @@ void pdcp_entity_tx::reestablish(security::sec_128_as_config sec_cfg_) logger.log_info("Reestablished PDCP. st={}", st); } -void pdcp_entity_tx::write_data_pdu_to_lower_layers(uint32_t count, byte_buffer buf) +void pdcp_entity_tx::write_data_pdu_to_lower_layers(uint32_t count, byte_buffer buf, bool is_retx) { - logger.log_info( - buf.begin(), buf.end(), "TX PDU. type=data pdu_len={} sn={} count={}", buf.length(), SN(count), count); + logger.log_info(buf.begin(), + buf.end(), + "TX PDU. type=data pdu_len={} sn={} count={} is_retx={}", + buf.length(), + SN(count), + count, + is_retx); metrics_add_pdus(1, buf.length()); - lower_dn.on_new_pdu(std::move(buf)); + lower_dn.on_new_pdu(std::move(buf), is_retx); } void pdcp_entity_tx::write_control_pdu_to_lower_layers(byte_buffer buf) { logger.log_info(buf.begin(), buf.end(), "TX PDU. type=ctrl pdu_len={}", buf.length()); metrics_add_pdus(1, buf.length()); - lower_dn.on_new_pdu(std::move(buf)); + lower_dn.on_new_pdu(std::move(buf), /* is_retx = */ false); } void pdcp_entity_tx::handle_status_report(byte_buffer_chain status) { auto status_buffer = byte_buffer::create(status.begin(), status.end()); - if (status_buffer.is_error()) { + if (not status_buffer.has_value()) { logger.log_warning("Unable to allocate byte_buffer"); return; } @@ -297,7 +302,7 @@ expected pdcp_entity_tx::apply_ciphering_and_integrity_protection(b // Append MAC-I if (is_srb() || (is_drb() && (integrity_enabled == security::integrity_enabled::on))) { if (not buf.append(mac)) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } } @@ -427,7 +432,7 @@ void pdcp_entity_tx::retransmit_all_pdus() // Pack header auto buf_copy = sdu_info.sdu.deep_copy(); - if (buf_copy.is_error()) { + if (not buf_copy.has_value()) { logger.log_error("Could not deep copy SDU, dropping SDU and notifying RRC. count={} {}", sdu_info.count, st); upper_cn.on_protocol_failure(); return; @@ -446,7 +451,7 @@ void pdcp_entity_tx::retransmit_all_pdus() // Perform integrity protection and ciphering expected exp_buf = apply_ciphering_and_integrity_protection(std::move(buf), sdu_info.count); - if (exp_buf.is_error()) { + if (not exp_buf.has_value()) { logger.log_error("Could not apply ciphering and integrity protection during retransmissions, dropping SDU and " "notifying RRC. count={} {}", sdu_info.count, @@ -456,7 +461,7 @@ void pdcp_entity_tx::retransmit_all_pdus() } byte_buffer protected_buf = std::move(exp_buf.value()); - write_data_pdu_to_lower_layers(sdu_info.count, std::move(protected_buf)); + write_data_pdu_to_lower_layers(sdu_info.count, std::move(protected_buf), /* is_retx = */ true); } } } @@ -516,10 +521,42 @@ void pdcp_entity_tx::handle_delivery_notification(uint32_t notif_sn) if (is_am()) { stop_discard_timer(notif_count); } else { - logger.log_warning("Received PDU delivery notification on UM bearer. sn={}", notif_sn); + logger.log_error("Ignored unexpected PDU delivery notification in UM bearer. notif_sn={}", notif_sn); } } +void pdcp_entity_tx::handle_retransmit_notification(uint32_t notif_sn) +{ + if (SRSRAN_UNLIKELY(is_srb())) { + logger.log_error("Ignored unexpected PDU retransmit notification in SRB. notif_sn={}", notif_sn); + return; + } + if (SRSRAN_UNLIKELY(is_um())) { + logger.log_error("Ignored unexpected PDU retransmit notification in UM bearer. notif_sn={}", notif_sn); + return; + } + + // Nothing to do here + logger.log_debug("Ignored handling PDU retransmit notification for notif_sn={}", notif_sn); +} + +void pdcp_entity_tx::handle_delivery_retransmitted_notification(uint32_t notif_sn) +{ + if (SRSRAN_UNLIKELY(is_srb())) { + logger.log_error("Ignored unexpected PDU delivery retransmitted notification in SRB. notif_sn={}", notif_sn); + return; + } + if (SRSRAN_UNLIKELY(is_um())) { + logger.log_error("Ignored unexpected PDU delivery retransmitted notification in UM bearer. notif_sn={}", notif_sn); + return; + } + + // TODO: Here we can stop discard timers of successfully retransmitted PDUs once they can be distinguished from + // origianls (e.g. if they are moved into a separate container upon retransmission). + // For now those retransmitted PDUs will be cleaned when handling delivery notification for following originals. + logger.log_debug("Ignored handling PDU delivery retransmitted notification for notif_sn={}", notif_sn); +} + uint32_t pdcp_entity_tx::notification_count_estimation(uint32_t notification_sn) { // Get lower edge of the window. If discard timer is enabled, use the lower edge of the tx_window, i.e. TX_NEXT_ACK. diff --git a/lib/pdcp/pdcp_entity_tx.h b/lib/pdcp/pdcp_entity_tx.h index cfe2f62942..00a42c010a 100644 --- a/lib/pdcp/pdcp_entity_tx.h +++ b/lib/pdcp/pdcp_entity_tx.h @@ -126,6 +126,8 @@ class pdcp_entity_tx final : public pdcp_entity_tx_rx_base, void handle_transmit_notification(uint32_t notif_sn) override; void handle_delivery_notification(uint32_t notif_sn) override; + void handle_retransmit_notification(uint32_t notif_sn) override; + void handle_delivery_retransmitted_notification(uint32_t notif_sn) override; /// \brief Evaluates a PDCP status report /// @@ -244,7 +246,7 @@ class pdcp_entity_tx final : public pdcp_entity_tx_rx_base, security::integrity_enabled integrity_enabled = security::integrity_enabled::off; security::ciphering_enabled ciphering_enabled = security::ciphering_enabled::off; - void write_data_pdu_to_lower_layers(uint32_t count, byte_buffer buf); + void write_data_pdu_to_lower_layers(uint32_t count, byte_buffer buf, bool is_retx); void write_control_pdu_to_lower_layers(byte_buffer buf); /// Apply ciphering and integrity protection to the payload diff --git a/lib/phy/generic_functions/CMakeLists.txt b/lib/phy/generic_functions/CMakeLists.txt index d08fc74694..f849780c9e 100644 --- a/lib/phy/generic_functions/CMakeLists.txt +++ b/lib/phy/generic_functions/CMakeLists.txt @@ -48,6 +48,4 @@ add_library(srsran_generic_funcs STATIC generic_functions_factories.cpp) set_source_files_properties(generic_functions_factories.cpp PROPERTIES COMPILE_DEFINITIONS "${SRSRAN_DFT_DEFINITIONS}") target_link_libraries(srsran_generic_funcs srsran_generic_funcs_dft srslog) -install(TARGETS srsran_generic_funcs - srsran_generic_funcs_dft - EXPORT srsran_export) \ No newline at end of file +add_to_exported_libs(srsran_generic_funcs srsran_generic_funcs_dft) diff --git a/lib/phy/generic_functions/precoding/CMakeLists.txt b/lib/phy/generic_functions/precoding/CMakeLists.txt index e2f2f41027..0bfe45330b 100644 --- a/lib/phy/generic_functions/precoding/CMakeLists.txt +++ b/lib/phy/generic_functions/precoding/CMakeLists.txt @@ -38,5 +38,4 @@ endif (${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") add_library(srsran_channel_precoder STATIC ${channel_precoder_sources}) -install(TARGETS srsran_channel_precoder - EXPORT srsran_export) +add_to_exported_libs(srsran_channel_precoder) diff --git a/lib/phy/support/CMakeLists.txt b/lib/phy/support/CMakeLists.txt index 226478b5f7..7d2a64fdf6 100644 --- a/lib/phy/support/CMakeLists.txt +++ b/lib/phy/support/CMakeLists.txt @@ -33,5 +33,4 @@ add_library(srsran_phy_support STATIC support_factories.cpp) target_link_libraries(srsran_phy_support srsvec srsran_instrumentation) -install(TARGETS srsran_phy_support - EXPORT srsran_export) \ No newline at end of file +add_to_exported_libs(srsran_phy_support) diff --git a/lib/phy/upper/CMakeLists.txt b/lib/phy/upper/CMakeLists.txt index 8729d6568b..653679fc8b 100644 --- a/lib/phy/upper/CMakeLists.txt +++ b/lib/phy/upper/CMakeLists.txt @@ -58,6 +58,4 @@ add_library(srsran_upper_phy_support vrb_to_prb_mapper.cpp ) -install(TARGETS log_likelihood_ratio - srsran_upper_phy_support - EXPORT srsran_export) +add_to_exported_libs(log_likelihood_ratio srsran_upper_phy_support) diff --git a/lib/phy/upper/channel_coding/CMakeLists.txt b/lib/phy/upper/channel_coding/CMakeLists.txt index 92e4aafefd..906d8a9bdf 100644 --- a/lib/phy/upper/channel_coding/CMakeLists.txt +++ b/lib/phy/upper/channel_coding/CMakeLists.txt @@ -41,9 +41,8 @@ target_link_libraries(srsran_channel_coding srsran_ldpc srsran_short_block) -install(TARGETS srsran_channel_coding +add_to_exported_libs(srsran_channel_coding srsran_crc_calculator srsran_polar srsran_ldpc - srsran_short_block - EXPORT srsran_export) + srsran_short_block) diff --git a/lib/phy/upper/channel_modulation/CMakeLists.txt b/lib/phy/upper/channel_modulation/CMakeLists.txt index c9237f04dc..d6336a95ea 100644 --- a/lib/phy/upper/channel_modulation/CMakeLists.txt +++ b/lib/phy/upper/channel_modulation/CMakeLists.txt @@ -37,5 +37,4 @@ endif (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") add_library(srsran_channel_modulation STATIC ${SOURCES}) target_link_libraries(srsran_channel_modulation srsran_support log_likelihood_ratio) -install(TARGETS srsran_channel_modulation - EXPORT srsran_export) +add_to_exported_libs(srsran_channel_modulation) diff --git a/lib/phy/upper/channel_modulation/avx2_helpers.h b/lib/phy/upper/channel_modulation/avx2_helpers.h index b0769f8fa6..1ef8a95aa2 100644 --- a/lib/phy/upper/channel_modulation/avx2_helpers.h +++ b/lib/phy/upper/channel_modulation/avx2_helpers.h @@ -28,7 +28,7 @@ namespace srsran { namespace mm256 { -/// \brief Absolute values. +/// \brief Computes absolute values. /// \param[in] value Input single-precision AVX register. /// \return A single-precision AVX register with the absolute value. inline __m256 abs_ps(__m256 value) @@ -37,7 +37,7 @@ inline __m256 abs_ps(__m256 value) return _mm256_and_ps(value, mask); } -/// \brief Copy sign from a single-precision AVX register. +/// \brief Copies sign from a single-precision AVX register. /// \param[in] value0 Single-precision AVX register. /// \param[in] value1 Single-precision AVX register. /// \return A single-precision AVX register with the magnitudes of \c value0 and the signs of \c value1. @@ -52,7 +52,7 @@ inline __m256 copysign_ps(__m256 value0, __m256 value1) /// \brief Clips the values of a single-precision AVX register. /// -/// The values greater than \c range_ceil or lower than \c range_floor are substituted by their corresponding range +/// Values greater than \c range_ceil or lower than \c range_floor are substituted by their corresponding range /// limits. /// /// \param[in] value Input values. @@ -68,7 +68,7 @@ inline __m256 clip_ps(__m256 value, __m256 range_ceil, __m256 range_floor) /// \brief Clips the values of an AVX register carrying eight signed 32-bit integers. /// -/// The values greater than \c range_ceil or lower than \c range_floor are substituted by their corresponding range +/// Values greater than \c range_ceil or lower than \c range_floor are substituted by their corresponding range /// limits. /// /// \param[in] value Input values. @@ -82,11 +82,11 @@ inline __m256i clip_epi32(__m256i value, __m256i range_ceil, __m256i range_floor return value; } -/// \brief Ensures that 32-bit integers are bounded, otherwise set them to zero. +/// \brief Ensures that 32-bit integers in an AVX register are bounded, otherwise set them to zero. /// /// \param[in] value Integers to be tested. -/// \param[in] bound_up Upper bound. -/// \param[in] bound_low Lower bound. +/// \param[in] bound_up Upper bounds. +/// \param[in] bound_low Lower bounds. /// \return The input integers with zeros in place of the out-of-bound values. inline __m256i check_bounds_epi32(__m256i value, __m256i bound_up, __m256i bound_low) { @@ -102,13 +102,15 @@ inline __m256i check_bounds_epi32(__m256i value, __m256i bound_up, __m256i bound return value; } -/// \brief Clips and quantizes four single-precision AVX registers, continuous log-likelihood ratio to the discrete -/// representation of type \c log_likelihood_ratio in a single AVX register. +/// \brief Clips and quantizes four single-precision AVX registers. /// -/// \param[in] value_0 Single-precision AVX register with the first eight continuous log-likelihood ratio. -/// \param[in] value_1 Single-precision AVX register with the second eight continuous log-likelihood ratio. -/// \param[in] value_2 Single-precision AVX register with the third eight continuous log-likelihood ratio. -/// \param[in] value_3 Single-precision AVX register with the fourth eight continuous log-likelihood ratio. +/// Each AVX register contains eight 32-bit single-precission log-likelihood ratios. These are converted into a +/// 8-bit discrete representation of type \c log_likelihood_ratio in a single AVX register. +/// +/// \param[in] value_0 Single-precision AVX register with the first eight log-likelihood ratios. +/// \param[in] value_1 Single-precision AVX register with the second eight log-likelihood ratios. +/// \param[in] value_2 Single-precision AVX register with the third eight log-likelihood ratios. +/// \param[in] value_3 Single-precision AVX register with the fourth eight log-likelihood ratios. /// \param[in] range_limit The input value mapped to \ref log_likelihood_ratio::max(). /// \return A quantized representation of the input values as \c log_likelihood_ratio quantity. /// \note The quantization in the range (-range_limit, range_limit) is [mid-tread @@ -165,11 +167,11 @@ inline __m256i quantize_ps(__m256 value_0, __m256 value_1, __m256 value_2, __m25 return _mm256_packs_epi16(llr_i16_0_, llr_i16_1_); } -/// \brief Helper function to calculate an interval index from single-precision AVX register values. +/// \brief Computes an interval index from single-precision AVX register values. /// \param[in] value Input AVX register. /// \param[in] interval_width Interval width. /// \param[in] nof_intervals Number of intervals. -/// \return An AVX register carrying eight signed 32-bit integers with the corresponding interval indexes. +/// \return An AVX register carrying eight signed 32-bit integers with the corresponding interval indices. inline __m256i compute_interval_idx(__m256 value, float interval_width, int nof_intervals) { // Scale. @@ -191,40 +193,41 @@ inline __m256i compute_interval_idx(__m256 value, float interval_width, int nof_ return idx; } -/// \brief Get values from a look-up table. +/// \brief Gets values from a look-up table. /// \param[in] table Look-up table containing eight single-precision values. -/// \param[in] indexes AVX register containing eight indexes. -/// \return A single-precision AVX register containing the eight values corresponding to the indexes. -inline __m256 look_up_table(const std::array& table, __m256i indexes) +/// \param[in] indices AVX register containing eight indices. +/// \return A single-precision AVX register containing the eight values corresponding to the given indices. +inline __m256 look_up_table(const std::array& table, __m256i indices) { - return _mm256_permutevar8x32_ps(_mm256_loadu_ps(table.data()), indexes); + return _mm256_permutevar8x32_ps(_mm256_loadu_ps(table.data()), indices); } -/// \brief Get values from a look-up table. +/// \brief Gets values from a look-up table. /// \param[in] table Look-up table containing sixteen single-precision values. -/// \param[in] indexes AVX register containing eight indexes. -/// \return A single-precision AVX register containing the eight values corresponding to the indexes. -inline __m256 look_up_table(const std::array& table, __m256i indexes) +/// \param[in] indices AVX register containing eight indices. +/// \return A single-precision AVX register containing the eight values corresponding to the given indices. +inline __m256 look_up_table(const std::array& table, __m256i indices) { // Get lower part of the table. - __m256 lower = _mm256_permutevar8x32_ps(_mm256_loadu_ps(table.data()), indexes); + __m256 lower = _mm256_permutevar8x32_ps(_mm256_loadu_ps(table.data()), indices); // Get upper part of the table. - __m256 upper = _mm256_permutevar8x32_ps(_mm256_loadu_ps(table.data() + 8), indexes); + __m256 upper = _mm256_permutevar8x32_ps(_mm256_loadu_ps(table.data() + 8), indices); // Generate mask. Set to true if the MSB of the index is greater than zero. - __m256i mask = _mm256_cmpgt_epi32(indexes, _mm256_set1_epi32(0b111)); + __m256i mask = _mm256_cmpgt_epi32(indices, _mm256_set1_epi32(0b111)); // Select upper or lower value. return _mm256_blendv_ps(lower, upper, (__m256)mask); } -/// \brief Applies an interval function to a series of values. +/// \brief Applies a piecewise defined function (provided by look-up tables) to a series of values. /// \tparam Table Look-up table type. All tables mut be of the same type. /// \param[in] value Single-precision AVX register with eight input values. -/// \param[in] rcp_noise Single-precision AVX register with the reciprocal noise corresponding to the values. +/// \param[in] rcp_noise Single-precision AVX register with the reciprocal of the noise variance corresponding to +/// the values. /// \param[in] nof_intervals Number of intervals. -/// \param[in] interval_width Interval width to quantify the interval indexes. +/// \param[in] interval_width Interval width to quantify the interval indices. /// \param[in] slopes Table with the slope of each interval. /// \param[in] intercepts Table with the interception points of each interval. /// \return A single-precision AVX register containing the results of the interval function. @@ -256,15 +259,15 @@ inline __m256 interval_function(__m256 value, inline __m256 safe_div(__m256 dividend, __m256 divisor) { static const __m256 all_zero = _mm256_setzero_ps(); - // _CMP_GT_OQ: compare greater than, ordered (nan is false) and quite (no exceptions raised). + // _CMP_GT_OQ: compare greater than, ordered (nan is false) and quiet (no exceptions raised). #if defined(__AVX512F__) && defined(__AVX512VL__) __mmask8 mask = _mm256_cmp_ps_mask(divisor, all_zero, _CMP_GT_OQ); return _mm256_maskz_div_ps(mask, dividend, divisor); -#else +#else // defined(__AVX512F__) && defined(__AVX512VL__) __m256 mask = _mm256_cmp_ps(divisor, all_zero, _CMP_GT_OQ); __m256 result = _mm256_div_ps(dividend, divisor); return _mm256_blendv_ps(all_zero, result, mask); -#endif +#endif // defined(__AVX512F__) && defined(__AVX512VL__) } } // namespace mm256 diff --git a/lib/phy/upper/channel_modulation/avx512_helpers.h b/lib/phy/upper/channel_modulation/avx512_helpers.h new file mode 100644 index 0000000000..eea57fa08f --- /dev/null +++ b/lib/phy/upper/channel_modulation/avx512_helpers.h @@ -0,0 +1,333 @@ +/* + * + * 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 + +#ifndef __AVX512F__ +#error "Architecture missmatch. Missing avx512f." +#endif // __AVX512F__ + +#ifndef __AVX512BW__ +#error "Architecture missmatch. Missing avx512bw." +#endif // __AVX512BW__ + +#ifndef __AVX512DQ__ +#error "Architecture missmatch. Missing avx512dq." +#endif // __AVX512DQ__ + +#ifndef __AVX512VBMI__ +#error "Architecture missmatch. Missing avx512vbmi." +#endif // __AVX512VBMI__ + +#if defined(__GNUC__) && (__GNUC__ <= 9) +#error "GCC version must be greater than 9." +#endif // defined(__GNUC__) && (__GNUC__ <= 9) + +namespace srsran { + +namespace mm512 { + +/// \brief Computes absolute values. +/// \param[in] value Input single-precision AVX512 register. +/// \return A single-precision AVX512 register with the absolute value. +inline __m512 abs_ps(__m512 value) +{ + const __m512 mask = _mm512_castsi512_ps(_mm512_set1_epi32(0x7fffffff)); + return _mm512_and_ps(value, mask); +} + +/// \brief Copies sign from a single-precision AVX512 register. +/// \param[in] value0 Single-precision AVX512 register. +/// \param[in] value1 Single-precision AVX512 register. +/// \return A single-precision AVX512 register with the magnitudes of \c value0 and the signs of \c value1. +/// \remark A zero in the second argument is considered as a positive number, following the convention of \c +/// std::copysign. +inline __m512 copysign_ps(__m512 value0, __m512 value1) +{ + __m512 abs_value0 = abs_ps(value0); + __m512 sign_value1 = _mm512_and_ps(value1, _mm512_set1_ps(-0.0)); + return _mm512_or_ps(abs_value0, sign_value1); +} + +/// \brief Clips the values of a single-precision AVX512 register. +/// +/// Values greater than \c range_ceil or lower than \c range_floor are substituted by their corresponding range +/// limits. +/// +/// \param[in] value Input values. +/// \param[in] range_ceil Ceiling values. +/// \param[in] range_floor Floor values. +/// \return A single-precision AVX512 register containing the clipped values. +inline __m512 clip_ps(__m512 value, __m512 range_ceil, __m512 range_floor) +{ + value = _mm512_mask_blend_ps(_mm512_cmp_ps_mask(value, range_ceil, _CMP_GT_OS), value, range_ceil); + value = _mm512_mask_blend_ps(_mm512_cmp_ps_mask(value, range_floor, _CMP_LT_OS), value, range_floor); + return value; +} + +/// \brief Clips the values of an AVX512 register carrying sixteen signed 32-bit integers. +/// +/// Values greater than \c range_ceil or lower than \c range_floor are substituted by their corresponding range +/// limits. +/// +/// \param[in] value Input values. +/// \param[in] range_ceil Ceiling values. +/// \param[in] range_floor Floor values. +/// \return An AVX512 register containing the clipped values. +inline __m512i clip_epi32(__m512i value, __m512i range_ceil, __m512i range_floor) +{ + value = _mm512_mask_blend_epi32(_mm512_cmp_epi32_mask(value, range_ceil, _MM_CMPINT_NLT), value, range_ceil); + value = _mm512_mask_blend_epi32(_mm512_cmp_epi32_mask(range_floor, value, _MM_CMPINT_NLT), value, range_floor); + return value; +} + +/// \brief Ensures that 32-bit integers in an AVX512 register are bounded, otherwise set them to zero. +/// +/// \param[in] value Integers to be tested. +/// \param[in] bound_up Upper bounds. +/// \param[in] bound_low Lower bounds. +/// \return The input integers with zeros in place of the out-of-bound values. +inline __m512i check_bounds_epi32(__m512i value, __m512i bound_up, __m512i bound_low) +{ + __m512i ZEROS = _mm512_set1_epi32(0); + __mmask16 mask = _mm512_cmp_epi32_mask(bound_up, value, _MM_CMPINT_LT); + // Since the mask is set for all bytes of the int32_t integers, we can use the byte-wise blend. + value = _mm512_mask_blend_epi32(mask, value, ZEROS); + + mask = _mm512_cmp_epi32_mask(value, bound_low, _MM_CMPINT_LT); + // Since the mask is set for all bytes of the int32_t integers, we can use the byte-wise blend. + value = _mm512_mask_blend_epi32(mask, value, ZEROS); + + return value; +} + +/// \brief Clips and quantizes four single-precision AVX512 registers. +/// +/// Each AVX512 register contains sixteen 32-bit single-precission log-likelihood ratios. These are converted into a +/// 8-bit discrete representation of type \c log_likelihood_ratio in a single AVX512 register. +/// +/// \param[in] value_0 Single-precision AVX512 register with the first eight log-likelihood ratios. +/// \param[in] value_1 Single-precision AVX512 register with the second eight log-likelihood ratios. +/// \param[in] value_2 Single-precision AVX512 register with the third eight log-likelihood ratios. +/// \param[in] value_3 Single-precision AVX512 register with the fourth eight log-likelihood ratios. +/// \param[in] range_limit The input value mapped to \ref log_likelihood_ratio::max(). +/// \return A quantized representation of the input values as \c log_likelihood_ratio quantity. +/// \note The quantization in the range (-range_limit, range_limit) is [mid-tread +/// uniform](https://en.wikipedia.org/wiki/Quantization_(signal_processing)#Mid-riser_and_mid-tread_uniform_quantizers), +/// with quantization step range_limit / LLR_MAX . +/// \note All values larger (in magnitude) than \c range_limit, will be clipped and mapped to +/// ±LLR_MAX, depending on their sign. +inline __m512i quantize_ps(__m512 value_0, __m512 value_1, __m512 value_2, __m512 value_3, float range_limit) +{ + // Scale. + __m512 SCALE = _mm512_set1_ps(static_cast(log_likelihood_ratio::max().to_int()) / range_limit); + value_0 = _mm512_mul_ps(value_0, SCALE); + value_1 = _mm512_mul_ps(value_1, SCALE); + value_2 = _mm512_mul_ps(value_2, SCALE); + value_3 = _mm512_mul_ps(value_3, SCALE); + + // Clip and round to the nearest integer. + __m512 RANGE_CEIL = _mm512_set1_ps(log_likelihood_ratio::max().to_int()); + __m512 RANGE_FLOOR = _mm512_set1_ps(log_likelihood_ratio::min().to_int()); + value_0 = _mm512_roundscale_ps(clip_ps(value_0, RANGE_CEIL, RANGE_FLOOR), _MM_FROUND_TO_NEAREST_INT); + value_1 = _mm512_roundscale_ps(clip_ps(value_1, RANGE_CEIL, RANGE_FLOOR), _MM_FROUND_TO_NEAREST_INT); + value_2 = _mm512_roundscale_ps(clip_ps(value_2, RANGE_CEIL, RANGE_FLOOR), _MM_FROUND_TO_NEAREST_INT); + value_3 = _mm512_roundscale_ps(clip_ps(value_3, RANGE_CEIL, RANGE_FLOOR), _MM_FROUND_TO_NEAREST_INT); + + // Convert to 32 bit. + __m512i llr_i32_0 = _mm512_cvtps_epi32(value_0); + __m512i llr_i32_1 = _mm512_cvtps_epi32(value_1); + __m512i llr_i32_2 = _mm512_cvtps_epi32(value_2); + __m512i llr_i32_3 = _mm512_cvtps_epi32(value_3); + + // Check bounds one more time: if value_X contains a NaN, its casting to int32_t is indeterminate - set it to zero. + __m512i BOUND_UP = _mm512_set1_epi32(log_likelihood_ratio::max().to_int()); + __m512i BOUND_LOW = _mm512_set1_epi32(log_likelihood_ratio::min().to_int()); + llr_i32_0 = check_bounds_epi32(llr_i32_0, BOUND_UP, BOUND_LOW); + llr_i32_1 = check_bounds_epi32(llr_i32_1, BOUND_UP, BOUND_LOW); + llr_i32_2 = check_bounds_epi32(llr_i32_2, BOUND_UP, BOUND_LOW); + llr_i32_3 = check_bounds_epi32(llr_i32_3, BOUND_UP, BOUND_LOW); + + // Conversion to 16 bit. + __m512i llr_i16_0 = _mm512_packs_epi32(llr_i32_0, llr_i32_1); + __m512i llr_i16_1 = _mm512_packs_epi32(llr_i32_2, llr_i32_3); + + // Conversion to 8 bit. + return _mm512_packs_epi16(llr_i16_0, llr_i16_1); +} + +/// \brief Clips and quantizes three single-precision AVX512 registers. +/// +/// Each AVX512 register contains sixteen 32-bit single-precission log-likelihood ratios. These are converted into a +/// 8-bit discrete representation of type \c log_likelihood_ratio in a single AVX512 register. +/// +/// \param[in] value_0 Single-precision AVX512 register with the first eight log-likelihood ratios. +/// \param[in] value_1 Single-precision AVX512 register with the second eight log-likelihood ratios. +/// \param[in] value_2 Single-precision AVX512 register with the third eight log-likelihood ratios. +/// \param[in] range_limit The input value mapped to \ref log_likelihood_ratio::max(). +/// \return A quantized representation of the input values as \c log_likelihood_ratio quantity. +/// \note The quantization in the range (-range_limit, range_limit) is [mid-tread +/// uniform](https://en.wikipedia.org/wiki/Quantization_(signal_processing)#Mid-riser_and_mid-tread_uniform_quantizers), +/// with quantization step range_limit / LLR_MAX . +/// \note All values larger (in magnitude) than \c range_limit, will be clipped and mapped to +/// ±LLR_MAX, depending on their sign. +inline __m512i quantize_ps(__m512 value_0, __m512 value_1, __m512 value_2, float range_limit) +{ + // Scale. + __m512 SCALE = _mm512_set1_ps(static_cast(log_likelihood_ratio::max().to_int()) / range_limit); + value_0 = _mm512_mul_ps(value_0, SCALE); + value_1 = _mm512_mul_ps(value_1, SCALE); + value_2 = _mm512_mul_ps(value_2, SCALE); + + // Clip and round to the nearest integer. + __m512 RANGE_CEIL = _mm512_set1_ps(log_likelihood_ratio::max().to_int()); + __m512 RANGE_FLOOR = _mm512_set1_ps(log_likelihood_ratio::min().to_int()); + value_0 = _mm512_roundscale_ps(clip_ps(value_0, RANGE_CEIL, RANGE_FLOOR), _MM_FROUND_TO_NEAREST_INT); + value_1 = _mm512_roundscale_ps(clip_ps(value_1, RANGE_CEIL, RANGE_FLOOR), _MM_FROUND_TO_NEAREST_INT); + value_2 = _mm512_roundscale_ps(clip_ps(value_2, RANGE_CEIL, RANGE_FLOOR), _MM_FROUND_TO_NEAREST_INT); + + // Convert to 32 bit. + __m512i llr_i32_0 = _mm512_cvtps_epi32(value_0); + __m512i llr_i32_1 = _mm512_cvtps_epi32(value_1); + __m512i llr_i32_2 = _mm512_cvtps_epi32(value_2); + + // Check bounds one more time: if value_X contains a NaN, its casting to int32_t is indeterminate - set it to zero. + __m512i BOUND_UP = _mm512_set1_epi32(log_likelihood_ratio::max().to_int()); + __m512i BOUND_LOW = _mm512_set1_epi32(log_likelihood_ratio::min().to_int()); + llr_i32_0 = check_bounds_epi32(llr_i32_0, BOUND_UP, BOUND_LOW); + llr_i32_1 = check_bounds_epi32(llr_i32_1, BOUND_UP, BOUND_LOW); + llr_i32_2 = check_bounds_epi32(llr_i32_2, BOUND_UP, BOUND_LOW); + + // Conversion to 16 bit. + __m512i llr_i16_0 = _mm512_packs_epi32(llr_i32_0, llr_i32_1); + __m512i llr_i16_1 = _mm512_packs_epi32(llr_i32_2, _mm512_setzero_si512()); + + // Conversion to 8 bit. + return _mm512_packs_epi16(llr_i16_0, llr_i16_1); +} + +/// \brief Computes an interval index from single-precision AVX512 register values. +/// \param[in] value Input AVX512 register. +/// \param[in] interval_width Interval width. +/// \param[in] nof_intervals Number of intervals. +/// \return An AVX512 register carrying sixteen signed 32-bit integers with interval indices of the corresponding +/// values. +inline __m512i compute_interval_idx(__m512 value, float interval_width, int nof_intervals) +{ + // Scale. + value = _mm512_mul_ps(value, _mm512_set1_ps(1.0F / interval_width)); + + // Round to the lowest integer. + value = _mm512_roundscale_ps(value, _MM_FROUND_TO_NEG_INF); + + // Convert to int32. + __m512i idx = _mm512_cvtps_epi32(value); + + // Add interval offset. + idx = _mm512_add_epi32(idx, _mm512_set1_epi32(nof_intervals / 2)); + + // Clip index. + idx = clip_epi32(idx, _mm512_set1_epi32(nof_intervals - 1), _mm512_setzero_si512()); + + // Clip integer and return. + return idx; +} + +/// \brief Gets values from a look-up table. +/// \param[in] table Look-up table containing sixteen single-precision values. +/// \param[in] indices AVX512 register containing sixteen indices. +/// \return A single-precision AVX512 register containing the sixteen values corresponding to the given indices. +inline __m512 look_up_table(const std::array& table, __m512i indices) +{ + // Load table in 256-bit register. + __m256 table_m256 = _mm256_loadu_ps(table.data()); + // Cast the 256-bit register into a 512-bit register. + __m512 table_m512 = _mm512_zextps256_ps512(table_m256); + // Read the table. + return _mm512_permutexvar_ps(indices, table_m512); +} + +/// \brief Gets values from a look-up table. +/// \param[in] table Look-up table containing sixteen single-precision values. +/// \param[in] indices AVX512 register containing sixteen indices. +/// \return A single-precision AVX512 register containing the sixteen values corresponding to the given indices. +inline __m512 look_up_table(const std::array& table, __m512i indices) +{ + // Load the entire table into a 512-bit register. + __m512 table_m512 = _mm512_loadu_ps(table.data()); + // Read the table. + return _mm512_permutexvar_ps(indices, table_m512); +} + +/// \brief Applies a piecewise defined function (provided by look-up tables) to a series of values. +/// \tparam Table Look-up table type. All tables mut be of the same type. +/// \param[in] value Single-precision AVX512 register with sixteen input values. +/// \param[in] rcp_noise Single-precision AVX512 register with the reciprocal of the noise variance corresponding +/// to the values. +/// \param[in] nof_intervals Number of intervals. +/// \param[in] interval_width Interval width to quantify the interval indices. +/// \param[in] slopes Table with the slope of each interval. +/// \param[in] intercepts Table with the interception points of each interval. +/// \return A single-precision AVX512 register containing the results of the interval function. +/// \remark The number of intervals must be lower than or equal to \c Table size. +template +inline __m512 interval_function(__m512 value, + __m512 rcp_noise, + float interval_width, + unsigned nof_intervals, + const Table& slopes, + const Table& intercepts) +{ + __m512i interval_index = compute_interval_idx(value, interval_width, nof_intervals); + + __m512 slope = mm512::look_up_table(slopes, interval_index); + __m512 intercept = mm512::look_up_table(intercepts, interval_index); + + __m512 result = _mm512_mul_ps(_mm512_add_ps(_mm512_mul_ps(slope, value), intercept), rcp_noise); + + __m512 zero_thr = _mm512_set1_ps(near_zero); + __mmask16 zero_mask = _mm512_cmp_ps_mask(abs_ps(value), zero_thr, _CMP_LE_OQ); + + return _mm512_mask_blend_ps(zero_mask, result, _mm512_setzero_ps()); +} + +/// \brief Safe division. +/// +/// \return dividend / divisor if \c divisor is greater than zero, \c 0 otherwise. +inline __m256 safe_div(__m256 dividend, __m256 divisor) +{ + static const __m256 all_zero = _mm256_setzero_ps(); + // _CMP_GT_OQ: compare greater than, ordered (nan is false) and quiet (no exceptions raised). +#ifdef __AVX512VL__ + __mmask8 mask = _mm256_cmp_ps_mask(divisor, all_zero, _CMP_GT_OQ); + return _mm256_maskz_div_ps(mask, dividend, divisor); +#else // __AVX512VL__ + __m256 mask = _mm256_cmp_ps(divisor, all_zero, _CMP_GT_OQ); + __m256 result = _mm256_div_ps(dividend, divisor); + return _mm256_blendv_ps(all_zero, result, mask); +#endif // __AVX512VL__ +} + +} // namespace mm512 +} // namespace srsran diff --git a/lib/phy/upper/channel_modulation/demodulation_mapper_qam16.cpp b/lib/phy/upper/channel_modulation/demodulation_mapper_qam16.cpp index 4a42bd4de3..b53f7ebfcd 100644 --- a/lib/phy/upper/channel_modulation/demodulation_mapper_qam16.cpp +++ b/lib/phy/upper/channel_modulation/demodulation_mapper_qam16.cpp @@ -51,7 +51,7 @@ static void demod_QAM16_avx2(log_likelihood_ratio* llr, const cf_t* symbol, cons // Load noise. __m256 noise_0 = _mm256_loadu_ps(noise_var + 0); - // Make noise reciprocal. + // Take the reciprocal of the noise variance. __m256 rcp_noise_0 = mm256::safe_div(_mm256_set1_ps(1), noise_0); // Repeat noise values for real and imaginary parts. @@ -132,7 +132,7 @@ static void demod_QAM16_neon(log_likelihood_ratio* llr, const cf_t* symbol, cons // Load noise. float32x4_t noise_0 = vld1q_f32(noise_var); - // Make noise reciprocal. + // Take the reciprocal of the noise variance. float32x4_t rcp_noise_0 = neon::safe_div(vdupq_n_f32(1.0f), noise_0); // Repeat noise values for real and imaginary parts. diff --git a/lib/phy/upper/channel_modulation/demodulation_mapper_qam256.cpp b/lib/phy/upper/channel_modulation/demodulation_mapper_qam256.cpp index 5fb4befe4a..1c9a388003 100644 --- a/lib/phy/upper/channel_modulation/demodulation_mapper_qam256.cpp +++ b/lib/phy/upper/channel_modulation/demodulation_mapper_qam256.cpp @@ -23,6 +23,12 @@ #include "demodulation_mapper_intervals.h" #include "srsran/phy/upper/log_likelihood_ratio.h" +#if defined(__AVX512F__) && defined(__AVX512BW__) && defined(__AVX512DQ__) && defined(__AVX512VBMI__) && \ + (!defined(__GNUC__) || (__GNUC__ > 9)) +#define HAVE_AVX512 +#include "avx512_helpers.h" +#endif + #ifdef __AVX2__ #include "avx2_helpers.h" #endif // __AVX2__ @@ -154,6 +160,56 @@ const std::array SLOPE_67 = {4 * M_SQRT1_170, const std::array INTERCEPT_67 = {28.0F / 85, -20.0F / 85, 12.0F / 85, -4.0F / 85, -4.0F / 85, 12.0F / 85, -20.0F / 85, 28.0F / 85}; +#ifdef HAVE_AVX512 +static void demod_QAM256_avx512(log_likelihood_ratio* llr, const cf_t* symbol, const float* noise_var) +{ + __m512i rcp_noise_idx = _mm512_setr_epi32(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7); + + // Load symbols. + __m512 symbols = _mm512_loadu_ps(reinterpret_cast(symbol)); + + // Load noise. 8 values only. + __m256 noise = _mm256_loadu_ps(noise_var); + + // Take the reciprocal of the noise variance. + // _CMP_GT_OQ: compare greater than, ordered (nan is false) and quiet (no exceptions raised). + __m256 rcp_noise = mm512::safe_div(_mm256_set1_ps(1), noise); + + // Repeat noise values for real and imaginary parts and put the results in an AVX register. + __m512 rcp_noise_ = _mm512_zextps256_ps512(rcp_noise); + rcp_noise_ = _mm512_permutexvar_ps(rcp_noise_idx, rcp_noise_); + + // Calculate l_value for bits 0 and 1. + __m512 l_value_01 = + mm512::interval_function(symbols, rcp_noise_, INTERVAL_WIDTH_01, NOF_INTERVALS_01, SLOPE_01, INTERCEPT_01); + + // Calculate l_value for bits 2 and 3. + __m512 l_value_23 = + mm512::interval_function(symbols, rcp_noise_, INTERVAL_WIDTH_23, NOF_INTERVALS_23, SLOPE_23, INTERCEPT_23); + + // Calculate l_value for bits 4 and 5. + __m512 l_value_45 = + mm512::interval_function(symbols, rcp_noise_, INTERVAL_WIDTH_45, NOF_INTERVALS_45, SLOPE_45, INTERCEPT_45); + + // Calculate l_value for bits 6 and 7. + __m512 l_value_67 = + mm512::interval_function(symbols, rcp_noise_, INTERVAL_WIDTH_67, NOF_INTERVALS_67, SLOPE_67, INTERCEPT_67); + + // Store result. + __m512i llr_i8 = mm512::quantize_ps(l_value_01, l_value_23, l_value_45, l_value_67, RANGE_LIMIT_FLOAT); + + // Recolocate LLR within the 512-bit register. + uint8_t idx_[64] = {0x00, 0x01, 0x04, 0x05, 0x08, 0x09, 0x0c, 0x0d, 0x02, 0x03, 0x06, 0x07, 0x0a, 0x0b, 0x0e, 0x0f, + 0x10, 0x11, 0x14, 0x15, 0x18, 0x19, 0x1c, 0x1d, 0x12, 0x13, 0x16, 0x17, 0x1a, 0x1b, 0x1e, 0x1f, + 0x20, 0x21, 0x24, 0x25, 0x28, 0x29, 0x2c, 0x2d, 0x22, 0x23, 0x26, 0x27, 0x2a, 0x2b, 0x2e, 0x2f, + 0x30, 0x31, 0x34, 0x35, 0x38, 0x39, 0x3c, 0x3d, 0x32, 0x33, 0x36, 0x37, 0x3a, 0x3b, 0x3e, 0x3f}; + __m512i idx = _mm512_loadu_si512(idx_); + __m512i reordered = _mm512_permutexvar_epi8(idx, llr_i8); + + _mm512_storeu_si512(reinterpret_cast<__m512i*>(llr), reordered); +} +#endif // HAVE_AVX512 + #ifdef __AVX2__ static void demod_QAM256_avx2(log_likelihood_ratio* llr, const cf_t* symbol, const float* noise_var) { @@ -163,8 +219,8 @@ static void demod_QAM256_avx2(log_likelihood_ratio* llr, const cf_t* symbol, con // Load noise. 4 values only. __m128 noise = _mm_loadu_ps(noise_var); - // Make noise reciprocal. - // _CMP_GT_OQ: compare greater than, ordered (nan is false) and quite (no exceptions raised). + // Take the reciprocal of the noise variance. + // _CMP_GT_OQ: compare greater than, ordered (nan is false) and quiet (no exceptions raised). __m128 mask = _mm_cmp_ps(noise, _mm_set1_ps(0), _CMP_GT_OQ); __m128 rcp_noise = _mm_div_ps(_mm_set1_ps(1), noise); rcp_noise = _mm_blendv_ps(_mm_set1_ps(0), rcp_noise, mask); @@ -216,7 +272,7 @@ static void demod_QAM256_neon(log_likelihood_ratio* llr, const cf_t* symbol, con // Load noise. float32x4_t noise_0 = vld1q_f32(noise_var); - // Make noise reciprocal. + // Take the reciprocal of the noise variance. float32x4_t rcp_noise_0 = neon::safe_div(vdupq_n_f32(1.0f), noise_0); // Repeat noise values for real and imaginary parts. @@ -308,6 +364,17 @@ void srsran::demodulate_soft_QAM256(span llrs, log_likelihood_ratio* llr_it = llrs.begin(); std::size_t symbol_index = 0; +#ifdef HAVE_AVX512 + // For AVX512, it generates 64 LLRs simultaneously. The input is read in batches of 8 symbols. + for (std::size_t symbol_index_end = (symbols.size() / 8) * 8; symbol_index != symbol_index_end; symbol_index += 8) { + demod_QAM256_avx512(llr_it, symbols_it, noise_it); + + llr_it += 64; + symbols_it += 8; + noise_it += 8; + } +#endif // HAVE_AVX512 + #ifdef __AVX2__ // For AVX2, it generates 32 LLRs simultaneously. The input is read in batches of 4 symbols. for (std::size_t symbol_index_end = (symbols.size() / 4) * 4; symbol_index != symbol_index_end; symbol_index += 4) { diff --git a/lib/phy/upper/channel_modulation/demodulation_mapper_qam64.cpp b/lib/phy/upper/channel_modulation/demodulation_mapper_qam64.cpp index df7cfd604b..0e3737da46 100644 --- a/lib/phy/upper/channel_modulation/demodulation_mapper_qam64.cpp +++ b/lib/phy/upper/channel_modulation/demodulation_mapper_qam64.cpp @@ -23,6 +23,12 @@ #include "demodulation_mapper_intervals.h" #include "srsran/phy/upper/log_likelihood_ratio.h" +#if defined(__AVX512F__) && defined(__AVX512BW__) && defined(__AVX512DQ__) && defined(__AVX512VBMI__) && \ + (!defined(__GNUC__) || (__GNUC__ > 9)) +#define HAVE_AVX512 +#include "avx512_helpers.h" +#endif + #ifdef __AVX2__ #include "avx2_helpers.h" #endif // __AVX2__ @@ -71,8 +77,111 @@ static const std::array SLOPE_45 = {4 * M_SQRT1_42, -4 * M_SQRT1_42, 4 * M_SQRT1_42, -4 * M_SQRT1_42, 0, 0, 0, 0}; static const std::array INTERCEPT_45 = {12.0F / 21, -4.0F / 21, -4.0F / 21, 12.0F / 21, 0, 0, 0, 0}; -#ifdef __AVX2__ +#ifdef HAVE_AVX512 +static void demod_QAM64_avx512(log_likelihood_ratio* llr, const cf_t* symbol, const float* noise_var) +{ + __m512i rcp_noise_idx = _mm512_setr_epi32(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7); + + // Load symbols. + __m512 symbols_0 = _mm512_loadu_ps(reinterpret_cast(symbol + 0)); + __m512 symbols_1 = _mm512_loadu_ps(reinterpret_cast(symbol + 8)); + __m512 symbols_2 = _mm512_loadu_ps(reinterpret_cast(symbol + 16)); + __m512 symbols_3 = _mm512_loadu_ps(reinterpret_cast(symbol + 24)); + + // Load noise. + __m256 noise_0 = _mm256_loadu_ps(noise_var + 0); + __m256 noise_1 = _mm256_loadu_ps(noise_var + 8); + __m256 noise_2 = _mm256_loadu_ps(noise_var + 16); + __m256 noise_3 = _mm256_loadu_ps(noise_var + 24); + + // Take the reciprocal of the noise variance. + __m256 rcp_noise_si256_0 = mm512::safe_div(_mm256_set1_ps(1), noise_0); + __m256 rcp_noise_si256_1 = mm512::safe_div(_mm256_set1_ps(1), noise_1); + __m256 rcp_noise_si256_2 = mm512::safe_div(_mm256_set1_ps(1), noise_2); + __m256 rcp_noise_si256_3 = mm512::safe_div(_mm256_set1_ps(1), noise_3); + + // Repeat noise values for real and imaginary parts and put the results in an AVX512 register. + __m512 rcp_noise_0 = _mm512_permutexvar_ps(rcp_noise_idx, _mm512_zextps256_ps512(rcp_noise_si256_0)); + __m512 rcp_noise_1 = _mm512_permutexvar_ps(rcp_noise_idx, _mm512_zextps256_ps512(rcp_noise_si256_1)); + __m512 rcp_noise_2 = _mm512_permutexvar_ps(rcp_noise_idx, _mm512_zextps256_ps512(rcp_noise_si256_2)); + __m512 rcp_noise_3 = _mm512_permutexvar_ps(rcp_noise_idx, _mm512_zextps256_ps512(rcp_noise_si256_3)); + // Calculate l_value for bits 0 and 1. + __m512 l_value_01_0 = + mm512::interval_function(symbols_0, rcp_noise_0, INTERVAL_WIDTH_01, NOF_INTERVALS_01, SLOPE_01, INTERCEPT_01); + __m512 l_value_01_1 = + mm512::interval_function(symbols_1, rcp_noise_1, INTERVAL_WIDTH_01, NOF_INTERVALS_01, SLOPE_01, INTERCEPT_01); + __m512 l_value_01_2 = + mm512::interval_function(symbols_2, rcp_noise_2, INTERVAL_WIDTH_01, NOF_INTERVALS_01, SLOPE_01, INTERCEPT_01); + __m512 l_value_01_3 = + mm512::interval_function(symbols_3, rcp_noise_3, INTERVAL_WIDTH_01, NOF_INTERVALS_01, SLOPE_01, INTERCEPT_01); + + // Calculate l_value for bits 2 and 3. + __m512 l_value_23_0 = + mm512::interval_function(symbols_0, rcp_noise_0, INTERVAL_WIDTH_23, NOF_INTERVALS_23, SLOPE_23, INTERCEPT_23); + __m512 l_value_23_1 = + mm512::interval_function(symbols_1, rcp_noise_1, INTERVAL_WIDTH_23, NOF_INTERVALS_23, SLOPE_23, INTERCEPT_23); + __m512 l_value_23_2 = + mm512::interval_function(symbols_2, rcp_noise_2, INTERVAL_WIDTH_23, NOF_INTERVALS_23, SLOPE_23, INTERCEPT_23); + __m512 l_value_23_3 = + mm512::interval_function(symbols_3, rcp_noise_3, INTERVAL_WIDTH_23, NOF_INTERVALS_23, SLOPE_23, INTERCEPT_23); + + // Calculate l_value for bits 4 and 5. + __m512 l_value_45_0 = + mm512::interval_function(symbols_0, rcp_noise_0, INTERVAL_WIDTH_45, NOF_INTERVALS_45, SLOPE_45, INTERCEPT_45); + __m512 l_value_45_1 = + mm512::interval_function(symbols_1, rcp_noise_1, INTERVAL_WIDTH_45, NOF_INTERVALS_45, SLOPE_45, INTERCEPT_45); + __m512 l_value_45_2 = + mm512::interval_function(symbols_2, rcp_noise_2, INTERVAL_WIDTH_45, NOF_INTERVALS_45, SLOPE_45, INTERCEPT_45); + __m512 l_value_45_3 = + mm512::interval_function(symbols_3, rcp_noise_3, INTERVAL_WIDTH_45, NOF_INTERVALS_45, SLOPE_45, INTERCEPT_45); + + // Quantize LLRs. + __m512i llr_i8_0 = mm512::quantize_ps(l_value_01_0, l_value_23_0, l_value_45_0, RANGE_LIMIT_FLOAT); + __m512i llr_i8_1 = mm512::quantize_ps(l_value_01_1, l_value_23_1, l_value_45_1, RANGE_LIMIT_FLOAT); + __m512i llr_i8_2 = mm512::quantize_ps(l_value_01_2, l_value_23_2, l_value_45_2, RANGE_LIMIT_FLOAT); + __m512i llr_i8_3 = mm512::quantize_ps(l_value_01_3, l_value_23_3, l_value_45_3, RANGE_LIMIT_FLOAT); + + // Reorganize 8-bit LLRs in three 512-bit registers. + uint8_t idx0_[64] = { + 0x00, 0x01, 0x04, 0x05, 0x08, 0x09, 0x02, 0x03, 0x06, 0x07, 0x0a, 0x0b, // 0 - 11 + 0x10, 0x11, 0x14, 0x15, 0x18, 0x19, 0x12, 0x13, 0x16, 0x17, 0x1a, 0x1b, // 12 - 23 + 0x20, 0x21, 0x24, 0x25, 0x28, 0x29, 0x22, 0x23, 0x26, 0x27, 0x2a, 0x2b, // 24 - 35 + 0x30, 0x31, 0x34, 0x35, 0x38, 0x39, 0x32, 0x33, 0x36, 0x37, 0x3a, 0x3b, // 36 - 47 + 0x40, 0x41, 0x44, 0x45, 0x48, 0x49, 0x42, 0x43, 0x46, 0x47, 0x4a, 0x4b, // 48 - 59 + 0x50, 0x51, 0x54, 0x55 // 60 - 63 + }; + uint8_t idx1_[64] = { + 0x18, 0x19, 0x12, 0x13, 0x16, 0x17, 0x1a, 0x1b, // 0 - 7 + 0x20, 0x21, 0x24, 0x25, 0x28, 0x29, 0x22, 0x23, 0x26, 0x27, 0x2a, 0x2b, // 8 - 19 + 0x30, 0x31, 0x34, 0x35, 0x38, 0x39, 0x32, 0x33, 0x36, 0x37, 0x3a, 0x3b, // 20 - 31 + 0x40, 0x41, 0x44, 0x45, 0x48, 0x49, 0x42, 0x43, 0x46, 0x47, 0x4a, 0x4b, // 32 - 43 + 0x50, 0x51, 0x54, 0x55, 0x58, 0x59, 0x52, 0x53, 0x56, 0x57, 0x5a, 0x5b, // 44 - 55 + 0x60, 0x61, 0x64, 0x65, 0x68, 0x69, 0x62, 0x63 // 56 - 63 + }; + uint8_t idx2_[64] = { + 0x26, 0x27, 0x2a, 0x2b, // 0 - 3 + 0x30, 0x31, 0x34, 0x35, 0x38, 0x39, 0x32, 0x33, 0x36, 0x37, 0x3a, 0x3b, // 4 - 15 + 0x40, 0x41, 0x44, 0x45, 0x48, 0x49, 0x42, 0x43, 0x46, 0x47, 0x4a, 0x4b, // 16 - 27 + 0x50, 0x51, 0x54, 0x55, 0x58, 0x59, 0x52, 0x53, 0x56, 0x57, 0x5a, 0x5b, // 28 - 39 + 0x60, 0x61, 0x64, 0x65, 0x68, 0x69, 0x62, 0x63, 0x66, 0x67, 0x6a, 0x6b, // 40 - 41 + 0x70, 0x71, 0x74, 0x75, 0x78, 0x79, 0x72, 0x73, 0x76, 0x77, 0x7a, 0x7b // 52 - 63 + }; + __m512i idx0 = _mm512_loadu_si512(idx0_); + __m512i idx1 = _mm512_loadu_si512(idx1_); + __m512i idx2 = _mm512_loadu_si512(idx2_); + __m512i llr_i8_0_ = _mm512_permutex2var_epi8(llr_i8_0, idx0, llr_i8_1); + __m512i llr_i8_1_ = _mm512_permutex2var_epi8(llr_i8_1, idx1, llr_i8_2); + __m512i llr_i8_2_ = _mm512_permutex2var_epi8(llr_i8_2, idx2, llr_i8_3); + + // Store results. + _mm512_storeu_si512(reinterpret_cast<__m512i*>(llr + 0), llr_i8_0_); + _mm512_storeu_si512(reinterpret_cast<__m512i*>(llr + 64), llr_i8_1_); + _mm512_storeu_si512(reinterpret_cast<__m512i*>(llr + 128), llr_i8_2_); +} +#endif // HAVE_AVX512 + +#ifdef __AVX2__ static void demod_QAM64_avx2(log_likelihood_ratio* llr, const cf_t* symbol, const float* noise_var) { // Load symbols. @@ -85,7 +194,7 @@ static void demod_QAM64_avx2(log_likelihood_ratio* llr, const cf_t* symbol, cons __m256 noise_0 = _mm256_loadu_ps(noise_var + 0); __m256 noise_1 = _mm256_loadu_ps(noise_var + 8); - // Make noise reciprocal. + // Take the reciprocal of the noise variance. __m256 rcp_noise_0 = mm256::safe_div(_mm256_set1_ps(1), noise_0); __m256 rcp_noise_1 = mm256::safe_div(_mm256_set1_ps(1), noise_1); @@ -183,7 +292,7 @@ static void demod_QAM64_neon(log_likelihood_ratio* llr, const cf_t* symbol, cons float32x4_t noise_0 = vld1q_f32(noise_var); float32x4_t noise_1 = vld1q_f32(noise_var + 4); - // Make noise reciprocal. + // Take the reciprocal of the noise variance. float32x4_t rcp_noise_0 = neon::safe_div(vdupq_n_f32(1.0f), noise_0); float32x4_t rcp_noise_1 = neon::safe_div(vdupq_n_f32(1.0f), noise_1); @@ -291,6 +400,18 @@ void srsran::demodulate_soft_QAM64(span llrs, log_likelihood_ratio* llr_it = llrs.begin(); std::size_t symbol_index = 0; +#ifdef HAVE_AVX512 + // For AVX512, it generates 192 LLRs simultaneously. The input is read in batches of 32 symbols. + for (std::size_t symbol_index_end = (symbols.size() / 32) * 32; symbol_index != symbol_index_end; + symbol_index += 32) { + demod_QAM64_avx512(llr_it, symbols_it, noise_it); + + llr_it += 192; + symbols_it += 32; + noise_it += 32; + } +#endif // HAVE_AVX512 + #ifdef __AVX2__ // For AVX2, it generates 96 LLRs simultaneously. The input is read in batches of 16 symbols. for (std::size_t symbol_index_end = (symbols.size() / 16) * 16; symbol_index != symbol_index_end; diff --git a/lib/phy/upper/channel_modulation/neon_helpers.h b/lib/phy/upper/channel_modulation/neon_helpers.h index e374e02bac..49b0d3ba99 100644 --- a/lib/phy/upper/channel_modulation/neon_helpers.h +++ b/lib/phy/upper/channel_modulation/neon_helpers.h @@ -29,8 +29,8 @@ namespace srsran { namespace neon { -/// \brief Absolute values. -/// \brief Absolute values. +/// \brief Computes absolute values. +/// \brief Computes absolute values. /// \param[in] value Input single-precision NEON register. /// \return A single-precision NEON register with the absolute value. inline float32x4_t abs_f32(float32x4_t value) @@ -55,7 +55,7 @@ inline float32x4_t copysign_f32(float32x4_t value0, float32x4_t value1) /// \brief Clips the values of a single-precision NEON register. /// -/// The values greater than \c range_ceil or lower than \c range_floor are substituted by their corresponding range +/// Values greater than \c range_ceil or lower than \c range_floor are substituted by their corresponding range /// limits. /// /// \param[in] value Input values. @@ -71,7 +71,7 @@ inline float32x4_t clip_f32(float32x4_t value, float32x4_t range_ceil, float32x4 /// \brief Clips the values of a NEON register carrying four signed 32-bit integers. /// -/// The values greater than \c range_ceil or lower than \c range_floor are substituted by their corresponding range +/// Values greater than \c range_ceil or lower than \c range_floor are substituted by their corresponding range /// limits. /// /// \param[in] value Input values. @@ -85,11 +85,11 @@ inline int32x4_t clip_s32(int32x4_t value, int32x4_t range_ceil, int32x4_t range return value; } -/// \brief Ensures that 32-bit integers are bounded, otherwise set them to zero. +/// \brief Ensures that 32-bit integers in a NEON register are bounded, otherwise set them to zero. /// /// \param[in] value Integers to be tested. -/// \param[in] bound_up Upper bound. -/// \param[in] bound_low Lower bound. +/// \param[in] bound_up Upper bounds. +/// \param[in] bound_low Lower bounds. /// \return The input integers with zeros in place of the out-of-bound values. inline int32x4_t check_bounds_s32(int32x4_t value, int32x4_t bound_up, int32x4_t bound_low) { @@ -103,13 +103,15 @@ inline int32x4_t check_bounds_s32(int32x4_t value, int32x4_t bound_up, int32x4_t return value; } -/// \brief Clips and quantizes four single-precision NEON registers, continuous log-likelihood ratio to the discrete -/// representation of type \c log_likelihood_ratio in a single NEON register. +/// \brief Clips and quantizes four single-precision NEON registers. /// -/// \param[in] value_0 Single-precision NEON register with the first four continuous log-likelihood ratio. -/// \param[in] value_1 Single-precision NEON register with the second four continuous log-likelihood ratio. -/// \param[in] value_2 Single-precision NEON register with the third four continuous log-likelihood ratio. -/// \param[in] value_3 Single-precision NEON register with the fourth four continuous log-likelihood ratio. +/// Each NEON register contains four 32-bit single-precission log-likelihood ratios. These are converted into a +/// 8-bit discrete representation of type \c log_likelihood_ratio in a single NEON register. +/// +/// \param[in] value_0 Single-precision NEON register with the first eight log-likelihood ratios. +/// \param[in] value_1 Single-precision NEON register with the second eight log-likelihood ratios. +/// \param[in] value_2 Single-precision NEON register with the third eight log-likelihood ratios. +/// \param[in] value_3 Single-precision NEON register with the fourth eight log-likelihood ratios. /// \param[in] range_limit The input value mapped to \ref log_likelihood_ratio::max(). /// \return A quantized representation of the input values as \c log_likelihood_ratio quantity. /// \note The quantization in the range (-range_limit, range_limit) is [mid-tread @@ -165,7 +167,7 @@ quantize_f32(float32x4_t value_0, float32x4_t value_1, float32x4_t value_2, floa /// \param[in] value Input NEON register. /// \param[in] interval_width Interval width. /// \param[in] nof_intervals Number of intervals. -/// \return A NEON register carrying four signed 32-bit integers with the corresponding interval indexes. +/// \return A NEON register carrying four signed 32-bit integers with the corresponding interval indices. inline uint32x4_t compute_interval_idx(float32x4_t value, float interval_width, int nof_intervals) { // Scale. @@ -184,26 +186,26 @@ inline uint32x4_t compute_interval_idx(float32x4_t value, float interval_width, return vreinterpretq_u32_s32(clip_s32(idx, vdupq_n_s32(nof_intervals - 1), vdupq_n_s32(0))); } -/// \brief Get values from a look-up table. +/// \brief Gets values from a look-up table. /// \param[in] table Look-up table containing eight single-precision values. -/// \param[in] indexes NEON register containing four indexes. -/// \return A single-precision NEON register containing the eight values corresponding to the indexes. -inline float32x4_t look_up_table(const float* table, uint32x4_t indexes) +/// \param[in] indices NEON register containing four indices. +/// \return A single-precision NEON register containing the eight values corresponding to the given indices. +inline float32x4_t look_up_table(const float* table, uint32x4_t indices) { float32x4_t value = vdupq_n_f32(0.0); - value = vsetq_lane_f32(table[vgetq_lane_u32(indexes, 0)], value, 0); - value = vsetq_lane_f32(table[vgetq_lane_u32(indexes, 1)], value, 1); - value = vsetq_lane_f32(table[vgetq_lane_u32(indexes, 2)], value, 2); - value = vsetq_lane_f32(table[vgetq_lane_u32(indexes, 3)], value, 3); + value = vsetq_lane_f32(table[vgetq_lane_u32(indices, 0)], value, 0); + value = vsetq_lane_f32(table[vgetq_lane_u32(indices, 1)], value, 1); + value = vsetq_lane_f32(table[vgetq_lane_u32(indices, 2)], value, 2); + value = vsetq_lane_f32(table[vgetq_lane_u32(indices, 3)], value, 3); return value; } -/// \brief Applies an interval function to a series of values. +/// \brief Applies a piecewise defined function (provided by look-up tables) to a series of values. /// \tparam Table Look-up table type. All tables mut be of the same type. /// \param[in] value Single-precision NEON register with eight input values. /// \param[in] rcp_noise Single-precision NEON register with the reciprocal noise corresponding to the values. /// \param[in] nof_intervals Number of intervals. -/// \param[in] interval_width Interval width to quantify the interval indexes. +/// \param[in] interval_width Interval width to quantify the interval indices. /// \param[in] slopes Table with the slope of each interval. /// \param[in] intercepts Table with the interception points of each interval. /// \return A single-precision NEON register containing the results of the interval function. diff --git a/lib/phy/upper/channel_processors/CMakeLists.txt b/lib/phy/upper/channel_processors/CMakeLists.txt index 6055590755..29ad676451 100644 --- a/lib/phy/upper/channel_processors/CMakeLists.txt +++ b/lib/phy/upper/channel_processors/CMakeLists.txt @@ -87,7 +87,7 @@ target_link_libraries(srsran_channel_processors srsran_ssb_processor srsran_uci_decoder) -install(TARGETS srsran_channel_processors +add_to_exported_libs(srsran_channel_processors srsran_pbch_encoder srsran_pbch_modulator srsran_pdcch_encoder @@ -102,5 +102,4 @@ install(TARGETS srsran_channel_processors srsran_pucch_processor srsran_pusch_processor srsran_ssb_processor - srsran_uci_decoder - EXPORT srsran_export) + srsran_uci_decoder) diff --git a/lib/phy/upper/channel_processors/channel_processor_factories.cpp b/lib/phy/upper/channel_processors/channel_processor_factories.cpp index 439ffa11cb..e085904b31 100644 --- a/lib/phy/upper/channel_processors/channel_processor_factories.cpp +++ b/lib/phy/upper/channel_processors/channel_processor_factories.cpp @@ -30,6 +30,7 @@ #include "pdsch_encoder_hw_impl.h" #include "pdsch_encoder_impl.h" #include "pdsch_modulator_impl.h" +#include "pdsch_processor_asynchronous_pool.h" #include "pdsch_processor_concurrent_impl.h" #include "pdsch_processor_impl.h" #include "pdsch_processor_lite_impl.h" @@ -458,13 +459,12 @@ class pdsch_processor_lite_factory_sw : public pdsch_processor_factory } }; -class pdsch_processor_pool_factory : public pdsch_processor_factory +class pdsch_processor_asynchronous_pool_factory : public pdsch_processor_factory { public: - pdsch_processor_pool_factory(std::shared_ptr factory_, - unsigned max_nof_processors_, - bool blocking_) : - factory(std::move(factory_)), max_nof_processors(max_nof_processors_), blocking(blocking_) + pdsch_processor_asynchronous_pool_factory(std::shared_ptr factory_, + unsigned max_nof_processors_) : + factory(std::move(factory_)), max_nof_processors(max_nof_processors_) { srsran_assert(factory, "Invalid PDSCH processor factory."); srsran_assert(max_nof_processors >= 1, @@ -480,7 +480,7 @@ class pdsch_processor_pool_factory : public pdsch_processor_factory processor = factory->create(); } - return std::make_unique(processors, blocking); + return std::make_unique(processors, false); } std::unique_ptr create(srslog::basic_logger& logger, bool enable_logging_broadcast) override @@ -491,7 +491,7 @@ class pdsch_processor_pool_factory : public pdsch_processor_factory processor = factory->create(logger, enable_logging_broadcast); } - return std::make_unique(processors, blocking); + return std::make_unique(processors, false); } std::unique_ptr create_validator() override { return factory->create_validator(); } @@ -499,7 +499,57 @@ class pdsch_processor_pool_factory : public pdsch_processor_factory private: std::shared_ptr factory; unsigned max_nof_processors; - bool blocking; +}; + +class pdsch_processor_pool_factory : public pdsch_processor_factory +{ +public: + pdsch_processor_pool_factory(std::shared_ptr factory_, unsigned max_nof_processors_) : + factory(std::move(factory_)), max_nof_processors(max_nof_processors_) + { + srsran_assert(factory, "Invalid PDSCH processor factory."); + srsran_assert( + max_nof_processors > 1, "The number of processors (i.e., {}) must be greater than one.", max_nof_processors); + } + + std::unique_ptr create() override + { + // Creates the processors without logging. + if (!processors) { + // Create PDSCH processsor instances. + std::vector> instances(max_nof_processors); + std::generate(instances.begin(), instances.end(), [this]() { return factory->create(); }); + + // Create pool. + processors = std::make_shared(std::move(instances)); + } + + return std::make_unique(processors); + } + + std::unique_ptr create(srslog::basic_logger& logger, bool enable_logging_broadcast) override + { + // Creates the processors with logging. + if (!processors) { + // Create PDSCH processor instances. + std::vector> instances(max_nof_processors); + std::generate(instances.begin(), instances.end(), [this, &logger, &enable_logging_broadcast]() { + return factory->create(logger, enable_logging_broadcast); + }); + + // Create pool. + processors = std::make_shared(std::move(instances)); + } + + return std::make_unique(processors); + } + + std::unique_ptr create_validator() override { return factory->create_validator(); } + +private: + std::shared_ptr factory; + std::shared_ptr processors; + unsigned max_nof_processors; }; class prach_detector_factory_sw : public prach_detector_factory @@ -957,12 +1007,18 @@ srsran::create_pdsch_lite_processor_factory_sw(std::shared_ptr +srsran::create_pdsch_processor_asynchronous_pool(std::shared_ptr pdsch_proc_factory, + unsigned max_nof_processors) +{ + return std::make_shared(std::move(pdsch_proc_factory), max_nof_processors); +} + std::shared_ptr srsran::create_pdsch_processor_pool(std::shared_ptr pdsch_proc_factory, - unsigned max_nof_processors, - bool blocking) + unsigned max_nof_processors) { - return std::make_shared(std::move(pdsch_proc_factory), max_nof_processors, blocking); + return std::make_shared(std::move(pdsch_proc_factory), max_nof_processors); } std::shared_ptr diff --git a/lib/phy/upper/channel_processors/pdsch_processor_asynchronous_pool.h b/lib/phy/upper/channel_processors/pdsch_processor_asynchronous_pool.h new file mode 100644 index 0000000000..3a284560b2 --- /dev/null +++ b/lib/phy/upper/channel_processors/pdsch_processor_asynchronous_pool.h @@ -0,0 +1,143 @@ +/* + * + * 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/concurrent_queue.h" +#include "srsran/phy/upper/channel_processors/channel_processor_formatters.h" +#include "srsran/phy/upper/channel_processors/pdsch_processor.h" +#include "srsran/srslog/logger.h" + +namespace srsran { + +namespace detail { + +/// Free PDSCH processor identifier list type. +using pdsch_processor_free_list = + concurrent_queue; + +/// PDSCH processor wrapper. It appends its identifier into the free list when the processing is finished. +class pdsch_processor_wrapper : private pdsch_processor, private pdsch_processor_notifier +{ +public: + /// Creates a PDSCH processor wrapper from another PDSCH processor. + explicit pdsch_processor_wrapper(unsigned index_, + pdsch_processor_free_list& free_list_, + std::unique_ptr processor_) : + index(index_), free_list(free_list_), notifier(nullptr), processor(std::move(processor_)) + { + srsran_assert(processor, "Invalid PDSCH processor."); + } + + /// Creates a PDSCH processor wrapper from another PDSCH processor wrapper. + pdsch_processor_wrapper(pdsch_processor_wrapper&& other) : + index(other.index), free_list(other.free_list), notifier(other.notifier), processor(std::move(other.processor)) + { + other.notifier = nullptr; + } + + // See pdsch_processor interface for documentation. + void process(resource_grid_mapper& mapper, + pdsch_processor_notifier& notifier_, + static_vector, pdsch_processor::MAX_NOF_TRANSPORT_BLOCKS> data, + const pdsch_processor::pdu_t& pdu) override + { + // Save original notifier. + notifier = ¬ifier_; + + // Process. + processor->process(mapper, *this, data, pdu); + } + +private: + // See pdsch_processor_notifier for documentation. + void on_finish_processing() override + { + srsran_assert(notifier != nullptr, "Invalid notifier."); + + // Notify the completion of the processing. + notifier->on_finish_processing(); + + // Return the PDSCH processor identifier to the free list. + free_list.push_blocking(index); + } + + /// Processor identifier within the pool. + unsigned index; + /// List of free processors. + pdsch_processor_free_list& free_list; + /// Current PDSCH processor notifier. + pdsch_processor_notifier* notifier = nullptr; + /// Wrapped PDSCH processor. + std::unique_ptr processor; +}; +} // namespace detail + +/// \brief Asynchronous PDSCH processor pool. +/// +/// It contains PDSCH processors that are asynchronously executed. The processing of a PDSCH transmission is dropped if +/// there are no free PDSCH processors available. +class pdsch_processor_asynchronous_pool : public pdsch_processor +{ +public: + explicit pdsch_processor_asynchronous_pool(span> processors_, bool blocking_) : + logger(srslog::fetch_basic_logger("PHY")), free_list(processors_.size()), blocking(blocking_) + { + unsigned index = 0; + for (std::unique_ptr& processor : processors_) { + free_list.push_blocking(index); + processors.emplace_back(detail::pdsch_processor_wrapper(index++, free_list, std::move(processor))); + } + } + + void process(resource_grid_mapper& mapper, + pdsch_processor_notifier& notifier, + static_vector, MAX_NOF_TRANSPORT_BLOCKS> data, + const pdu_t& pdu) override + { + // Try to get a worker. + std::optional index; + + do { + index = free_list.try_pop(); + } while (blocking && !index.has_value()); + + // If no worker is available. + if (!index.has_value()) { + logger.warning( + pdu.slot.sfn(), pdu.slot.slot_index(), "Insufficient number of PDSCH processors. Dropping PDSCH {:s}.", pdu); + notifier.on_finish_processing(); + return; + } + + // Process PDSCH. + processors[index.value()].process(mapper, notifier, data, pdu); + } + +private: + srslog::basic_logger& logger; + std::vector processors; + detail::pdsch_processor_free_list free_list; + bool blocking; +}; + +} // namespace srsran diff --git a/lib/phy/upper/channel_processors/pdsch_processor_pool.h b/lib/phy/upper/channel_processors/pdsch_processor_pool.h index 16647ba672..2ea2dcda87 100644 --- a/lib/phy/upper/channel_processors/pdsch_processor_pool.h +++ b/lib/phy/upper/channel_processors/pdsch_processor_pool.h @@ -29,116 +29,36 @@ namespace srsran { -namespace detail { - -/// Free PDSCH processor identifier list type. -using pdsch_processor_free_list = - concurrent_queue; - -/// PDSCH processor wrapper. It appends its identifier into the free list when the processing is finished. -class pdsch_processor_wrapper : private pdsch_processor, private pdsch_processor_notifier -{ -public: - /// Creates a PDSCH processor wrapper from another PDSCH processor. - explicit pdsch_processor_wrapper(unsigned index_, - pdsch_processor_free_list& free_list_, - std::unique_ptr processor_) : - index(index_), free_list(free_list_), notifier(nullptr), processor(std::move(processor_)) - { - srsran_assert(processor, "Invalid PDSCH processor."); - } - - /// Creates a PDSCH processor wrapper from another PDSCH processor wrapper. - pdsch_processor_wrapper(pdsch_processor_wrapper&& other) : - index(other.index), free_list(other.free_list), notifier(other.notifier), processor(std::move(other.processor)) - { - other.notifier = nullptr; - } - - // See pdsch_processor interface for documentation. - void process(resource_grid_mapper& mapper, - pdsch_processor_notifier& notifier_, - static_vector, pdsch_processor::MAX_NOF_TRANSPORT_BLOCKS> data, - const pdsch_processor::pdu_t& pdu) override - { - // Save original notifier. - notifier = ¬ifier_; - - // Process. - processor->process(mapper, *this, data, pdu); - } - -private: - // See pdsch_processor_notifier for documentation. - void on_finish_processing() override - { - srsran_assert(notifier != nullptr, "Invalid notifier."); - - // Notify the completion of the processing. - notifier->on_finish_processing(); - - // Return the PDSCH processor identifier to the free list. - free_list.push_blocking(index); - } - - /// Processor identifier within the pool. - unsigned index; - /// List of free processors. - pdsch_processor_free_list& free_list; - /// Current PDSCH processor notifier. - pdsch_processor_notifier* notifier = nullptr; - /// Wrapped PDSCH processor. - std::unique_ptr processor; -}; -} // namespace detail - -/// \brief PDSCH processor pool -/// -/// It contains PDSCH processors that are asynchronously executed. The processing of a PDSCH transmission is dropped if -/// there are no free PDSCH processors available. +/// \brief PDSCH processor pool. /// +/// It contains a PDSCH processor for each DL thread. The processors are assigned to threads when they are first +/// requested. An assertion is thrown if the number of threads processing PDSCH is larger than the number of PDSCH +/// processors in the pool. class pdsch_processor_pool : public pdsch_processor { public: - explicit pdsch_processor_pool(span> processors_, bool blocking_) : - logger(srslog::fetch_basic_logger("PHY")), free_list(processors_.size()), blocking(blocking_) + /// PDSCH processor pool type. + using pdsch_processor_pool_type = concurrent_thread_local_object_pool; + + /// Creates a PDSCH processor pool from a list of processors. Ownership is transferred to the pool. + explicit pdsch_processor_pool(std::shared_ptr processors_) : + processors(std::move(processors_)) { - unsigned index = 0; - for (std::unique_ptr& processor : processors_) { - free_list.push_blocking(index); - processors.emplace_back(detail::pdsch_processor_wrapper(index++, free_list, std::move(processor))); - } + srsran_assert(processors, "Invalid processor pool."); } + // See interface for documentation. void process(resource_grid_mapper& mapper, pdsch_processor_notifier& notifier, static_vector, MAX_NOF_TRANSPORT_BLOCKS> data, const pdu_t& pdu) override { - // Try to get a worker. - std::optional index; - - do { - index = free_list.try_pop(); - } while (blocking && !index.has_value()); - - // If no worker is available. - if (!index.has_value()) { - logger.warning( - pdu.slot.sfn(), pdu.slot.slot_index(), "Insufficient number of PDSCH processors. Dropping PDSCH {:s}.", pdu); - notifier.on_finish_processing(); - return; - } - - // Process PDSCH. - processors[index.value()].process(mapper, notifier, data, pdu); + pdsch_processor& processor = processors->get(); + return processor.process(mapper, notifier, data, pdu); } private: - srslog::basic_logger& logger; - std::vector processors; - detail::pdsch_processor_free_list free_list; - bool blocking; + std::shared_ptr processors; }; } // namespace srsran diff --git a/lib/phy/upper/equalization/CMakeLists.txt b/lib/phy/upper/equalization/CMakeLists.txt index 8d458db11b..f0f1bf351f 100644 --- a/lib/phy/upper/equalization/CMakeLists.txt +++ b/lib/phy/upper/equalization/CMakeLists.txt @@ -25,5 +25,4 @@ set(SOURCES add_library(srsran_channel_equalizer STATIC ${SOURCES}) target_link_libraries(srsran_channel_equalizer srsvec) -install(TARGETS srsran_channel_equalizer - EXPORT srsran_export) +add_to_exported_libs(srsran_channel_equalizer) diff --git a/lib/phy/upper/sequence_generators/CMakeLists.txt b/lib/phy/upper/sequence_generators/CMakeLists.txt index 592433df00..51ea4171a0 100644 --- a/lib/phy/upper/sequence_generators/CMakeLists.txt +++ b/lib/phy/upper/sequence_generators/CMakeLists.txt @@ -35,5 +35,4 @@ set(SOURCES add_library(srsran_sequence_generators STATIC ${SOURCES}) target_link_libraries(srsran_sequence_generators srsran_support srsvec) -install(TARGETS srsran_sequence_generators - EXPORT srsran_export) \ No newline at end of file +add_to_exported_libs(srsran_sequence_generators) diff --git a/lib/phy/upper/signal_processors/CMakeLists.txt b/lib/phy/upper/signal_processors/CMakeLists.txt index 663a19b29e..7ecf494479 100644 --- a/lib/phy/upper/signal_processors/CMakeLists.txt +++ b/lib/phy/upper/signal_processors/CMakeLists.txt @@ -38,5 +38,4 @@ set(SOURCES add_library(srsran_signal_processors STATIC ${SOURCES}) target_link_libraries(srsran_signal_processors srsvec srsran_phy_support srsran_upper_phy_support srsran_ran srsran_generic_funcs) -install(TARGETS srsran_signal_processors - EXPORT srsran_export) \ No newline at end of file +add_to_exported_libs(srsran_signal_processors) diff --git a/lib/phy/upper/upper_phy_factories.cpp b/lib/phy/upper/upper_phy_factories.cpp index 3d99b89779..89fdd705dd 100644 --- a/lib/phy/upper/upper_phy_factories.cpp +++ b/lib/phy/upper/upper_phy_factories.cpp @@ -760,6 +760,10 @@ srsran::create_downlink_processor_factory_sw(const downlink_processor_factory_sw *pdsch_processor_config.pdsch_codeblock_task_executor, pdsch_processor_config.nof_pdsch_codeblock_threads); report_fatal_error_if_not(pdsch_proc_factory, "Invalid PDSCH processor factory."); + + // Wrap PDSCH processor factory with a PDSCH processor asynchronous pool. + pdsch_proc_factory = create_pdsch_processor_asynchronous_pool(std::move(pdsch_proc_factory), + pdsch_processor_config.max_nof_simultaneous_pdsch); } else if (std::holds_alternative(config.pdsch_processor)) { pdsch_proc_factory = create_pdsch_lite_processor_factory_sw( ldpc_seg_tx_factory, ldpc_enc_factory, ldpc_rm_factory, prg_factory, mod_factory, dmrs_pdsch_proc_factory); @@ -783,19 +787,16 @@ srsran::create_downlink_processor_factory_sw(const downlink_processor_factory_sw // Wrap the downlink processor dependencies with pools to allow concurrent execution. if (config.nof_concurrent_threads > 1) { - // If the PDSCH instance is concurrent, the number of instances is given by the PDSCH processor configuration. - unsigned max_nof_simultaneous_pdsch = config.nof_concurrent_threads; - if (std::holds_alternative(config.pdsch_processor)) { - max_nof_simultaneous_pdsch = - std::get(config.pdsch_processor).max_nof_simultaneous_pdsch; - } - pdcch_proc_factory = create_pdcch_processor_pool_factory(std::move(pdcch_proc_factory), config.nof_concurrent_threads); report_fatal_error_if_not(pdcch_proc_factory, "Invalid PDCCH processor pool factory."); - pdsch_proc_factory = create_pdsch_processor_pool(std::move(pdsch_proc_factory), max_nof_simultaneous_pdsch); - report_fatal_error_if_not(pdcch_proc_factory, "Invalid PDSCH processor pool factory."); + // If the PDSCH instance is concurrent, the operation is asynchronous. In this case, each DL processor has an + // asynchronous pool of processors and no more pools are necessary. + if (!std::holds_alternative(config.pdsch_processor)) { + pdsch_proc_factory = create_pdsch_processor_pool(std::move(pdsch_proc_factory), config.nof_concurrent_threads); + report_fatal_error_if_not(pdcch_proc_factory, "Invalid PDSCH processor pool factory."); + } ssb_proc_factory = create_ssb_processor_pool_factory(std::move(ssb_proc_factory), config.nof_concurrent_threads); report_fatal_error_if_not(pdcch_proc_factory, "Invalid SSB processor pool factory."); diff --git a/lib/radio/CMakeLists.txt b/lib/radio/CMakeLists.txt index fa8a213e35..6ab4d52c84 100644 --- a/lib/radio/CMakeLists.txt +++ b/lib/radio/CMakeLists.txt @@ -40,4 +40,4 @@ endif (ZEROMQ_FOUND AND ENABLE_ZEROMQ) add_definitions(${SRSGNB_RADIO_DEFINITIONS}) target_link_libraries(srsran_radio ${SRSGNB_RADIO_LIBRARIES} srslog dl) -install(TARGETS srsran_radio EXPORT srsran_export) \ No newline at end of file +add_to_exported_libs(srsran_radio) diff --git a/lib/radio/uhd/CMakeLists.txt b/lib/radio/uhd/CMakeLists.txt index deab4e307e..549b6276eb 100644 --- a/lib/radio/uhd/CMakeLists.txt +++ b/lib/radio/uhd/CMakeLists.txt @@ -28,4 +28,4 @@ add_library(srsran_radio_uhd STATIC ${SOURCES_UHD}) target_link_libraries(srsran_radio_uhd ${UHD_LIBRARIES} fmt srslog) -install(TARGETS srsran_radio_uhd EXPORT srsran_export) \ No newline at end of file +add_to_exported_libs(srsran_radio_uhd) diff --git a/lib/radio/uhd/radio_config_uhd_validator.cpp b/lib/radio/uhd/radio_config_uhd_validator.cpp index 20a50d7f9f..ad3c89a250 100644 --- a/lib/radio/uhd/radio_config_uhd_validator.cpp +++ b/lib/radio/uhd/radio_config_uhd_validator.cpp @@ -147,29 +147,6 @@ static bool validate_otw_format(radio_configuration::over_the_wire_format otw_fo return true; } -static bool validate_log_level(const std::string& log_level) -{ - // Converts to a logger level. - srslog::basic_levels level = srslog::str_to_basic_level(log_level); - - // Convert the logger level back to a string. - std::string actual_log_level = srslog::basic_level_to_string(level); - - // Check if the strings are equal without considering the case. - bool are_equal = std::equal( - log_level.begin(), log_level.end(), actual_log_level.begin(), actual_log_level.end(), [](char a, char b) { - return std::tolower(a) == std::tolower(b); - }); - - // The log level is not valid if the strings are different. - if (!are_equal) { - fmt::print("Log level {} does not correspond to an actual logger level.\n", log_level); - return false; - } - - return true; -} - bool radio_config_uhd_config_validator::is_configuration_valid(const radio_configuration::radio& config) const { if (!validate_clock_sources(config.clock)) { @@ -220,10 +197,6 @@ bool radio_config_uhd_config_validator::is_configuration_valid(const radio_confi return false; } - if (!validate_log_level(config.log_level)) { - return false; - } - if (config.power_ramping_us < 0) { fmt::print("Power ramping time, i.e., {:.1f} us, must be positive or zero.\n", config.power_ramping_us); return false; diff --git a/lib/radio/uhd/radio_uhd_impl.cpp b/lib/radio/uhd/radio_uhd_impl.cpp index 0c45d31852..7e05cd3816 100644 --- a/lib/radio/uhd/radio_uhd_impl.cpp +++ b/lib/radio/uhd/radio_uhd_impl.cpp @@ -219,27 +219,20 @@ radio_session_uhd_impl::radio_session_uhd_impl(const radio_configuration::radio& // Set the logging level. #ifdef UHD_LOG_INFO - uhd::log::severity_level severity_level = uhd::log::severity_level::info; - if (!radio_config.log_level.empty()) { - std::string log_level = radio_config.log_level; - - for (auto& e : log_level) { - e = std::toupper(e); - } - - if (log_level == "WARNING") { - severity_level = uhd::log::severity_level::warning; - } else if (log_level == "INFO") { - severity_level = uhd::log::severity_level::info; - } else if (log_level == "DEBUG") { - severity_level = uhd::log::severity_level::debug; - } else if (log_level == "TRACE") { - severity_level = uhd::log::severity_level::trace; - } else { - severity_level = uhd::log::severity_level::error; - } + switch (radio_config.log_level) { + case srslog::basic_levels::warning: + uhd::log::set_console_level(uhd::log::severity_level::warning); + break; + case srslog::basic_levels::debug: + uhd::log::set_console_level(uhd::log::severity_level::debug); + break; + case srslog::basic_levels::error: + uhd::log::set_console_level(uhd::log::severity_level::error); + break; + default: + uhd::log::set_console_level(uhd::log::severity_level::info); + break; } - uhd::log::set_console_level(severity_level); #endif unsigned total_rx_channel_count = 0; diff --git a/lib/radio/zmq/CMakeLists.txt b/lib/radio/zmq/CMakeLists.txt index 94f1593a04..095fb1cdb0 100644 --- a/lib/radio/zmq/CMakeLists.txt +++ b/lib/radio/zmq/CMakeLists.txt @@ -30,4 +30,4 @@ add_library(srsran_radio_zmq STATIC ${SOURCES_ZMQ}) target_link_libraries(srsran_radio_zmq ${ZEROMQ_LIBRARIES} fmt srslog) -install(TARGETS srsran_radio_zmq EXPORT srsran_export) \ No newline at end of file +add_to_exported_libs(srsran_radio_zmq) diff --git a/lib/radio/zmq/radio_config_zmq_validator.cpp b/lib/radio/zmq/radio_config_zmq_validator.cpp index edc174514b..63fee0263e 100644 --- a/lib/radio/zmq/radio_config_zmq_validator.cpp +++ b/lib/radio/zmq/radio_config_zmq_validator.cpp @@ -135,29 +135,6 @@ static bool validate_otw_format(radio_configuration::over_the_wire_format otw_fo return true; } -static bool validate_log_level(const std::string& log_level) -{ - // Converts to a logger level. - srslog::basic_levels level = srslog::str_to_basic_level(log_level); - - // Convert the logger level back to a string. - std::string actual_log_level = srslog::basic_level_to_string(level); - - // Check if the strings are equal without considering the case. - bool are_equal = std::equal( - log_level.begin(), log_level.end(), actual_log_level.begin(), actual_log_level.end(), [](char a, char b) { - return std::tolower(a) == std::tolower(b); - }); - - // The log level is not valid if the strings are different. - if (!are_equal) { - fmt::print("Log level {} does not correspond to an actual logger level.\n", log_level); - return false; - } - - return true; -} - bool radio_config_zmq_config_validator::is_configuration_valid(const radio_configuration::radio& config) const { if (!validate_clock_sources(config.clock)) { @@ -194,10 +171,6 @@ bool radio_config_zmq_config_validator::is_configuration_valid(const radio_confi return false; } - if (!validate_log_level(config.log_level)) { - return false; - } - if (config.tx_mode != radio_configuration::transmission_mode::continuous) { fmt::print("Discontinuous transmission modes are not supported by the ZMQ radio.\n"); return false; diff --git a/lib/radio/zmq/radio_session_zmq_impl.cpp b/lib/radio/zmq/radio_session_zmq_impl.cpp index ca2e500207..3ac0ba35f0 100644 --- a/lib/radio/zmq/radio_session_zmq_impl.cpp +++ b/lib/radio/zmq/radio_session_zmq_impl.cpp @@ -48,7 +48,7 @@ radio_session_zmq_impl::radio_session_zmq_impl(const radio_configuration::radio& bool allow_log_level_debug = (config.args.find("verbose") != std::string::npos); // ZMQ logging in debug is extremely verbose. The following lines avoid debug level unless set to paranoid. - srslog::basic_levels log_level = srslog::str_to_basic_level(config.log_level); + srslog::basic_levels log_level = config.log_level; if (!allow_log_level_debug && (log_level >= srslog::basic_levels::debug)) { log_level = srslog::basic_levels::info; } diff --git a/lib/ran/CMakeLists.txt b/lib/ran/CMakeLists.txt index b47da4078a..4d9bfde484 100644 --- a/lib/ran/CMakeLists.txt +++ b/lib/ran/CMakeLists.txt @@ -62,5 +62,4 @@ add_library(srsran_ran tdd_ul_dl_config.cpp) target_link_libraries(srsran_ran srslog) -install(TARGETS srsran_ran - EXPORT srsran_export) +add_to_exported_libs(srsran_ran) diff --git a/lib/ran/band_helper.cpp b/lib/ran/band_helper.cpp index d948493184..4fb0e40fde 100644 --- a/lib/ran/band_helper.cpp +++ b/lib/ran/band_helper.cpp @@ -421,7 +421,7 @@ static error_type validate_band_n28(uint32_t arfcn, bs_channel_band { const nr_band_raster band_raster = fetch_band_raster(nr_band::n28, {}); if (band_raster.band == srsran::nr_band::invalid) { - return error_type{fmt::format("Band n28 channel raster not found")}; + return make_unexpected(fmt::format("Band n28 channel raster not found")); } // Try first if the ARFCN matches any value of the interval for 100kHz channel raster. @@ -436,10 +436,11 @@ static error_type validate_band_n28(uint32_t arfcn, bs_channel_band return error_type{}; } - return {fmt::format("DL ARFCN must be within the interval [{},{}], in steps of {}, for the chosen band", - band_raster.dl_nref_first, - band_raster.dl_nref_last, - band_raster.dl_nref_step)}; + return make_unexpected( + fmt::format("DL ARFCN must be within the interval [{},{}], in steps of {}, for the chosen band", + band_raster.dl_nref_first, + band_raster.dl_nref_last, + band_raster.dl_nref_step)); } // Validates band n46, whose valid ARFCN values depend on the channel BW, as per Table 5.4.2.3-1, TS 38.104, @@ -473,7 +474,7 @@ static error_type validate_band_n46(uint32_t arfcn, bs_channel_band const nr_band_raster band_raster = fetch_band_raster(nr_band::n46, {}); if (band_raster.band == srsran::nr_band::invalid or arfcn < band_raster.dl_nref_first or arfcn > band_raster.dl_nref_last) { - return error_type{fmt::format("Band n46 channel raster not found")}; + return make_unexpected(fmt::format("Band n46 channel raster not found")); } auto dl_arfcn_exist = [](span band_list, unsigned dl_arfcn) { @@ -483,37 +484,31 @@ static error_type validate_band_n46(uint32_t arfcn, bs_channel_band const char* error_msg = {"Only a restricted set of DL-ARFCN values are allowed in band n46"}; switch (bw) { case bs_channel_bandwidth_fr1::MHz10: { - return dl_arfcn_exist(span(n46_b_10_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(n46_b_10_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz20: { - return dl_arfcn_exist(span(n46_b_20_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(n46_b_20_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz40: { - return dl_arfcn_exist(span(n46_b_40_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(n46_b_40_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz60: { - return dl_arfcn_exist(span(n46_b_60_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(n46_b_60_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz80: { - return dl_arfcn_exist(span(n46_b_80_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(n46_b_80_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz100: { - return dl_arfcn_exist(span(n46_b_100_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(n46_b_100_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } default: - return error_type{fmt::format("DL-ARFCN not valid for band n46.")}; + return make_unexpected(fmt::format("DL-ARFCN not valid for band n46.")); } } @@ -559,7 +554,7 @@ static error_type validate_band_n96(uint32_t arfcn, bs_channel_band const nr_band_raster band_raster = fetch_band_raster(nr_band::n96, {}); if (band_raster.band == srsran::nr_band::invalid or arfcn < band_raster.dl_nref_first or arfcn > band_raster.dl_nref_last) { - return error_type{fmt::format("Band n96 channel raster not found")}; + return make_unexpected(fmt::format("Band n96 channel raster not found")); } auto dl_arfcn_exist = [](span band_list, unsigned dl_arfcn) { @@ -569,32 +564,27 @@ static error_type validate_band_n96(uint32_t arfcn, bs_channel_band const char* error_msg = {"Only a restricted set of DL-ARFCN values are allowed in band n96"}; switch (bw) { case bs_channel_bandwidth_fr1::MHz20: { - return dl_arfcn_exist(span(b_20_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(b_20_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz40: { - return dl_arfcn_exist(span(b_40_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(b_40_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz60: { - return dl_arfcn_exist(span(b_60_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(b_60_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz80: { - return dl_arfcn_exist(span(b_80_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(b_80_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz100: { - return dl_arfcn_exist(span(b_100_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(b_100_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } default: - return error_type{fmt::format("DL-ARFCN not valid for band n96.")}; + return make_unexpected(fmt::format("DL-ARFCN not valid for band n96.")); } } @@ -624,7 +614,7 @@ static error_type validate_band_n102(uint32_t arfcn, bs_channel_ban const nr_band_raster band_raster = fetch_band_raster(nr_band::n102, {}); if (band_raster.band == srsran::nr_band::invalid or arfcn < band_raster.dl_nref_first or arfcn > band_raster.dl_nref_last) { - return error_type{fmt::format("Band n102 channel raster not found")}; + return make_unexpected(fmt::format("Band n102 channel raster not found")); } auto dl_arfcn_exist = [](span band_list, unsigned dl_arfcn) { @@ -634,32 +624,27 @@ static error_type validate_band_n102(uint32_t arfcn, bs_channel_ban const char* error_msg = {"Only a restricted set of DL-ARFCN values are allowed in band n102"}; switch (bw) { case bs_channel_bandwidth_fr1::MHz20: { - return dl_arfcn_exist(span(b_20_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(b_20_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz40: { - return dl_arfcn_exist(span(b_40_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(b_40_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz60: { - return dl_arfcn_exist(span(b_60_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(b_60_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz80: { - return dl_arfcn_exist(span(b_80_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(b_80_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } case bs_channel_bandwidth_fr1::MHz100: { - return dl_arfcn_exist(span(b_100_dlarfnc), arfcn) - ? error_type{} - : error_type{fmt::format(error_msg)}; + return dl_arfcn_exist(span(b_100_dlarfnc), arfcn) ? error_type{} + : make_unexpected(fmt::format(error_msg)); } default: - return error_type{fmt::format("DL-ARFCN not valid for band n102.")}; + return make_unexpected(fmt::format("DL-ARFCN not valid for band n102.")); } } @@ -671,7 +656,7 @@ static error_type validate_band_n90(uint32_t arfcn, subcarrier_spac // Try first 100kHz channel raster. nr_band_raster band_raster = fetch_band_raster(nr_band::n90, delta_freq_raster::kHz100); if (band_raster.band == srsran::nr_band::invalid) { - return error_type{fmt::format("Band n90 channel raster for SCS {} not found", to_string(scs))}; + return make_unexpected(fmt::format("Band n90 channel raster for SCS {} not found", to_string(scs))); } if (arfcn >= band_raster.dl_nref_first and arfcn <= band_raster.dl_nref_last and @@ -688,8 +673,8 @@ static error_type validate_band_n90(uint32_t arfcn, subcarrier_spac return error_type{}; } } - return { - fmt::format("DL ARFCN for band n90 is either outside the allowed interval or not compatible with the step size")}; + return make_unexpected( + fmt::format("DL ARFCN for band n90 is either outside the allowed interval or not compatible with the step size")); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -757,13 +742,14 @@ error_type srsran::band_helper::is_dl_arfcn_valid_given_band(nr_ban ((arfcn - raster_band.dl_nref_first) % raster_band.dl_nref_step) == 0) { return {}; } - return {fmt::format("DL ARFCN must be within the interval [{},{}], in steps of {}, for the chosen band", - raster_band.dl_nref_first, - raster_band.dl_nref_last, - raster_band.dl_nref_step)}; + return make_unexpected( + fmt::format("DL ARFCN must be within the interval [{},{}], in steps of {}, for the chosen band", + raster_band.dl_nref_first, + raster_band.dl_nref_last, + raster_band.dl_nref_step)); } } - return {fmt::format("Band is not valid")}; + return make_unexpected(fmt::format("Band is not valid")); } uint32_t srsran::band_helper::get_ul_arfcn_from_dl_arfcn(uint32_t dl_arfcn, std::optional band) @@ -1445,8 +1431,7 @@ error_type srsran::band_helper::is_ssb_arfcn_valid_given_band(uint3 // Convert the ARFCN to GSCN. std::optional gscn = band_helper::get_gscn_from_ss_ref(nr_arfcn_to_freq(ssb_arfcn)); if (not gscn.has_value()) { - return error_type{ - fmt::format("GSCN {} is not valid for band {} with SSB SCS {}", gscn, band, ssb_scs)}; + return make_unexpected(fmt::format("GSCN {} is not valid for band {} with SSB SCS {}", gscn, band, ssb_scs)); } // If the GCSN exists, check if it is a valid one. return band_helper::is_gscn_valid_given_band(gscn.value(), band, ssb_scs, bw); diff --git a/lib/ran/prach/prach_helper.cpp b/lib/ran/prach/prach_helper.cpp index e03ac433b4..9ec3998ac2 100644 --- a/lib/ran/prach/prach_helper.cpp +++ b/lib/ran/prach/prach_helper.cpp @@ -34,11 +34,11 @@ error_type srsran::prach_helper::prach_config_index_is_valid(uint8_ ? prach_cfg_idx <= 107U or (prach_cfg_idx > 197U and prach_cfg_idx < 219U) : prach_cfg_idx <= 86U or (prach_cfg_idx > 144U and prach_cfg_idx < 169U); if (not is_prach_cfg_idx_supported) { - return fmt::format( + return make_unexpected(fmt::format( "PRACH configuration index {} not supported. For {}, the supported PRACH configuration indices are {}\n", prach_cfg_idx, is_paired_spectrum ? "FDD" : "TDD", - is_paired_spectrum ? "[0, 107] and [198, 218]" : "[0, 86] and [145, 168]"); + is_paired_spectrum ? "[0, 107] and [198, 218]" : "[0, 86] and [145, 168]")); } return {}; @@ -50,24 +50,24 @@ error_type srsran::prach_helper::zero_correlation_zone_is_valid(uin { prach_configuration prach_config = prach_configuration_get(frequency_range::FR1, dplx_mode, prach_cfg_idx); if (prach_config.format == prach_format_type::invalid) { - return fmt::format("Invalid PRACH configuration index: {}\n", prach_cfg_idx); + return make_unexpected(fmt::format("Invalid PRACH configuration index: {}\n", prach_cfg_idx)); } if (dplx_mode == duplex_mode::FDD) { // Paired spectrum case. if ((prach_config.format == prach_format_type::B4) && (zero_correlation_zone != 0) && (zero_correlation_zone != 11)) { - return fmt::format( + return make_unexpected(fmt::format( "PRACH Zero Correlation Zone index (i.e., {}) with Format B4 is not supported for FDD. Use 0 or 11.\n", - zero_correlation_zone); + zero_correlation_zone)); } } else { // Unpaired spectrum case. if ((prach_config.format == prach_format_type::B4) && (zero_correlation_zone != 0) && (zero_correlation_zone != 14)) { - return fmt::format( + return make_unexpected(fmt::format( "PRACH Zero Correlation Zone index (i.e., {}) with Format B4 is not supported for TDD. Use 0 or 14.\n", - zero_correlation_zone); + zero_correlation_zone)); } } @@ -82,7 +82,7 @@ error_type> srsran::prach_helper::prach_fits_in_tdd_pattern(su const prach_configuration prach_cfg = prach_configuration_get(frequency_range::FR1, duplex_mode::TDD, prach_cfg_idx); if (prach_cfg.format == prach_format_type::invalid) { - return interval{}; + return make_unexpected(interval{}); } const prach_symbols_slots_duration dur = get_prach_duration_info(prach_cfg, pusch_scs); @@ -97,7 +97,7 @@ error_type> srsran::prach_helper::prach_fits_in_tdd_pattern(su // Note: For now, PRACH in special slots is not supported. if (not is_tdd_full_ul_slot(tdd_cfg, slot_index)) { // No UL symbols exist in this slot. - return interval{start_slot_index, start_slot_index + dur.prach_length_slots}; + return make_unexpected(interval{start_slot_index, start_slot_index + dur.prach_length_slots}); } } } @@ -155,9 +155,10 @@ srsran::prach_helper::nof_ssb_per_ro_and_nof_cb_preambles_per_ssb_is_valid(float } if (not is_valid) { - return fmt::format("Invalid nof. contention based preambles per SSB ({}) for nof. SSB per RACH occasion ({}).\n", - nof_cb_preambles_per_ssb, - nof_ssb_per_ro); + return make_unexpected( + fmt::format("Invalid nof. contention based preambles per SSB ({}) for nof. SSB per RACH occasion ({}).\n", + nof_cb_preambles_per_ssb, + nof_ssb_per_ro)); } return {}; diff --git a/lib/ran/ssb_gscn.cpp b/lib/ran/ssb_gscn.cpp index f5095372ed..ae9627b8cf 100644 --- a/lib/ran/ssb_gscn.cpp +++ b/lib/ran/ssb_gscn.cpp @@ -178,9 +178,9 @@ validate_irregular_gscn_rasters(unsigned gscn, nr_band band, subcarrier_spacing is_gscn_valid = std::find(gscn_band_n102.begin(), gscn_band_n102.end(), gscn) != gscn_band_n102.end(); } - return is_gscn_valid ? error_type{} - : error_type{ - fmt::format("GSCN {} is not valid for band {} with SSB SCS {}", gscn, band, ssb_scs)}; + return is_gscn_valid + ? error_type{} + : make_unexpected(fmt::format("GSCN {} is not valid for band {} with SSB SCS {}", gscn, band, ssb_scs)); } error_type srsran::band_helper::is_gscn_valid_given_band(unsigned gscn, @@ -195,10 +195,11 @@ error_type srsran::band_helper::is_gscn_valid_given_band(unsigned ((gscn - raster.gscn_first) % raster.gscn_step) == 0) { return {}; } - return {fmt::format("GSCN must be within the interval [{},{}], in steps of {}, for the chosen band", - raster.gscn_first, - raster.gscn_last, - raster.gscn_step)}; + return make_unexpected( + fmt::format("GSCN must be within the interval [{},{}], in steps of {}, for the chosen band", + raster.gscn_first, + raster.gscn_last, + raster.gscn_step)); } } diff --git a/lib/rlc/rlc_rx_am_entity.cpp b/lib/rlc/rlc_rx_am_entity.cpp index 4ba4bb96d6..afcef5a59e 100644 --- a/lib/rlc/rlc_rx_am_entity.cpp +++ b/lib/rlc/rlc_rx_am_entity.cpp @@ -496,13 +496,13 @@ expected rlc_rx_am_entity::reassemble_sdu(rlc_rx_am_sdu_info& // Sanity check if (!sdu_info.fully_received) { logger.log_error("Cannot reassemble SDU not marked as fully_received. sn={} {}", sn, sdu_info); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } expected sdu = byte_buffer_chain::create(); if (!sdu) { logger.log_error("Failed to create SDU buffer. sn={} {}", sn, sdu_info); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } if (std::holds_alternative(sdu_info.sdu_data)) { @@ -510,7 +510,7 @@ expected rlc_rx_am_entity::reassemble_sdu(rlc_rx_am_sdu_info& byte_buffer_slice& payload = std::get(sdu_info.sdu_data); if (!sdu.value().append(std::move(payload))) { logger.log_error("Failed to append segment in SDU buffer. sn={} {}", sn, sdu_info); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } } else if (std::holds_alternative(sdu_info.sdu_data)) { rlc_rx_am_sdu_info::segment_set_t& segments = std::get(sdu_info.sdu_data); @@ -522,7 +522,7 @@ expected rlc_rx_am_entity::reassemble_sdu(rlc_rx_am_sdu_info& segm.so, segm.payload.length(), sdu_info); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } } logger.log_debug("Assembled SDU from segments. sn={} sdu_len={}", sn, sdu.value().length()); diff --git a/lib/rlc/rlc_rx_um_entity.cpp b/lib/rlc/rlc_rx_um_entity.cpp index f3f0bd8d10..68aa2c18d7 100644 --- a/lib/rlc/rlc_rx_um_entity.cpp +++ b/lib/rlc/rlc_rx_um_entity.cpp @@ -415,13 +415,13 @@ expected rlc_rx_um_entity::reassemble_sdu(rlc_rx_um_sdu_info& // Sanity check if (!sdu_info.fully_received) { logger.log_error("Cannot reassemble SDU not marked as fully_received. sn={} {}", sn, sdu_info); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } expected sdu = byte_buffer_chain::create(); if (!sdu) { logger.log_error("Failed to create SDU buffer. sn={} {}", sn, sdu_info); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } for (const rlc_rx_um_sdu_segment& segm : sdu_info.segments) { @@ -432,7 +432,7 @@ expected rlc_rx_um_entity::reassemble_sdu(rlc_rx_um_sdu_info& segm.so, segm.payload.length(), sdu_info); - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } } logger.log_debug("Assembled SDU from segments. sn={} sdu_len={}", sn, sdu.value().length()); diff --git a/lib/rlc/rlc_tx_am_entity.cpp b/lib/rlc/rlc_tx_am_entity.cpp index 9337ef3e9d..7746f75e2e 100644 --- a/lib/rlc/rlc_tx_am_entity.cpp +++ b/lib/rlc/rlc_tx_am_entity.cpp @@ -33,7 +33,7 @@ using namespace srsran; rlc_tx_am_entity::rlc_tx_am_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, - rb_id_t rb_id, + rb_id_t rb_id_, const rlc_tx_am_config& config, rlc_tx_upper_layer_data_notifier& upper_dn_, rlc_tx_upper_layer_control_notifier& upper_cn_, @@ -43,7 +43,7 @@ rlc_tx_am_entity::rlc_tx_am_entity(gnb_du_id_t gnb_du_i task_executor& ue_executor_, bool metrics_enabled_, rlc_pcap& pcap_) : - rlc_tx_entity(gnb_du_id, ue_index, rb_id, upper_dn_, upper_cn_, lower_dn_, metrics_enabled_, pcap_), + rlc_tx_entity(gnb_du_id, ue_index, rb_id_, upper_dn_, upper_cn_, lower_dn_, metrics_enabled_, pcap_), cfg(config), sdu_queue(cfg.queue_size, logger), retx_queue(window_size(to_number(cfg.sn_field_length))), @@ -57,7 +57,7 @@ rlc_tx_am_entity::rlc_tx_am_entity(gnb_du_id_t gnb_du_i is_poll_retransmit_timer_expired(false), pcell_executor(pcell_executor_), ue_executor(ue_executor_), - pcap_context(ue_index, rb_id, config) + pcap_context(ue_index, rb_id_, config) { metrics.metrics_set_mode(rlc_mode::am); @@ -67,7 +67,7 @@ rlc_tx_am_entity::rlc_tx_am_entity(gnb_du_id_t gnb_du_i config.pdcp_sn_len, gnb_du_id, ue_index, - rb_id); + rb_id_); // check timer t_poll_retransmission timer srsran_assert(poll_retransmit_timer.is_valid(), "Cannot create RLC TX AM, timers not configured."); @@ -82,26 +82,38 @@ rlc_tx_am_entity::rlc_tx_am_entity(gnb_du_id_t gnb_du_i } // TS 38.322 v16.2.0 Sec. 5.2.3.1 -void rlc_tx_am_entity::handle_sdu(byte_buffer sdu_buf) +void rlc_tx_am_entity::handle_sdu(byte_buffer sdu_buf, bool is_retx) { rlc_sdu sdu; sdu.time_of_arrival = std::chrono::high_resolution_clock::now(); sdu.buf = std::move(sdu_buf); + sdu.is_retx = is_retx; sdu.pdcp_sn = get_pdcp_sn(sdu.buf, cfg.pdcp_sn_len, logger.get_basic_logger()); + // Sanity check for PDCP ReTx in SRBs + if (SRSRAN_UNLIKELY(rb_id.is_srb() && sdu.is_retx)) { + logger.log_error("Ignored unexpected PDCP retransmission flag in SRB RLC AM SDU"); + sdu.is_retx = false; + } + size_t sdu_length = sdu.buf.length(); if (sdu_queue.write(sdu)) { logger.log_info(sdu.buf.begin(), sdu.buf.end(), - "TX SDU. sdu_len={} pdcp_sn={} {}", + "TX SDU. sdu_len={} pdcp_sn={} is_retx={} {}", sdu.buf.length(), sdu.pdcp_sn, + sdu.is_retx, sdu_queue.get_state()); metrics.metrics_add_sdus(1, sdu_length); handle_changed_buffer_state(); } else { - logger.log_warning("Dropped SDU. sdu_len={} pdcp_sn={} {}", sdu_length, sdu.pdcp_sn, sdu_queue.get_state()); + logger.log_warning("Dropped SDU. sdu_len={} pdcp_sn={} is_retx={} {}", + sdu_length, + sdu.pdcp_sn, + sdu.is_retx, + sdu_queue.get_state()); metrics.metrics_add_lost_sdus(1); } } @@ -219,13 +231,18 @@ 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); - sdu_info.pdcp_sn = sdu.pdcp_sn; 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; sdu_info.time_of_arrival = sdu.time_of_arrival; // Notify the upper layer about the beginning of the transfer of the current SDU if (sdu.pdcp_sn.has_value()) { - upper_dn.on_transmitted_sdu(sdu.pdcp_sn.value()); + if (sdu.is_retx) { + upper_dn.on_retransmitted_sdu(sdu.pdcp_sn.value()); + } else { + upper_dn.on_transmitted_sdu(sdu.pdcp_sn.value()); + } } // Segment new SDU if necessary @@ -659,13 +676,18 @@ void rlc_tx_am_entity::handle_status_pdu(rlc_am_status_pdu status) ? status.ack_sn : status.get_nacks()[0].nack_sn; // Stop processing ACKs at the first NACK, if it exists. - std::optional max_deliv_pdcp_sn = {}; // initialize with not value set - bool recycle_bin_full = false; + std::optional max_deliv_pdcp_sn = {}; // initialize with not value set + 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 (sdu_info.pdcp_sn.has_value()) { - max_deliv_pdcp_sn = (*tx_window)[sn].pdcp_sn; + if (sdu_info.is_retx) { + max_deliv_retx_pdcp_sn = (*tx_window)[sn].pdcp_sn; + } else { + 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. if (!pdu_recycler.add_discarded_pdu(std::move(sdu_info.sdu))) { @@ -682,6 +704,9 @@ void rlc_tx_am_entity::handle_status_pdu(rlc_am_status_pdu status) if (max_deliv_pdcp_sn.has_value()) { upper_dn.on_delivered_sdu(max_deliv_pdcp_sn.value()); } + if (max_deliv_retx_pdcp_sn.has_value()) { + upper_dn.on_delivered_retransmitted_sdu(max_deliv_retx_pdcp_sn.value()); + } logger.log_debug("Processed status report ACKs. ack_sn={} tx_next_ack={}", status.ack_sn, st.tx_next_ack); if (recycle_bin_full) { logger.log_warning("Could not postpone recycling of PDU byte_buffers. Performance can be impaired."); diff --git a/lib/rlc/rlc_tx_am_entity.h b/lib/rlc/rlc_tx_am_entity.h index e129529cd2..f0104fc722 100644 --- a/lib/rlc/rlc_tx_am_entity.h +++ b/lib/rlc/rlc_tx_am_entity.h @@ -37,11 +37,12 @@ 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 = {}; + 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; - std::optional pdcp_sn; - uint32_t next_so = 0; - uint32_t retx_count = RETX_COUNT_NOT_STARTED; + uint32_t next_so = 0; ///< Segmentation progress + uint32_t retx_count = RETX_COUNT_NOT_STARTED; ///< Retransmission counter }; /// \brief TX state variables @@ -139,7 +140,7 @@ class rlc_tx_am_entity : public rlc_tx_entity, public rlc_tx_am_status_handler, public: rlc_tx_am_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, - rb_id_t rb_id, + rb_id_t rb_id_, const rlc_tx_am_config& config, rlc_tx_upper_layer_data_notifier& upper_dn_, rlc_tx_upper_layer_control_notifier& upper_cn_, @@ -160,7 +161,7 @@ class rlc_tx_am_entity : public rlc_tx_entity, public rlc_tx_am_status_handler, void set_status_provider(rlc_rx_am_status_provider* status_provider_) { status_provider = status_provider_; } // Interfaces for higher layers - void handle_sdu(byte_buffer sdu_buf) override; + void handle_sdu(byte_buffer sdu_buf, bool is_retx) override; void discard_sdu(uint32_t pdcp_sn) override; // Interfaces for lower layers diff --git a/lib/rlc/rlc_tx_entity.h b/lib/rlc/rlc_tx_entity.h index 763714e286..de5a06d930 100644 --- a/lib/rlc/rlc_tx_entity.h +++ b/lib/rlc/rlc_tx_entity.h @@ -40,14 +40,15 @@ class rlc_tx_entity : public rlc_tx_upper_layer_data_interface, protected: rlc_tx_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, - rb_id_t rb_id, + rb_id_t rb_id_, rlc_tx_upper_layer_data_notifier& upper_dn_, rlc_tx_upper_layer_control_notifier& upper_cn_, rlc_tx_lower_layer_notifier& lower_dn_, bool metrics_enabled, rlc_pcap& pcap_) : - logger("RLC", {gnb_du_id, ue_index, rb_id, "DL"}), + logger("RLC", {gnb_du_id, ue_index, rb_id_, "DL"}), metrics(metrics_enabled), + rb_id(rb_id_), upper_dn(upper_dn_), upper_cn(upper_cn_), lower_dn(lower_dn_), @@ -57,6 +58,7 @@ class rlc_tx_entity : public rlc_tx_upper_layer_data_interface, rlc_bearer_logger logger; rlc_tx_metrics_container metrics; + rb_id_t rb_id; rlc_tx_upper_layer_data_notifier& upper_dn; rlc_tx_upper_layer_control_notifier& upper_cn; rlc_tx_lower_layer_notifier& lower_dn; diff --git a/lib/rlc/rlc_tx_tm_entity.cpp b/lib/rlc/rlc_tx_tm_entity.cpp index 860a5cd359..ccd83e4e60 100644 --- a/lib/rlc/rlc_tx_tm_entity.cpp +++ b/lib/rlc/rlc_tx_tm_entity.cpp @@ -26,7 +26,7 @@ using namespace srsran; rlc_tx_tm_entity::rlc_tx_tm_entity(gnb_du_id_t du_id, du_ue_index_t ue_index, - rb_id_t rb_id, + rb_id_t rb_id_, const rlc_tx_tm_config& config, rlc_tx_upper_layer_data_notifier& upper_dn_, rlc_tx_upper_layer_control_notifier& upper_cn_, @@ -34,23 +34,28 @@ rlc_tx_tm_entity::rlc_tx_tm_entity(gnb_du_id_t du_id, task_executor& pcell_executor_, bool metrics_enabled_, rlc_pcap& pcap_) : - rlc_tx_entity(du_id, ue_index, rb_id, upper_dn_, upper_cn_, lower_dn_, metrics_enabled_, pcap_), + rlc_tx_entity(du_id, ue_index, rb_id_, upper_dn_, upper_cn_, lower_dn_, metrics_enabled_, pcap_), cfg(config), sdu_queue(cfg.queue_size, logger), pcell_executor(pcell_executor_), - pcap_context(ue_index, rb_id, /* is_uplink */ false) + pcap_context(ue_index, rb_id_, /* is_uplink */ false) { metrics.metrics_set_mode(rlc_mode::tm); logger.log_info("RLC TM created. {}", cfg); } // TS 38.322 v16.2.0 Sec. 5.2.1.1 -void rlc_tx_tm_entity::handle_sdu(byte_buffer sdu_buf) +void rlc_tx_tm_entity::handle_sdu(byte_buffer sdu_buf, bool is_retx) { rlc_sdu sdu_; sdu_.buf = std::move(sdu_buf); + // Sanity check for PDCP ReTx in RLC TM + if (SRSRAN_UNLIKELY(is_retx)) { + logger.log_error("Ignored unexpected PDCP retransmission flag in RLC TM SDU"); + } + size_t sdu_len = sdu_.buf.length(); if (sdu_queue.write(sdu_)) { logger.log_info(sdu_.buf.begin(), sdu_.buf.end(), "TX SDU. sdu_len={} {}", sdu_len, sdu_queue.get_state()); diff --git a/lib/rlc/rlc_tx_tm_entity.h b/lib/rlc/rlc_tx_tm_entity.h index 92482cdbd7..f05d014798 100644 --- a/lib/rlc/rlc_tx_tm_entity.h +++ b/lib/rlc/rlc_tx_tm_entity.h @@ -50,7 +50,7 @@ class rlc_tx_tm_entity : public rlc_tx_entity public: rlc_tx_tm_entity(gnb_du_id_t du_id, du_ue_index_t ue_index, - rb_id_t rb_id, + rb_id_t rb_id_, const rlc_tx_tm_config& config, rlc_tx_upper_layer_data_notifier& upper_dn_, rlc_tx_upper_layer_control_notifier& upper_cn_, @@ -64,7 +64,7 @@ class rlc_tx_tm_entity : public rlc_tx_entity }; // Interfaces for higher layers - void handle_sdu(byte_buffer sdu_buf) override; + void handle_sdu(byte_buffer sdu_buf, bool is_retx) override; void discard_sdu(uint32_t pdcp_sn) override; // Interfaces for lower layers diff --git a/lib/rlc/rlc_tx_um_entity.cpp b/lib/rlc/rlc_tx_um_entity.cpp index f020536237..97f7805934 100644 --- a/lib/rlc/rlc_tx_um_entity.cpp +++ b/lib/rlc/rlc_tx_um_entity.cpp @@ -29,7 +29,7 @@ using namespace srsran; rlc_tx_um_entity::rlc_tx_um_entity(gnb_du_id_t du_id, du_ue_index_t ue_index, - rb_id_t rb_id, + rb_id_t rb_id_, const rlc_tx_um_config& config, rlc_tx_upper_layer_data_notifier& upper_dn_, rlc_tx_upper_layer_control_notifier& upper_cn_, @@ -37,7 +37,7 @@ rlc_tx_um_entity::rlc_tx_um_entity(gnb_du_id_t du_id, task_executor& pcell_executor_, bool metrics_enabled, rlc_pcap& pcap_) : - rlc_tx_entity(du_id, ue_index, rb_id, upper_dn_, upper_cn_, lower_dn_, metrics_enabled, pcap_), + rlc_tx_entity(du_id, ue_index, rb_id_, upper_dn_, upper_cn_, lower_dn_, metrics_enabled, pcap_), cfg(config), sdu_queue(cfg.queue_size, logger), mod(cardinality(to_number(cfg.sn_field_length))), @@ -45,7 +45,7 @@ rlc_tx_um_entity::rlc_tx_um_entity(gnb_du_id_t du_id, head_len_first(rlc_um_pdu_header_size_no_so(cfg.sn_field_length)), head_len_not_first(rlc_um_pdu_header_size_with_so(cfg.sn_field_length)), pcell_executor(pcell_executor_), - pcap_context(ue_index, rb_id, config) + pcap_context(ue_index, rb_id_, config) { metrics.metrics_set_mode(rlc_mode::um_bidir); @@ -61,7 +61,7 @@ rlc_tx_um_entity::rlc_tx_um_entity(gnb_du_id_t du_id, } // TS 38.322 v16.2.0 Sec. 5.2.2.1 -void rlc_tx_um_entity::handle_sdu(byte_buffer sdu_buf) +void rlc_tx_um_entity::handle_sdu(byte_buffer sdu_buf, bool is_retx) { rlc_sdu sdu_; sdu_.time_of_arrival = std::chrono::high_resolution_clock::now(); @@ -69,6 +69,11 @@ void rlc_tx_um_entity::handle_sdu(byte_buffer sdu_buf) sdu_.buf = std::move(sdu_buf); sdu_.pdcp_sn = get_pdcp_sn(sdu_.buf, cfg.pdcp_sn_len, logger.get_basic_logger()); + // Sanity check for PDCP ReTx in RLC UM + if (SRSRAN_UNLIKELY(is_retx)) { + logger.log_error("Ignored unexpected PDCP retransmission flag in RLC UM SDU"); + } + size_t sdu_length = sdu_.buf.length(); if (sdu_queue.write(sdu_)) { logger.log_info(sdu_.buf.begin(), diff --git a/lib/rlc/rlc_tx_um_entity.h b/lib/rlc/rlc_tx_um_entity.h index 2bfceeff63..22fa1be52b 100644 --- a/lib/rlc/rlc_tx_um_entity.h +++ b/lib/rlc/rlc_tx_um_entity.h @@ -84,7 +84,7 @@ class rlc_tx_um_entity : public rlc_tx_entity public: rlc_tx_um_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, - rb_id_t rb_id, + rb_id_t rb_id_, const rlc_tx_um_config& config, rlc_tx_upper_layer_data_notifier& upper_dn_, rlc_tx_upper_layer_control_notifier& upper_cn_, @@ -98,7 +98,7 @@ class rlc_tx_um_entity : public rlc_tx_entity }; // Interfaces for higher layers - void handle_sdu(byte_buffer sdu_buf) override; + void handle_sdu(byte_buffer sdu_buf, bool is_retx) override; void discard_sdu(uint32_t pdcp_sn) override; // Interfaces for lower layers diff --git a/lib/rrc/rrc_du_impl.cpp b/lib/rrc/rrc_du_impl.cpp index f5f056c4b4..e7c48629fa 100644 --- a/lib/rrc/rrc_du_impl.cpp +++ b/lib/rrc/rrc_du_impl.cpp @@ -21,11 +21,11 @@ */ #include "rrc_du_impl.h" -#include "../ran/gnb_format.h" #include "ue/rrc_measurement_types_asn1_converters.h" +#include "ue/rrc_ue_helpers.h" #include "srsran/asn1/rrc_nr/cell_group_config.h" +#include "srsran/asn1/rrc_nr/dl_ccch_msg.h" #include "srsran/cu_cp/cu_cp_types.h" -#include "srsran/ran/nr_cgi_helpers.h" using namespace srsran; using namespace srs_cu_cp; @@ -108,6 +108,14 @@ bool rrc_du_impl::handle_served_cell_list(const std::vector& served_cell_list) override; // rrc_du_ue_repository + byte_buffer get_rrc_reject() override; rrc_ue_interface* add_ue(const rrc_ue_creation_message& msg) override; void release_ues() override; @@ -77,7 +78,7 @@ class rrc_du_impl : public rrc_du_interface // RRC-internal user database indexed by ue_index std::unordered_map> ue_db; // Cell database to store cell information from the DU - std::map cell_info_db; + std::map cell_info_db; }; } // namespace srs_cu_cp diff --git a/lib/rrc/ue/adapters/pdcp_adapters.h b/lib/rrc/ue/adapters/pdcp_adapters.h index 0e0d07a205..7304cf32e8 100644 --- a/lib/rrc/ue/adapters/pdcp_adapters.h +++ b/lib/rrc/ue/adapters/pdcp_adapters.h @@ -76,7 +76,11 @@ class pdcp_rrc_ue_tx_adapter : public pdcp_tx_lower_notifier public: pdcp_rrc_ue_tx_adapter() = default; - void on_new_pdu(byte_buffer pdu) override { pdcp_pdu = std::move(pdu); } + void on_new_pdu(byte_buffer pdu, bool is_retx) override + { + pdcp_pdu = std::move(pdu); + pdcp_pdu_is_retx = is_retx; + } void on_discard_pdu(uint32_t pdcp_sn) override { @@ -87,6 +91,7 @@ class pdcp_rrc_ue_tx_adapter : public pdcp_tx_lower_notifier private: byte_buffer pdcp_pdu; + bool pdcp_pdu_is_retx; }; /// Adapter between PDCP Tx control and RRC diff --git a/lib/rrc/ue/procedures/rrc_reestablishment_procedure.cpp b/lib/rrc/ue/procedures/rrc_reestablishment_procedure.cpp index 719369de5f..6cc90248e3 100644 --- a/lib/rrc/ue/procedures/rrc_reestablishment_procedure.cpp +++ b/lib/rrc/ue/procedures/rrc_reestablishment_procedure.cpp @@ -136,18 +136,14 @@ void rrc_reestablishment_procedure::operator()(coro_context>& c async_task rrc_reestablishment_procedure::handle_rrc_reestablishment_fallback() { + context.connection_cause.value = asn1::rrc_nr::establishment_cause_e::mt_access; + return launch_async([this](coro_context>& ctx) mutable { CORO_BEGIN(ctx); // Reject RRC Reestablishment Request by sending RRC Setup - CORO_AWAIT(launch_async(context, - asn1::rrc_nr::establishment_cause_e::mt_access, - du_to_cu_container, - rrc_ue_setup_notifier, - srb_notifier, - nas_notifier, - event_mng, - logger)); + CORO_AWAIT(launch_async( + context, du_to_cu_container, rrc_ue_setup_notifier, srb_notifier, nas_notifier, event_mng, logger)); if (old_ue_reest_context.ue_index != ue_index_t::invalid and !old_ue_reest_context.old_ue_fully_attached) { // The UE exists but still has not established an SRB2 and DRB. Request the release of the old UE. @@ -212,7 +208,7 @@ bool rrc_reestablishment_procedure::verify_security_context() // Get packed varShortMAC-Input asn1::rrc_nr::var_short_mac_input_s var_short_mac_input = {}; var_short_mac_input.source_pci = reestablishment_request.rrc_reest_request.ue_id.pci; - var_short_mac_input.target_cell_id.from_number(context.cell.cgi.nci); + var_short_mac_input.target_cell_id.from_number(context.cell.cgi.nci.value()); var_short_mac_input.source_c_rnti = reestablishment_request.rrc_reest_request.ue_id.c_rnti; byte_buffer var_short_mac_input_packed = {}; asn1::bit_ref bref(var_short_mac_input_packed); diff --git a/lib/rrc/ue/procedures/rrc_setup_procedure.cpp b/lib/rrc/ue/procedures/rrc_setup_procedure.cpp index e894d9b87f..8f838763cc 100644 --- a/lib/rrc/ue/procedures/rrc_setup_procedure.cpp +++ b/lib/rrc/ue/procedures/rrc_setup_procedure.cpp @@ -28,16 +28,14 @@ using namespace srsran; using namespace srsran::srs_cu_cp; using namespace asn1::rrc_nr; -rrc_setup_procedure::rrc_setup_procedure(rrc_ue_context_t& context_, - const asn1::rrc_nr::establishment_cause_e& cause_, - const byte_buffer& du_to_cu_container_, - rrc_ue_setup_proc_notifier& rrc_ue_notifier_, - rrc_ue_srb_handler& srb_notifier_, - rrc_ue_nas_notifier& nas_notifier_, - rrc_ue_event_manager& event_mng_, - rrc_ue_logger& logger_) : +rrc_setup_procedure::rrc_setup_procedure(rrc_ue_context_t& context_, + const byte_buffer& du_to_cu_container_, + rrc_ue_setup_proc_notifier& rrc_ue_notifier_, + rrc_ue_srb_handler& srb_notifier_, + rrc_ue_nas_notifier& nas_notifier_, + rrc_ue_event_manager& event_mng_, + rrc_ue_logger& logger_) : context(context_), - cause(cause_), du_to_cu_container(du_to_cu_container_), rrc_ue(rrc_ue_notifier_), srb_notifier(srb_notifier_), @@ -64,18 +62,23 @@ void rrc_setup_procedure::operator()(coro_context>& ctx) // Await UE response CORO_AWAIT(transaction); - if (transaction.has_response()) { - logger.log_debug("\"{}\" finished successfully", name()); - context.state = rrc_state::connected; - send_initial_ue_msg(transaction.response().msg.c1().rrc_setup_complete()); - } else if (transaction.failure_cause() == protocol_transaction_failure::timeout) { - logger.log_warning("\"{}\" timed out after {}ms", name(), context.cfg.rrc_procedure_timeout_ms); - rrc_ue.on_ue_release_required(cause_protocol_t::unspecified); - } else { - logger.log_warning("\"{}\" cancelled", name()); - // Do nothing. We are likely shutting down the DU processor. + if (!transaction.has_response()) { + if (transaction.failure_cause() == protocol_transaction_failure::timeout) { + logger.log_warning("\"{}\" timed out after {}ms", name(), context.cfg.rrc_procedure_timeout_ms); + rrc_ue.on_ue_release_required(cause_protocol_t::unspecified); + } else { + logger.log_warning("\"{}\" cancelled", name()); + // Do nothing. We are likely shutting down the DU processor. + } + CORO_EARLY_RETURN(); } + context.state = rrc_state::connected; + + send_initial_ue_msg(transaction.response().msg.c1().rrc_setup_complete()); + + logger.log_debug("\"{}\" finished successfully", name()); + CORO_RETURN(); } @@ -101,14 +104,15 @@ void rrc_setup_procedure::send_rrc_setup() rrc_ue.on_new_dl_ccch(dl_ccch_msg); } -void rrc_setup_procedure::send_initial_ue_msg(const asn1::rrc_nr::rrc_setup_complete_s& rrc_setup_complete_msg) +void rrc_setup_procedure::send_initial_ue_msg(const asn1::rrc_nr::rrc_setup_complete_s& rrc_setup_complete) { - cu_cp_initial_ue_message init_ue_msg = {}; + cu_cp_initial_ue_message init_ue_msg = {}; + init_ue_msg.ue_index = context.ue_index; - init_ue_msg.nas_pdu = rrc_setup_complete_msg.crit_exts.rrc_setup_complete().ded_nas_msg.copy(); - init_ue_msg.establishment_cause = static_cast(cause.value); + init_ue_msg.nas_pdu = rrc_setup_complete.crit_exts.rrc_setup_complete().ded_nas_msg.copy(); + init_ue_msg.establishment_cause = static_cast(context.connection_cause.value); init_ue_msg.user_location_info.nr_cgi = context.cell.cgi; - init_ue_msg.user_location_info.tai.plmn_id = context.cell.cgi.plmn_hex; + init_ue_msg.user_location_info.tai.plmn_id = context.cell.cgi.plmn_id; init_ue_msg.user_location_info.tai.tac = context.cell.tac; cu_cp_five_g_s_tmsi five_g_s_tmsi; @@ -118,9 +122,9 @@ void rrc_setup_procedure::send_initial_ue_msg(const asn1::rrc_nr::rrc_setup_comp init_ue_msg.five_g_s_tmsi = five_g_s_tmsi; } - if (rrc_setup_complete_msg.crit_exts.rrc_setup_complete().registered_amf_present) { + if (rrc_setup_complete.crit_exts.rrc_setup_complete().registered_amf_present) { cu_cp_amf_identifier_t amf_id = - asn1_to_amf_identifier(rrc_setup_complete_msg.crit_exts.rrc_setup_complete().registered_amf.amf_id); + asn1_to_amf_identifier(rrc_setup_complete.crit_exts.rrc_setup_complete().registered_amf.amf_id); init_ue_msg.amf_set_id = amf_id.amf_set_id; // TODO: Handle PLMN ID diff --git a/lib/rrc/ue/procedures/rrc_setup_procedure.h b/lib/rrc/ue/procedures/rrc_setup_procedure.h index ef420246f4..a5cc84ea46 100644 --- a/lib/rrc/ue/procedures/rrc_setup_procedure.h +++ b/lib/rrc/ue/procedures/rrc_setup_procedure.h @@ -25,6 +25,7 @@ #include "../rrc_ue_context.h" #include "../rrc_ue_logger.h" #include "rrc_ue_event_manager.h" +#include "srsran/asn1/rrc_nr/ul_dcch_msg_ies.h" #include "srsran/rrc/rrc_du.h" #include "srsran/rrc/rrc_ue.h" #include "srsran/support/async/async_task.h" @@ -68,14 +69,13 @@ namespace srs_cu_cp { class rrc_setup_procedure { public: - rrc_setup_procedure(rrc_ue_context_t& context_, - const asn1::rrc_nr::establishment_cause_e& cause_, - const byte_buffer& du_to_cu_container_, - rrc_ue_setup_proc_notifier& rrc_ue_notifier_, - rrc_ue_srb_handler& srb_notifier_, - rrc_ue_nas_notifier& nas_notifier_, - rrc_ue_event_manager& ev_mng_, - rrc_ue_logger& logger_); + rrc_setup_procedure(rrc_ue_context_t& context_, + const byte_buffer& du_to_cu_container_, + rrc_ue_setup_proc_notifier& rrc_ue_notifier_, + rrc_ue_srb_handler& srb_notifier_, + rrc_ue_nas_notifier& nas_notifier_, + rrc_ue_event_manager& ev_mng_, + rrc_ue_logger& logger_); void operator()(coro_context>& ctx); @@ -85,15 +85,14 @@ class rrc_setup_procedure /// Instruct DU processor to create SRB1 bearer. void create_srb1(); - /// \remark Send RRC Setup, see section 5.3.3 in TS 36.331 + /// \remark Send RRC Setup, see section 5.3.3 in TS 36.331. void send_rrc_setup(); /// \remark Forward the Initial UE Message to the NGAP void send_initial_ue_msg(const asn1::rrc_nr::rrc_setup_complete_s& rrc_setup_complete_msg); - rrc_ue_context_t& context; - const asn1::rrc_nr::establishment_cause_e cause; - const byte_buffer& du_to_cu_container; + rrc_ue_context_t& context; + const byte_buffer& du_to_cu_container; rrc_ue_setup_proc_notifier& rrc_ue; // handler to the parent RRC UE object rrc_ue_srb_handler& srb_notifier; // for creation of SRBs diff --git a/lib/rrc/ue/rrc_asn1_helpers.h b/lib/rrc/ue/rrc_asn1_helpers.h index fdca2ea466..2ea5461284 100644 --- a/lib/rrc/ue/rrc_asn1_helpers.h +++ b/lib/rrc/ue/rrc_asn1_helpers.h @@ -75,7 +75,7 @@ inline expected get_transaction_id(const asn1::rrc_nr::ul_dcch_msg_s& m default: break; } - return {default_error_t{}}; + return make_unexpected(default_error_t{}); } /// \brief Fills ASN.1 RRC Setup struct. diff --git a/lib/rrc/ue/rrc_ue_message_handlers.cpp b/lib/rrc/ue/rrc_ue_message_handlers.cpp index 6b202378f9..aa61d8bd7d 100644 --- a/lib/rrc/ue/rrc_ue_message_handlers.cpp +++ b/lib/rrc/ue/rrc_ue_message_handlers.cpp @@ -109,18 +109,13 @@ void rrc_ue_impl::handle_rrc_setup_request(const asn1::rrc_nr::rrc_setup_request context.connection_cause.value = request_ies.establishment_cause.value; // Launch RRC setup procedure - cu_cp_ue_notifier.schedule_async_task(launch_async(context, - request_ies.establishment_cause.value, - du_to_cu_container, - *this, - get_rrc_ue_srb_handler(), - nas_notifier, - *event_mng, - logger)); + cu_cp_ue_notifier.schedule_async_task(launch_async( + context, du_to_cu_container, *this, get_rrc_ue_srb_handler(), nas_notifier, *event_mng, logger)); } void rrc_ue_impl::handle_rrc_reest_request(const asn1::rrc_nr::rrc_reest_request_s& msg) { + // Launch RRC re-establishment procedure cu_cp_ue_notifier.schedule_async_task(launch_async(msg, context, du_to_cu_container, @@ -225,7 +220,7 @@ void rrc_ue_impl::handle_ul_info_transfer(const ul_info_transfer_ies_s& ul_info_ ul_nas_msg.ue_index = context.ue_index; ul_nas_msg.nas_pdu = ul_info_transfer.ded_nas_msg.copy(); ul_nas_msg.user_location_info.nr_cgi = context.cell.cgi; - ul_nas_msg.user_location_info.tai.plmn_id = context.cell.cgi.plmn_hex; + ul_nas_msg.user_location_info.tai.plmn_id = context.cell.cgi.plmn_id; ul_nas_msg.user_location_info.tai.tac = context.cell.tac; nas_notifier.on_ul_nas_transport_message(ul_nas_msg); @@ -347,7 +342,7 @@ rrc_ue_release_context rrc_ue_impl::get_rrc_ue_release_context(bool requires_rrc // prepare location info to return rrc_ue_release_context release_context; release_context.user_location_info.nr_cgi = context.cell.cgi; - release_context.user_location_info.tai.plmn_id = context.cell.cgi.plmn_hex; + release_context.user_location_info.tai.plmn_id = context.cell.cgi.plmn_id; release_context.user_location_info.tai.tac = context.cell.tac; if (requires_rrc_message) { diff --git a/lib/rrc/ue/rrc_ue_srb_context.h b/lib/rrc/ue/rrc_ue_srb_context.h index 5a21b4b85a..465869eaa4 100644 --- a/lib/rrc/ue/rrc_ue_srb_context.h +++ b/lib/rrc/ue/rrc_ue_srb_context.h @@ -149,7 +149,7 @@ class ue_srb_context pdcp_rx_result unpack_pdcp_pdu(byte_buffer pdcp_pdu) { auto buffer_chain = byte_buffer_chain::create(std::move(pdcp_pdu)); - if (buffer_chain.is_error()) { + if (not buffer_chain.has_value()) { return pdcp_rx_result{ngap_cause_misc_t::not_enough_user_plane_processing_res}; } diff --git a/lib/scheduler/CMakeLists.txt b/lib/scheduler/CMakeLists.txt index 0c7ee12e1d..f6119befff 100644 --- a/lib/scheduler/CMakeLists.txt +++ b/lib/scheduler/CMakeLists.txt @@ -29,6 +29,7 @@ set(SOURCES pdcch_scheduling/pdcch_slot_resource_allocator.cpp policy/scheduler_policy_factory.cpp policy/scheduler_time_rr.cpp + policy/scheduler_time_pf.cpp pucch_scheduling/pucch_allocator_impl.cpp pucch_scheduling/pucch_guardbands_scheduler.cpp pucch_scheduling/pucch_resource_manager.cpp diff --git a/lib/scheduler/config/sched_config_manager.cpp b/lib/scheduler/config/sched_config_manager.cpp index 8d9fcfdff2..a7c16c74e1 100644 --- a/lib/scheduler/config/sched_config_manager.cpp +++ b/lib/scheduler/config/sched_config_manager.cpp @@ -84,7 +84,7 @@ const cell_configuration* sched_config_manager::add_cell(const sched_cell_config // Ensure the common cell config is valid. auto ret = config_validators::validate_sched_cell_configuration_request_message(msg, expert_params); - srsran_assert(not ret.is_error(), "Invalid cell configuration request message. Cause: {}", ret.error().c_str()); + srsran_assert(ret.has_value(), "Invalid cell configuration request message. Cause: {}", ret.error().c_str()); added_cells.emplace(msg.cell_index, std::make_unique(expert_params, msg)); @@ -118,7 +118,7 @@ ue_config_update_event sched_config_manager::add_ue(const sched_ue_creation_requ error_type result = config_validators::validate_sched_ue_creation_request_message(cfg_req, *added_cells[pcell_index]); - if (result.is_error()) { + if (not result.has_value()) { logger.warning("ue={} rnti={}: Discarding invalid UE creation request. Cause: {}", cfg_req.ue_index, cfg_req.crnti, diff --git a/lib/scheduler/config/scheduler_cell_config_validator.cpp b/lib/scheduler/config/scheduler_cell_config_validator.cpp index 0d8e7d2dd4..590d973546 100644 --- a/lib/scheduler/config/scheduler_cell_config_validator.cpp +++ b/lib/scheduler/config/scheduler_cell_config_validator.cpp @@ -37,12 +37,12 @@ using namespace config_validators; #define VERIFY(cond, ...) \ if (not(cond)) { \ - return error_type(fmt::format(__VA_ARGS__)); \ + return make_unexpected(fmt::format(__VA_ARGS__)); \ } #define HANDLE_CODE(cond) \ { \ auto ret = cond; \ - if (ret.is_error()) { \ + if (not ret.has_value()) { \ return ret; \ } \ } @@ -82,7 +82,7 @@ static error_type validate_rach_cfg_common(const sched_cell_configu // Check PRACH config index. auto code = prach_helper::prach_config_index_is_valid(rach_cfg_cmn.rach_cfg_generic.prach_config_index, dplx_mode); - if (code.is_error()) { + if (not code.has_value()) { return code; } @@ -98,13 +98,14 @@ static error_type validate_rach_cfg_common(const sched_cell_configu if (msg.tdd_ul_dl_cfg_common.has_value()) { auto ret = prach_helper::prach_fits_in_tdd_pattern( pusch_scs, rach_cfg_cmn.rach_cfg_generic.prach_config_index, *msg.tdd_ul_dl_cfg_common); - if (ret.is_error()) { + if (not ret.has_value()) { std::string s = fmt::format("PRACH configuration index {} not supported with current TDD pattern.", rach_cfg_cmn.rach_cfg_generic.prach_config_index); if (ret.error().empty()) { - return s + fmt::format(" Cause: PRACH configuration is not valid"); + return make_unexpected(s + fmt::format(" Cause: PRACH configuration is not valid")); } else { - return s + fmt::format(" Cause: Slot indexes used for PRACH {} fall outside TDD UL slots", ret.error()); + return make_unexpected( + s + fmt::format(" Cause: Slot indexes used for PRACH {} fall outside TDD UL slots", ret.error())); } } } @@ -113,7 +114,7 @@ static error_type validate_rach_cfg_common(const sched_cell_configu code = prach_helper::zero_correlation_zone_is_valid(rach_cfg_cmn.rach_cfg_generic.zero_correlation_zone_config, rach_cfg_cmn.rach_cfg_generic.prach_config_index, dplx_mode); - if (code.is_error()) { + if (not code.has_value()) { return code; } diff --git a/lib/scheduler/config/serving_cell_config_validator.cpp b/lib/scheduler/config/serving_cell_config_validator.cpp index 4dffcbffee..51578d021c 100644 --- a/lib/scheduler/config/serving_cell_config_validator.cpp +++ b/lib/scheduler/config/serving_cell_config_validator.cpp @@ -32,7 +32,7 @@ using namespace srsran; #define VERIFY_ID_EXISTS(cond_lambda, id_list, ...) \ if (std::find_if(id_list.begin(), id_list.end(), cond_lambda) == id_list.end()) { \ - return error_type(fmt::format(__VA_ARGS__)); \ + return make_unexpected(fmt::format(__VA_ARGS__)); \ } validator_result srsran::config_validators::validate_pdcch_cfg(const serving_cell_config& ue_cell_cfg, diff --git a/lib/scheduler/policy/scheduler_policy_factory.cpp b/lib/scheduler/policy/scheduler_policy_factory.cpp index 21d1877f83..e35690abc5 100644 --- a/lib/scheduler/policy/scheduler_policy_factory.cpp +++ b/lib/scheduler/policy/scheduler_policy_factory.cpp @@ -21,12 +21,18 @@ */ #include "scheduler_policy_factory.h" +#include "scheduler_time_pf.h" #include "scheduler_time_rr.h" using namespace srsran; -std::unique_ptr srsran::create_scheduler_strategy(const scheduler_strategy_params& params, - const scheduler_ue_expert_config& expert_cfg_) +std::unique_ptr srsran::create_scheduler_strategy(const scheduler_ue_expert_config& expert_cfg_) { - return std::make_unique(expert_cfg_); + if (std::holds_alternative(expert_cfg_.strategy_cfg)) { + return std::make_unique(expert_cfg_); + } + if (std::holds_alternative(expert_cfg_.strategy_cfg)) { + return std::make_unique(expert_cfg_); + } + return nullptr; } diff --git a/lib/scheduler/policy/scheduler_policy_factory.h b/lib/scheduler/policy/scheduler_policy_factory.h index 53eed0b634..921e0b283f 100644 --- a/lib/scheduler/policy/scheduler_policy_factory.h +++ b/lib/scheduler/policy/scheduler_policy_factory.h @@ -26,12 +26,6 @@ namespace srsran { -struct scheduler_strategy_params { - std::string strategy = "time_rr"; - srslog::basic_logger* logger; -}; - -std::unique_ptr create_scheduler_strategy(const scheduler_strategy_params& params, - const scheduler_ue_expert_config& expert_cfg_); +std::unique_ptr create_scheduler_strategy(const scheduler_ue_expert_config& expert_cfg_); } // namespace srsran diff --git a/lib/scheduler/policy/scheduler_time_pf.cpp b/lib/scheduler/policy/scheduler_time_pf.cpp new file mode 100644 index 0000000000..4e8cad282d --- /dev/null +++ b/lib/scheduler/policy/scheduler_time_pf.cpp @@ -0,0 +1,361 @@ +/* + * + * 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 "scheduler_time_pf.h" +#include "../support/csi_report_helpers.h" + +using namespace srsran; + +scheduler_time_pf::scheduler_time_pf(const scheduler_ue_expert_config& expert_cfg_) : + fairness_coeff(std::get(expert_cfg_.strategy_cfg).pf_sched_fairness_coeff) +{ +} + +void scheduler_time_pf::dl_sched(ue_pdsch_allocator& pdsch_alloc, + const ue_resource_grid_view& res_grid, + const ue_repository& ues) +{ + // Clear the existing contents of the queue. + dl_queue.clear(); + + // Remove deleted users from history. + for (auto it = ue_history_db.begin(); it != ue_history_db.end();) { + if (not ues.contains(it->ue_index)) { + ue_history_db.erase(it->ue_index); + } + ++it; + } + + // Add new users to history db, and update DL priority queue. + for (const auto& u : ues) { + if (not ue_history_db.contains(u->ue_index)) { + // TODO: Consider other cells when carrier aggregation is supported. + ue_history_db.emplace(u->ue_index, ue_ctxt{u->ue_index, u->get_pcell().cell_index, this}); + } + ue_ctxt& ctxt = ue_history_db[u->ue_index]; + ctxt.compute_dl_prio(*u); + dl_queue.push(&ctxt); + } + + alloc_result alloc_result = {alloc_status::invalid_params}; + while (not dl_queue.empty()) { + ue_ctxt& ue = *dl_queue.top(); + if (alloc_result.status != alloc_status::skip_slot) { + alloc_result = try_dl_alloc(ue, ues, pdsch_alloc); + } + ue.save_dl_alloc(alloc_result.alloc_bytes); + // Re-add the UE to the queue if scheduling of re-transmission fails so that scheduling of retransmission are + // attempted again before scheduling new transmissions. + if (ue.dl_retx_h != nullptr and alloc_result.status == alloc_status::invalid_params) { + dl_queue.push(&ue); + } + dl_queue.pop(); + } +} + +void scheduler_time_pf::ul_sched(ue_pusch_allocator& pusch_alloc, + const ue_resource_grid_view& res_grid, + const ue_repository& ues) +{ + // Clear the existing contents of the queue. + ul_queue.clear(); + + // Remove deleted users from history. + for (auto it = ue_history_db.begin(); it != ue_history_db.end();) { + if (not ues.contains(it->ue_index)) { + ue_history_db.erase(it->ue_index); + } + ++it; + } + + // Add new users to history db, and update UL priority queue. + for (const auto& u : ues) { + if (not ue_history_db.contains(u->ue_index)) { + // TODO: Consider other cells when carrier aggregation is supported. + ue_history_db.emplace(u->ue_index, ue_ctxt{u->ue_index, u->get_pcell().cell_index, this}); + } + ue_ctxt& ctxt = ue_history_db[u->ue_index]; + ctxt.compute_ul_prio(*u, res_grid); + ul_queue.push(&ctxt); + } + + alloc_result alloc_result = {alloc_status::invalid_params}; + while (not ul_queue.empty()) { + ue_ctxt& ue = *ul_queue.top(); + if (alloc_result.status != alloc_status::skip_slot) { + alloc_result = try_ul_alloc(ue, ues, pusch_alloc); + } + ue.save_ul_alloc(alloc_result.alloc_bytes); + // Re-add the UE to the queue if scheduling of re-transmission fails so that scheduling of retransmission are + // attempted again before scheduling new transmissions. + if (ue.ul_retx_h != nullptr and alloc_result.status == alloc_status::invalid_params) { + ul_queue.push(&ue); + } + ul_queue.pop(); + } +} + +alloc_result scheduler_time_pf::try_dl_alloc(ue_ctxt& ctxt, const ue_repository& ues, ue_pdsch_allocator& pdsch_alloc) +{ + alloc_result alloc_result = {alloc_status::invalid_params}; + ue_pdsch_grant grant{&ues[ctxt.ue_index], ctxt.cell_index}; + // Prioritize reTx over newTx. + if (ctxt.dl_retx_h != nullptr) { + grant.h_id = ctxt.dl_retx_h->id; + alloc_result = pdsch_alloc.allocate_dl_grant(grant); + if (alloc_result.status == alloc_status::success) { + ctxt.dl_retx_h = nullptr; + } + // Return result here irrespective of the outcome so that reTxs of UEs are scheduled before scheduling newTxs of + // UEs. + return alloc_result; + } + + if (ctxt.dl_newtx_h != nullptr) { + grant.h_id = ctxt.dl_newtx_h->id; + grant.recommended_nof_bytes = ues[ctxt.ue_index].pending_dl_newtx_bytes(); + alloc_result = pdsch_alloc.allocate_dl_grant(grant); + if (alloc_result.status == alloc_status::success) { + ctxt.dl_newtx_h = nullptr; + } + return alloc_result; + } + + return {alloc_status::skip_ue}; +} + +alloc_result scheduler_time_pf::try_ul_alloc(ue_ctxt& ctxt, const ue_repository& ues, ue_pusch_allocator& pusch_alloc) +{ + alloc_result alloc_result = {alloc_status::invalid_params}; + ue_pusch_grant grant{&ues[ctxt.ue_index], ctxt.cell_index}; + // Prioritize reTx over newTx. + if (ctxt.ul_retx_h != nullptr) { + grant.h_id = ctxt.ul_retx_h->id; + alloc_result = pusch_alloc.allocate_ul_grant(grant); + if (alloc_result.status == alloc_status::success) { + ctxt.ul_retx_h = nullptr; + } + // Return result here irrespective of the outcome so that reTxs of UEs are scheduled before scheduling newTxs of + // UEs. + return alloc_result; + } + + if (ctxt.ul_newtx_h != nullptr) { + grant.h_id = ctxt.ul_newtx_h->id; + grant.recommended_nof_bytes = ues[ctxt.ue_index].pending_ul_newtx_bytes(); + alloc_result = pusch_alloc.allocate_ul_grant(grant); + if (alloc_result.status == alloc_status::success) { + ctxt.ul_newtx_h = nullptr; + } + return alloc_result; + } + + return {alloc_status::skip_ue}; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void scheduler_time_pf::ue_ctxt::compute_dl_prio(const ue& u) +{ + dl_retx_h = nullptr; + dl_newtx_h = nullptr; + dl_prio = 0; + const ue_cell* ue_cc = u.find_cell(cell_index); + if (ue_cc == nullptr or not ue_cc->is_active() or ue_cc->is_in_fallback_mode()) { + return; + } + + // Calculate DL priority. + dl_retx_h = ue_cc->harqs.find_pending_dl_retx(); + dl_newtx_h = ue_cc->harqs.find_empty_dl_harq(); + if (dl_retx_h != nullptr or (dl_newtx_h != nullptr and u.has_pending_dl_newtx_bytes())) { + // NOTE: It does not matter whether it's a reTx or newTx since DL priority is computed based on estimated + // instantaneous achievable rate to the average throughput of the user. + // [Implementation-defined] We consider only the SearchSpace defined in UE dedicated configuration. + const auto* ss_info = ue_cc->cfg().find_search_space( + ue_cc->cfg().cfg_dedicated().init_dl_bwp.pdcch_cfg->search_spaces.back().get_id()); + if (ss_info == nullptr) { + return; + } + span pdsch_td_res_list = ss_info->pdsch_time_domain_list; + // [Implementation-defined] We pick the first element since PDSCH time domain resource list is sorted in descending + // order of nof. PDSCH symbols. And, we want to calculate estimate of instantaneous achievable rate with maximum + // nof. PDSCH symbols. + const pdsch_time_domain_resource_allocation& pdsch_td_cfg = pdsch_td_res_list.front(); + + pdsch_config_params pdsch_cfg; + switch (ss_info->get_dl_dci_format()) { + case dci_dl_format::f1_0: + pdsch_cfg = get_pdsch_config_f1_0_c_rnti(ue_cc->cfg().cell_cfg_common, &ue_cc->cfg(), pdsch_td_cfg); + break; + case dci_dl_format::f1_1: + pdsch_cfg = get_pdsch_config_f1_1_c_rnti( + ue_cc->cfg(), pdsch_td_cfg, ue_cc->channel_state_manager().get_nof_dl_layers()); + break; + default: + report_fatal_error("Unsupported PDCCH DCI DL format"); + } + + std::optional mcs = ue_cc->link_adaptation_controller().calculate_dl_mcs(pdsch_cfg.mcs_table); + if (not mcs.has_value()) { + // CQI is either 0, or > 15. + dl_retx_h = nullptr; + dl_newtx_h = nullptr; + return; + } + + // Calculate DL PF priority. + // NOTE: Estimated instantaneous DL rate is calculated assuming entire BWP CRBs are allocated to UE. + const double estimated_rate = ue_cc->get_estimated_dl_rate(pdsch_cfg, mcs.value(), ss_info->dl_crb_lims.length()); + const double current_avg_rate = dl_avg_rate(); + if (current_avg_rate != 0) { + dl_prio = estimated_rate / pow(current_avg_rate, parent->fairness_coeff); + } else { + dl_prio = estimated_rate == 0 ? 0 : std::numeric_limits::max(); + } + return; + } + dl_newtx_h = nullptr; +} + +void scheduler_time_pf::ue_ctxt::compute_ul_prio(const ue& u, const ue_resource_grid_view& res_grid) +{ + ul_retx_h = nullptr; + ul_newtx_h = nullptr; + ul_prio = 0; + sr_ind_received = false; + const ue_cell* ue_cc = u.find_cell(cell_index); + if (ue_cc == nullptr or not ue_cc->is_active() or ue_cc->is_in_fallback_mode()) { + return; + } + + // Calculate UL priority. + ul_retx_h = ue_cc->harqs.find_pending_ul_retx(); + ul_newtx_h = ue_cc->harqs.find_empty_ul_harq(); + sr_ind_received = u.has_pending_sr(); + if (ul_retx_h != nullptr or (ul_newtx_h != nullptr and u.pending_ul_newtx_bytes() > 0)) { + // NOTE: It does not matter whether it's a reTx or newTx since UL priority is computed based on estimated + // instantaneous achievable rate to the average throughput of the user. + // [Implementation-defined] We consider only the SearchSpace defined in UE dedicated configuration. + const auto* ss_info = ue_cc->cfg().find_search_space( + ue_cc->cfg().cfg_dedicated().init_dl_bwp.pdcch_cfg->search_spaces.back().get_id()); + if (ss_info == nullptr) { + return; + } + span pusch_td_res_list = ss_info->pusch_time_domain_list; + // [Implementation-defined] We pick the first element since PUSCH time domain resource list is sorted in descending + // order of nof. PUSCH symbols. And, we want to calculate estimate of instantaneous achievable rate with maximum + // nof. PUSCH symbols. + const pusch_time_domain_resource_allocation& pusch_td_cfg = pusch_td_res_list.front(); + // [Implementation-defined] We assume nof. HARQ ACK bits is zero at PUSCH slot as a simplification in calculating + // estimated instantaneous achievable rate. + constexpr unsigned nof_harq_ack_bits = 0; + const bool is_csi_report_slot = csi_helper::is_csi_reporting_slot( + u.get_pcell().cfg().cfg_dedicated(), res_grid.get_pusch_slot(cell_index, pusch_td_cfg.k2)); + + pusch_config_params pusch_cfg; + switch (ss_info->get_ul_dci_format()) { + case dci_ul_format::f0_0: + pusch_cfg = get_pusch_config_f0_0_c_rnti(ue_cc->cfg().cell_cfg_common, + &ue_cc->cfg(), + ue_cc->cfg().cell_cfg_common.ul_cfg_common.init_ul_bwp, + pusch_td_cfg, + nof_harq_ack_bits, + is_csi_report_slot); + break; + case dci_ul_format::f0_1: + pusch_cfg = get_pusch_config_f0_1_c_rnti(ue_cc->cfg(), + pusch_td_cfg, + ue_cc->channel_state_manager().get_nof_ul_layers(), + nof_harq_ack_bits, + is_csi_report_slot); + break; + default: + report_fatal_error("Unsupported PDCCH DCI UL format"); + } + + sch_mcs_index mcs = ue_cc->link_adaptation_controller().calculate_ul_mcs(pusch_cfg.mcs_table); + + // Calculate UL PF priority. + // NOTE: Estimated instantaneous UL rate is calculated assuming entire BWP CRBs are allocated to UE. + const double estimated_rate = ue_cc->get_estimated_ul_rate(pusch_cfg, mcs.value(), ss_info->ul_crb_lims.length()); + const double current_avg_rate = ul_avg_rate(); + if (current_avg_rate != 0) { + ul_prio = estimated_rate / pow(current_avg_rate, parent->fairness_coeff); + } else { + ul_prio = estimated_rate == 0 ? 0 : std::numeric_limits::max(); + } + return; + } + ul_newtx_h = nullptr; +} + +void scheduler_time_pf::ue_ctxt::save_dl_alloc(uint32_t alloc_bytes) +{ + if (dl_nof_samples < 1 / parent->exp_avg_alpha) { + // Fast start before transitioning to exponential average. + dl_avg_rate_ = dl_avg_rate_ + (alloc_bytes - dl_avg_rate_) / (dl_nof_samples + 1); + } else { + dl_avg_rate_ = (1 - parent->exp_avg_alpha) * dl_avg_rate_ + (parent->exp_avg_alpha) * alloc_bytes; + } + dl_nof_samples++; +} + +void scheduler_time_pf::ue_ctxt::save_ul_alloc(uint32_t alloc_bytes) +{ + if (ul_nof_samples < 1 / parent->exp_avg_alpha) { + // Fast start before transitioning to exponential average. + ul_avg_rate_ = ul_avg_rate_ + (alloc_bytes - ul_avg_rate_) / (ul_nof_samples + 1); + } else { + ul_avg_rate_ = (1 - parent->exp_avg_alpha) * ul_avg_rate_ + (parent->exp_avg_alpha) * alloc_bytes; + } + ul_nof_samples++; +} + +bool scheduler_time_pf::ue_dl_prio_compare::operator()(const scheduler_time_pf::ue_ctxt* lhs, + const scheduler_time_pf::ue_ctxt* rhs) const +{ + const bool is_lhs_retx = lhs->dl_retx_h != nullptr; + const bool is_rhs_retx = rhs->dl_retx_h != nullptr; + return (not is_lhs_retx and is_rhs_retx) or (is_lhs_retx == is_rhs_retx and lhs->dl_prio < rhs->dl_prio); +} + +bool scheduler_time_pf::ue_ul_prio_compare::operator()(const scheduler_time_pf::ue_ctxt* lhs, + const scheduler_time_pf::ue_ctxt* rhs) const +{ + const bool is_lhs_retx = lhs->ul_retx_h != nullptr; + const bool is_rhs_retx = rhs->ul_retx_h != nullptr; + if (not is_lhs_retx and is_rhs_retx) { + return true; + } + if (is_lhs_retx == is_rhs_retx) { + if (not is_lhs_retx) { + // NewTx and SR indication received for one of the UEs. + if (not lhs->sr_ind_received and rhs->sr_ind_received) { + return true; + } + } + // All other cases compare priorities. + return lhs->ul_prio < rhs->ul_prio; + } + return false; +} diff --git a/lib/scheduler/policy/scheduler_time_pf.h b/lib/scheduler/policy/scheduler_time_pf.h new file mode 100644 index 0000000000..f4509900e9 --- /dev/null +++ b/lib/scheduler/policy/scheduler_time_pf.h @@ -0,0 +1,162 @@ +/* + * + * 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 "scheduler_policy.h" + +namespace srsran { + +class scheduler_time_pf : public scheduler_policy +{ +public: + scheduler_time_pf(const scheduler_ue_expert_config& expert_cfg_); + + void + dl_sched(ue_pdsch_allocator& pdsch_alloc, const ue_resource_grid_view& res_grid, const ue_repository& ues) override; + + void + ul_sched(ue_pusch_allocator& pusch_alloc, const ue_resource_grid_view& res_grid, const ue_repository& ues) override; + +private: + /// Fairness parameters. + const double fairness_coeff; + const double exp_avg_alpha = 0.01; + + /// Holds the information needed to compute priority of a UE in a priority queue. + struct ue_ctxt { + ue_ctxt(du_ue_index_t ue_index_, du_cell_index_t cell_index_, const scheduler_time_pf* parent_) : + ue_index(ue_index_), cell_index(cell_index_), parent(parent_) + { + } + + /// Returns average DL rate expressed in bytes per slot. + [[nodiscard]] double dl_avg_rate() const { return dl_nof_samples == 0 ? 0 : dl_avg_rate_; } + /// Returns average UL rate expressed in bytes per slot. + [[nodiscard]] double ul_avg_rate() const { return ul_nof_samples == 0 ? 0 : ul_avg_rate_; } + + void compute_dl_prio(const ue& u); + void compute_ul_prio(const ue& u, const ue_resource_grid_view& res_grid); + + void save_dl_alloc(uint32_t alloc_bytes); + void save_ul_alloc(uint32_t alloc_bytes); + + const du_ue_index_t ue_index; + const du_cell_index_t cell_index; + const scheduler_time_pf* parent; + + /// DL priority value of the UE. + double dl_prio = 0; + /// UL priority value of the UE. + double ul_prio = 0; + + const dl_harq_process* dl_retx_h = nullptr; + const dl_harq_process* dl_newtx_h = nullptr; + const ul_harq_process* ul_retx_h = nullptr; + const ul_harq_process* ul_newtx_h = nullptr; + /// Flag indicating whether SR indication from the UE is received or not. + bool sr_ind_received = false; + + private: + /// Average DL rate expressed in bytes per slot experienced by UE. + double dl_avg_rate_ = 0; + /// Average UL rate expressed in bytes per slot experienced by UE. + double ul_avg_rate_ = 0; + /// Nof. DL samples over which average DL bitrate is computed. + uint32_t dl_nof_samples = 0; + /// Nof. UL samples over which average DL bitrate is computed. + uint32_t ul_nof_samples = 0; + }; + + /// \brief Attempts to allocate PDSCH for a UE. + /// \return Returns allocation status and nof. allocated bytes. + alloc_result try_dl_alloc(ue_ctxt& ctxt, const ue_repository& ues, ue_pdsch_allocator& pdsch_alloc); + /// \brief Attempts to allocate PUSCH for a UE. + /// \return Returns allocation status and nof. allocated bytes. + alloc_result try_ul_alloc(ue_ctxt& ctxt, const ue_repository& ues, ue_pusch_allocator& pusch_alloc); + + slotted_id_table ue_history_db; + + struct ue_dl_prio_compare { + bool operator()(const ue_ctxt* lhs, const ue_ctxt* rhs) const; + }; + struct ue_ul_prio_compare { + bool operator()(const ue_ctxt* lhs, const ue_ctxt* rhs) const; + }; + + // Note: the std::priority_queue makes its underlying container protected, so it seems that they are ok with + // inheritance. + class ue_dl_queue_t : public std::priority_queue, ue_dl_prio_compare> + { + using base_type = std::priority_queue, ue_dl_prio_compare>; + + public: + // Note: faster than while(!empty()) pop() because it avoids the O(NlogN). Faster than = {}, because it preserves + // memory. + void clear() + { + // Access to underlying vector. + this->c.clear(); + } + + // Adapter of the priority_queue push method to avoid adding candidates with skip priority level. + void push(ue_ctxt* elem) + { + if (elem->dl_retx_h == nullptr and elem->dl_newtx_h == nullptr) { + return; + } + base_type::push(elem); + } + }; + + // Note: the std::priority_queue makes its underlying container protected, so it seems that they are ok with + // inheritance. + class ue_ul_queue_t : public std::priority_queue, ue_ul_prio_compare> + { + using base_type = std::priority_queue, ue_ul_prio_compare>; + + public: + // Note: faster than while(!empty()) pop() because it avoids the O(NlogN). Faster than = {}, because it preserves + // memory. + void clear() + { + // Access to underlying vector. + this->c.clear(); + } + + // Adapter of the priority_queue push method to avoid adding candidates with skip priority level. + void push(ue_ctxt* elem) + { + if (elem->ul_retx_h == nullptr and elem->ul_newtx_h == nullptr) { + return; + } + base_type::push(elem); + } + }; + + /// Priority queue of UEs to be scheduled in DL. The UE in front of the queue has highest priority and vice versa. + ue_dl_queue_t dl_queue; + /// Priority queue of UEs to be scheduled in UL. The UE in front of the queue has highest priority and vice versa. + ue_ul_queue_t ul_queue; +}; + +} // namespace srsran diff --git a/lib/scheduler/policy/scheduler_time_rr.cpp b/lib/scheduler/policy/scheduler_time_rr.cpp index 75eb3aa5e0..227aca49fa 100644 --- a/lib/scheduler/policy/scheduler_time_rr.cpp +++ b/lib/scheduler/policy/scheduler_time_rr.cpp @@ -248,14 +248,14 @@ du_ue_index_t round_robin_apply(const ue_repository& ue_db, du_ue_index_t next_u // wrap-around it = ue_db.begin(); } - const ue& u = **it; - const alloc_outcome alloc_result = alloc_ue(u); - if (alloc_result == alloc_outcome::skip_slot) { + const ue& u = **it; + const alloc_result result = alloc_ue(u); + if (result.status == alloc_status::skip_slot) { // Grid allocator directed policy to stop allocations for this slot. break; } - if (alloc_result == alloc_outcome::success and first_alloc) { + if (result.status == alloc_status::success and first_alloc) { // Mark the next UE to be the first UE to get allocated in the following slot. // It is important that we equally distribute the opportunity to be the first UE being allocated in a slot for // all UEs. Otherwise, we could end up in a situation, where a UE is always the last one to be allocated and @@ -268,22 +268,22 @@ du_ue_index_t round_robin_apply(const ue_repository& ue_db, du_ue_index_t next_u } /// Allocate UE PDSCH grant. -static alloc_outcome alloc_dl_ue(const ue& u, - const ue_resource_grid_view& res_grid, - ue_pdsch_allocator& pdsch_alloc, - bool is_retx, - bool ue_with_srb_data_only, - srslog::basic_logger& logger, - const scheduler_ue_expert_config& ue_expert_cfg, - std::optional dl_new_tx_max_nof_rbs_per_ue_per_slot = {}) +static alloc_result alloc_dl_ue(const ue& u, + const ue_resource_grid_view& res_grid, + ue_pdsch_allocator& pdsch_alloc, + bool is_retx, + bool ue_with_srb_data_only, + srslog::basic_logger& logger, + const scheduler_ue_expert_config& ue_expert_cfg, + std::optional dl_new_tx_max_nof_rbs_per_ue_per_slot = {}) { if (not is_retx) { if (not u.has_pending_dl_newtx_bytes()) { - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } if (ue_with_srb_data_only and not u.has_pending_dl_newtx_bytes(LCID_SRB1) and not u.has_pending_dl_newtx_bytes(LCID_SRB2)) { - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } } @@ -293,13 +293,13 @@ static alloc_outcome alloc_dl_ue(const ue& u, if (ue_cc.is_in_fallback_mode()) { // Skip allocation for UEs in fallback mode, as it is handled by the SRB fallback scheduler. - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } // UE is already allocated in the PDCCH for this slot (e.g. we should skip a newTx if a reTx has already been // allocated for this UE). if (res_grid.has_ue_dl_pdcch(ue_cc.cell_index, u.crnti)) { - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } // Get DL HARQ candidates. @@ -316,33 +316,33 @@ static alloc_outcome alloc_dl_ue(const ue& u, grant.recommended_nof_bytes = u.pending_dl_newtx_bytes(); grant.max_nof_rbs = dl_new_tx_max_nof_rbs_per_ue_per_slot; } - const alloc_outcome result = pdsch_alloc.allocate_dl_grant(grant); + const alloc_result result = pdsch_alloc.allocate_dl_grant(grant); // If the allocation failed due to invalid parameters, we continue iteration. - if (result != alloc_outcome::invalid_params) { + if (result.status != alloc_status::invalid_params) { return result; } } } - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } /// Allocate UE PUSCH grant. -static alloc_outcome alloc_ul_ue(const ue& u, - const ue_resource_grid_view& res_grid, - ue_pusch_allocator& pusch_alloc, - bool is_retx, - bool schedule_sr_only, - srslog::basic_logger& logger, - std::optional ul_new_tx_max_nof_rbs_per_ue_per_slot = {}) +static alloc_result alloc_ul_ue(const ue& u, + const ue_resource_grid_view& res_grid, + ue_pusch_allocator& pusch_alloc, + bool is_retx, + bool schedule_sr_only, + srslog::basic_logger& logger, + std::optional ul_new_tx_max_nof_rbs_per_ue_per_slot = {}) { unsigned pending_newtx_bytes = 0; if (not is_retx) { if (schedule_sr_only and not u.has_pending_sr()) { - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } pending_newtx_bytes = u.pending_ul_newtx_bytes(); if (pending_newtx_bytes == 0) { - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } } @@ -351,7 +351,7 @@ static alloc_outcome alloc_ul_ue(const ue& u, const ue_cell& ue_cc = u.get_cell(to_ue_cell_index(i)); if (ue_cc.is_in_fallback_mode()) { // Skip allocation for UEs in fallback mode, as it is handled by the SRB fallback scheduler. - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } // Get UL HARQ candidates. @@ -368,14 +368,14 @@ static alloc_outcome alloc_ul_ue(const ue& u, grant.recommended_nof_bytes = u.pending_ul_newtx_bytes(); grant.max_nof_rbs = ul_new_tx_max_nof_rbs_per_ue_per_slot; } - const alloc_outcome result = pusch_alloc.allocate_ul_grant(grant); + const alloc_result result = pusch_alloc.allocate_ul_grant(grant); // If the allocation failed due to invalid parameters, we continue iteration. - if (result != alloc_outcome::invalid_params) { + if (result.status != alloc_status::invalid_params) { return result; } } } - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/lib/scheduler/policy/ue_allocator.h b/lib/scheduler/policy/ue_allocator.h index 16771f5216..ab117fdfaf 100644 --- a/lib/scheduler/policy/ue_allocator.h +++ b/lib/scheduler/policy/ue_allocator.h @@ -53,14 +53,21 @@ struct ue_pusch_grant { std::optional max_nof_rbs; }; -/// \brief Outcome of a UE grant allocation, and action for the scheduler policy to follow afterwards. +/// \brief Status of a UE grant allocation, and action for the scheduler policy to follow afterwards. /// -/// The current outcomes are: +/// The current status are: /// - success - the allocation was successful with the provided parameters. /// - skip_slot - failure to allocate and the scheduler policy should terminate the current slot processing. /// - skip_ue - failure to allocate and the scheduler policy should move on to the next candidate UE. /// - invalid_params - failure to allocate and the scheduler policy should try a different set of grant parameters. -enum class alloc_outcome { success, skip_slot, skip_ue, invalid_params }; +enum class alloc_status { success, skip_slot, skip_ue, invalid_params }; + +/// Allocation result of a UE grant allocation. +struct alloc_result { + alloc_status status; + /// Nof. of bytes allocated if allocation was successful. + unsigned alloc_bytes{0}; +}; /// Allocator of PDSCH grants for UEs. class ue_pdsch_allocator @@ -68,7 +75,7 @@ class ue_pdsch_allocator public: virtual ~ue_pdsch_allocator() = default; - virtual alloc_outcome allocate_dl_grant(const ue_pdsch_grant& grant) = 0; + virtual alloc_result allocate_dl_grant(const ue_pdsch_grant& grant) = 0; }; /// Allocator of PUSCH grants for UEs. @@ -77,7 +84,7 @@ class ue_pusch_allocator public: virtual ~ue_pusch_allocator() = default; - virtual alloc_outcome allocate_ul_grant(const ue_pusch_grant& grant) = 0; + virtual alloc_result allocate_ul_grant(const ue_pusch_grant& grant) = 0; }; } // namespace srsran diff --git a/lib/scheduler/slicing/slice_scheduler.cpp b/lib/scheduler/slicing/slice_scheduler.cpp index c803828812..f3eb5f9c3a 100644 --- a/lib/scheduler/slicing/slice_scheduler.cpp +++ b/lib/scheduler/slicing/slice_scheduler.cpp @@ -36,14 +36,12 @@ slice_scheduler::slice_scheduler(const cell_configuration& cell_cfg_) : ran_slice_id_t id_count{0}; // Default slice. slices.emplace_back(id_count, cell_cfg, slice_rrm_policy_config{}); - slices.back().inst.policy = - create_scheduler_strategy(scheduler_strategy_params{"time_rr", &logger}, cell_cfg.expert_cfg.ue); + slices.back().inst.policy = create_scheduler_strategy(cell_cfg.expert_cfg.ue); ++id_count; // Configured RRM policy members. for (const slice_rrm_policy_config& rrm : cell_cfg.rrm_policy_members) { slices.emplace_back(id_count, cell_cfg, rrm); - slices.back().inst.policy = - create_scheduler_strategy(scheduler_strategy_params{"time_rr", &logger}, cell_cfg.expert_cfg.ue); + slices.back().inst.policy = create_scheduler_strategy(cell_cfg.expert_cfg.ue); ++id_count; } } diff --git a/lib/scheduler/ue_scheduling/ue_cell.cpp b/lib/scheduler/ue_scheduling/ue_cell.cpp index 2a3a56124e..67450c51c8 100644 --- a/lib/scheduler/ue_scheduling/ue_cell.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell.cpp @@ -482,11 +482,9 @@ void ue_cell::apply_link_adaptation_procedures(const csi_report_data& csi_report } } -double -ue_cell::get_estimated_dl_brate_kbps(const pdsch_config_params& pdsch_cfg, sch_mcs_index mcs, unsigned nof_prbs) const +double ue_cell::get_estimated_dl_rate(const pdsch_config_params& pdsch_cfg, sch_mcs_index mcs, unsigned nof_prbs) const { - static const double slot_duration_ms = - SUBFRAME_DURATION_MSEC / get_nof_slots_per_subframe(cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.scs); + static constexpr unsigned NOF_BITS_PER_BYTE = 8U; const unsigned dmrs_prbs = calculate_nof_dmrs_per_rb(pdsch_cfg.dmrs); sch_mcs_description mcs_info = pdsch_mcs_get_config(pdsch_cfg.mcs_table, mcs); @@ -501,15 +499,13 @@ ue_cell::get_estimated_dl_brate_kbps(const pdsch_config_params& pdsch_cfg, sch_m .tb_scaling_field = pdsch_cfg.tb_scaling_field, .n_prb = nof_prbs}); - // Return the estimated throughput, considering that the number of bits is for a slot. - return tbs_bits / slot_duration_ms; + // Return the estimated throughput, considering that the number of bytes is for a slot. + return tbs_bits / NOF_BITS_PER_BYTE; } -double -ue_cell::get_estimated_ul_brate_kbps(const pusch_config_params& pusch_cfg, sch_mcs_index mcs, unsigned nof_prbs) const +double ue_cell::get_estimated_ul_rate(const pusch_config_params& pusch_cfg, sch_mcs_index mcs, unsigned nof_prbs) const { - static const double slot_duration_ms = - SUBFRAME_DURATION_MSEC / get_nof_slots_per_subframe(cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.scs); + static constexpr unsigned NOF_BITS_PER_BYTE = 8U; const unsigned dmrs_prbs = calculate_nof_dmrs_per_rb(pusch_cfg.dmrs); sch_mcs_description mcs_info = pusch_mcs_get_config(pusch_cfg.mcs_table, mcs, pusch_cfg.tp_pi2bpsk_present); @@ -524,6 +520,6 @@ ue_cell::get_estimated_ul_brate_kbps(const pusch_config_params& pusch_cfg, sch_m .tb_scaling_field = pusch_cfg.tb_scaling_field, .n_prb = nof_prbs}); - // Return the estimated throughput, considering that the number of bits is for a slot. - return tbs_bits / slot_duration_ms; + // Return the estimated throughput, considering that the number of bytes is for a slot. + return tbs_bits / NOF_BITS_PER_BYTE; } diff --git a/lib/scheduler/ue_scheduling/ue_cell.h b/lib/scheduler/ue_scheduling/ue_cell.h index 0cf8e256a3..10c8989bc3 100644 --- a/lib/scheduler/ue_scheduling/ue_cell.h +++ b/lib/scheduler/ue_scheduling/ue_cell.h @@ -147,10 +147,10 @@ class ue_cell const ue_link_adaptation_controller& link_adaptation_controller() const { return ue_mcs_calculator; } - /// \brief Returns an estimated DL bitrate in kbps (kilo bits per second) based on the given input parameters. - double get_estimated_dl_brate_kbps(const pdsch_config_params& pdsch_cfg, sch_mcs_index mcs, unsigned nof_prbs) const; - /// \brief Returns an estimated UL bitrate in kbps (kilo bits per second) based on the given input parameters. - double get_estimated_ul_brate_kbps(const pusch_config_params& pusch_cfg, sch_mcs_index mcs, unsigned nof_prbs) const; + /// \brief Returns an estimated DL rate in bytes per slot based on the given input parameters. + double get_estimated_dl_rate(const pdsch_config_params& pdsch_cfg, sch_mcs_index mcs, unsigned nof_prbs) const; + /// \brief Returns an estimated UL rate in bytes per slot based on the given input parameters. + double get_estimated_ul_rate(const pusch_config_params& pusch_cfg, sch_mcs_index mcs, unsigned nof_prbs) const; private: /// \brief Performs link adaptation procedures such as cancelling HARQs etc. diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index 911e22601c..4e29178504 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -71,7 +71,7 @@ void ue_cell_grid_allocator::slot_indication(slot_point sl) } } -alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& grant) +alloc_result ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& grant) { srsran_assert(ues.contains(grant.user->ue_index), "Invalid UE candidate index={}", grant.user->ue_index); srsran_assert(has_cell(grant.cell_index), "Invalid UE candidate cell_index={}", grant.cell_index); @@ -79,7 +79,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr if (dl_attempts_count++ >= expert_cfg.max_pdcch_alloc_attempts_per_slot) { logger.debug("Stopping DL allocations. Cause: Max number of DL PDCCH allocation attempts {} reached.", expert_cfg.max_pdcch_alloc_attempts_per_slot); - return alloc_outcome::skip_slot; + return {alloc_status::skip_slot}; } ue& u = ues[grant.user->ue_index]; @@ -90,7 +90,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr logger.warning("PDSCH allocation failed. Cause: The ue={} carrier with cell_index={} is inactive", u.ue_index, grant.cell_index); - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } const ue_cell_configuration& ue_cell_cfg = ue_cc->cfg(); @@ -106,7 +106,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr u.ue_index, u.crnti, pdcch_alloc.slot); - return alloc_outcome::skip_slot; + return {alloc_status::skip_slot}; } if (not ue_cc->is_active() and not is_retx) { @@ -116,12 +116,12 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr u.ue_index, u.crnti, grant.cell_index); - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } if (ue_cc->is_in_fallback_mode()) { // Skip allocation for UEs in fallback mode, as it is handled by the SRB fallback scheduler. - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } if (not is_retx and not grant.recommended_nof_bytes.has_value()) { @@ -130,7 +130,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr u.ue_index, u.crnti, grant.h_id); - return alloc_outcome::invalid_params; + return {alloc_status::invalid_params}; } // [Implementation-defined] @@ -145,10 +145,11 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr } // Create PDSCH param candidate search object. - ue_pdsch_alloc_param_candidate_searcher candidates{u, grant.cell_index, h_dl, pdcch_alloc.slot}; + ue_pdsch_alloc_param_candidate_searcher candidates{ + u, grant.cell_index, h_dl, pdcch_alloc.slot, slots_with_no_pdsch_space}; if (candidates.is_empty()) { // The conditions for a new PDSCH allocation for this UE were not met (e.g. lack of available SearchSpaces). - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } // Iterate through allocation parameter candidates. @@ -185,7 +186,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr u.ue_index, u.crnti, expert_cfg.max_pdschs_per_slot); - return alloc_outcome::skip_slot; + return {alloc_status::skip_slot}; } // Verify there is space in PDSCH and PDCCH result lists for new allocations. @@ -193,7 +194,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr logger.debug("ue={} rnti={}: Failed to allocate PDSCH. Cause: No space available in scheduler output list", u.ue_index, u.crnti); - return alloc_outcome::skip_slot; + return {alloc_status::skip_slot}; } if (not cell_cfg.is_dl_enabled(pdsch_alloc.slot)) { @@ -201,7 +202,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr u.ue_index, u.crnti, pdsch_alloc.slot); - return alloc_outcome::skip_slot; + return {alloc_status::skip_slot}; } // Apply RB allocation limits. @@ -209,7 +210,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr const unsigned end_rb = std::min(expert_cfg.pdsch_crb_limits.stop(), ss_info.dl_crb_lims.stop()); if (start_rb >= end_rb) { // Invalid RB allocation range. - return alloc_outcome::skip_slot; + return {alloc_status::skip_slot}; } const crb_interval dl_crb_lims = {start_rb, end_rb}; @@ -217,7 +218,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr pdsch_alloc.dl_res_grid.used_crbs(bwp_dl_cmn.generic_params.scs, dl_crb_lims, pdsch_td_cfg.symbols); if (used_crbs.all()) { slots_with_no_pdsch_space.push_back(pdsch_alloc.slot); - return alloc_outcome::skip_slot; + return {alloc_status::skip_slot}; } grant_prbs_mcs mcs_prbs = @@ -249,7 +250,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr if (mcs_prbs.n_prbs == 0) { logger.debug("ue={} rnti={} PDSCH allocation skipped. Cause: UE's CQI=0 ", u.ue_index, u.crnti); - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } crb_interval crbs = rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs, 0); @@ -258,7 +259,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr u.ue_index, u.crnti, pdsch_alloc.slot); - return alloc_outcome::skip_slot; + return {alloc_status::skip_slot}; } // In case of Retx, the #CRBs need to stay the same. @@ -269,7 +270,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr u.crnti, pdsch_alloc.slot, h_dl.id); - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } const aggregation_level aggr_lvl = @@ -283,7 +284,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr .alloc_dl_pdcch_ue(pdcch_alloc, u.crnti, ue_cell_cfg, ss_cfg.get_id(), aggr_lvl); if (pdcch == nullptr) { logger.info("ue={} rnti={}: Failed to allocate PDSCH. Cause: No space in PDCCH.", u.ue_index, u.crnti); - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } // Allocate UCI. UCI destination (i.e., PUCCH or PUSCH) depends on whether there exist a PUSCH grant for the UE. @@ -299,7 +300,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr } else { logger.debug("ue={} rnti={}: Failed to allocate PDSCH. Cause: UCI allocation failed.", u.ue_index, u.crnti); get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } // Fetch UL resource allocator. @@ -322,7 +323,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr u.crnti); get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); // TODO: Remove UCI allocated? - return alloc_outcome::invalid_params; + return {alloc_status::invalid_params}; } pdsch_config_params pdsch_cfg; @@ -377,7 +378,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr logger.warning( "ue={} rnti={}: Failed to allocate PDSCH. Cause: no MCS such that code rate <= 0.95.", u.ue_index, u.crnti); get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } // Mark resources as occupied in the ResourceGrid. @@ -494,14 +495,14 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr msg.context.buffer_occupancy = u.pending_dl_newtx_bytes(); } - return alloc_outcome::success; + return {alloc_status::success, h_dl.last_alloc_params().tb[0]->tbs_bytes}; } // No candidates for PDSCH allocation. - return alloc_outcome::invalid_params; + return {alloc_status::invalid_params}; } -alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant) +alloc_result ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant) { srsran_assert(ues.contains(grant.user->ue_index), "Invalid UE candidate index={}", grant.user->ue_index); srsran_assert(has_cell(grant.cell_index), "Invalid UE candidate cell_index={}", grant.cell_index); @@ -510,7 +511,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr if (ul_attempts_count++ >= expert_cfg.max_pdcch_alloc_attempts_per_slot) { logger.debug("Stopping UL allocations. Cause: Max number of UL PDCCH allocation attempts {} reached.", expert_cfg.max_pdcch_alloc_attempts_per_slot); - return alloc_outcome::skip_slot; + return {alloc_status::skip_slot}; } ue& u = ues[grant.user->ue_index]; @@ -521,12 +522,12 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr logger.warning("PUSCH allocation failed. Cause: The ue={} carrier with cell_index={} is inactive", u.ue_index, grant.cell_index); - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } if (ue_cc->is_in_fallback_mode()) { // Skip allocation for UEs in fallback mode, as it is handled by the SRB fallback scheduler. - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } const ue_cell_configuration& ue_cell_cfg = ue_cc->cfg(); @@ -541,7 +542,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr u.ue_index, u.crnti, pdcch_alloc.slot); - return alloc_outcome::skip_slot; + return {alloc_status::skip_slot}; } // Verify there is space in PDCCH result lists for new allocations. @@ -551,7 +552,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr u.ue_index, u.crnti, pdcch_alloc.result.dl.ul_pdcchs.capacity()); - return alloc_outcome::skip_slot; + return {alloc_status::skip_slot}; } if (not ue_cc->is_active() and not is_retx) { @@ -560,7 +561,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr u.ue_index, u.crnti, grant.cell_index); - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } if (not is_retx and not grant.recommended_nof_bytes.has_value()) { @@ -569,7 +570,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr u.ue_index, u.crnti, grant.h_id); - return alloc_outcome::invalid_params; + return {alloc_status::invalid_params}; } // [Implementation-defined] @@ -585,10 +586,11 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr } // Create PUSCH param candidate search object. - ue_pusch_alloc_param_candidate_searcher candidates{u, grant.cell_index, h_ul, pdcch_alloc.slot}; + ue_pusch_alloc_param_candidate_searcher candidates{ + u, grant.cell_index, h_ul, pdcch_alloc.slot, slots_with_no_pusch_space}; if (candidates.is_empty()) { // The conditions for a new PUSCH allocation for this UE were not met (e.g. lack of available SearchSpaces). - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } // Iterate through allocation parameter candidates. @@ -750,7 +752,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr u.ue_index, u.crnti, pusch_alloc.slot); - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } crb_interval crbs = rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs, 0); @@ -796,7 +798,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr .alloc_ul_pdcch_ue(pdcch_alloc, u.crnti, ue_cell_cfg, ss_cfg.get_id(), aggr_lvl); if (pdcch == nullptr) { logger.info("ue={} rnti={}: Failed to allocate PUSCH. Cause: No space in PDCCH.", u.ue_index, u.crnti); - return alloc_outcome::skip_ue; + return {alloc_status::skip_ue}; } const unsigned nof_harq_ack_bits = @@ -867,7 +869,7 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr .ul_config->init_ul_bwp.pusch_cfg->pusch_mapping_type_a_dmrs.value() .additional_positions)); get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); - return alloc_outcome::invalid_params; + return {alloc_status::invalid_params}; } // Mark resources as occupied in the ResourceGrid. @@ -1003,9 +1005,9 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr // In case there is a SR pending. Reset it. u.reset_sr_indication(); - return alloc_outcome::success; + return {alloc_status::success, h_ul.last_tx_params().tbs_bytes}; } // No candidates for PUSCH allocation. - return alloc_outcome::invalid_params; + return {alloc_status::invalid_params}; } diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h index 1128a59189..c143320762 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h @@ -50,9 +50,9 @@ class ue_cell_grid_allocator : public ue_pdsch_allocator, public ue_pusch_alloca void slot_indication(slot_point sl); - alloc_outcome allocate_dl_grant(const ue_pdsch_grant& grant) override; + alloc_result allocate_dl_grant(const ue_pdsch_grant& grant) override; - alloc_outcome allocate_ul_grant(const ue_pusch_grant& grant) override; + alloc_result allocate_ul_grant(const ue_pusch_grant& grant) override; private: struct cell_t { diff --git a/lib/scheduler/ue_scheduling/ue_pdsch_alloc_param_candidate_searcher.h b/lib/scheduler/ue_scheduling/ue_pdsch_alloc_param_candidate_searcher.h index ee1eb0e853..1888c9a19e 100644 --- a/lib/scheduler/ue_scheduling/ue_pdsch_alloc_param_candidate_searcher.h +++ b/lib/scheduler/ue_scheduling/ue_pdsch_alloc_param_candidate_searcher.h @@ -161,12 +161,14 @@ class ue_pdsch_alloc_param_candidate_searcher }; /// Create a searcher for UE PDSCH parameters. - ue_pdsch_alloc_param_candidate_searcher(const ue& ue_ref_, - du_cell_index_t cell_index, - dl_harq_process& dl_harq_, - slot_point pdcch_slot_) : + ue_pdsch_alloc_param_candidate_searcher(const ue& ue_ref_, + du_cell_index_t cell_index, + dl_harq_process& dl_harq_, + slot_point pdcch_slot_, + span slots_with_no_pdsch_space_) : ue_ref(ue_ref_), ue_cc(ue_ref.find_cell(cell_index)), + slots_with_no_pdsch_space(slots_with_no_pdsch_space_), dl_harq(dl_harq_), is_retx(not dl_harq.empty()), pdcch_slot(pdcch_slot_) @@ -189,7 +191,7 @@ class ue_pdsch_alloc_param_candidate_searcher iterator end() { return iterator{*this, ss_candidate_list.end(), 0}; } /// Returns whether there are candidates or not. - bool is_empty() { return ss_candidate_list.empty(); } + bool is_empty() { return ss_candidate_list.empty() or begin() == end(); } private: // Generate Search Space candidates for a given HARQ. @@ -237,14 +239,15 @@ class ue_pdsch_alloc_param_candidate_searcher return false; } + const slot_point pdsch_slot = pdcch_slot + current.pdsch_td_res().k0; + // Check whether PDSCH slot is DL enabled. - if (not ue_cc->cfg().cell_cfg_common.is_dl_enabled(pdcch_slot + current.pdsch_td_res().k0)) { + if (not ue_cc->cfg().cell_cfg_common.is_dl_enabled(pdsch_slot)) { return false; } // Check whether PDSCH time domain resource fits in DL symbols of the slot. - if (ue_cc->cfg().cell_cfg_common.get_nof_dl_symbol_per_slot(pdcch_slot + current.pdsch_td_res().k0) < - current.pdsch_td_res().symbols.stop()) { + if (ue_cc->cfg().cell_cfg_common.get_nof_dl_symbol_per_slot(pdsch_slot) < current.pdsch_td_res().symbols.stop()) { return false; } @@ -254,6 +257,12 @@ class ue_pdsch_alloc_param_candidate_searcher return false; } + // Check whether there is any space left in PDSCH slot for allocation. + if (slots_with_no_pdsch_space.end() != + std::find(slots_with_no_pdsch_space.begin(), slots_with_no_pdsch_space.end(), pdsch_slot)) { + return false; + } + // If it is a retx, we need to ensure we use a time_domain_resource with the same number of symbols as used for // the first transmission. if (is_retx and current.pdsch_td_res().symbols.length() != dl_harq.last_alloc_params().nof_symbols) { @@ -290,6 +299,9 @@ class ue_pdsch_alloc_param_candidate_searcher // UE cell being allocated. const ue_cell* ue_cc; + // Slots with no RBs left for PDSCH allocation. + span slots_with_no_pdsch_space; + // DL HARQ considered for allocation. const dl_harq_process& dl_harq; // Whether the current search is for a newTx or a reTx. diff --git a/lib/scheduler/ue_scheduling/ue_pusch_alloc_param_candidate_searcher.h b/lib/scheduler/ue_scheduling/ue_pusch_alloc_param_candidate_searcher.h index f210be52f2..27b6c70847 100644 --- a/lib/scheduler/ue_scheduling/ue_pusch_alloc_param_candidate_searcher.h +++ b/lib/scheduler/ue_scheduling/ue_pusch_alloc_param_candidate_searcher.h @@ -165,12 +165,14 @@ class ue_pusch_alloc_param_candidate_searcher }; /// Create a searcher for UE PUSCH parameters. - ue_pusch_alloc_param_candidate_searcher(const ue& ue_ref_, - du_cell_index_t cell_index, - ul_harq_process& ul_harq_, - slot_point pdcch_slot_) : + ue_pusch_alloc_param_candidate_searcher(const ue& ue_ref_, + du_cell_index_t cell_index, + ul_harq_process& ul_harq_, + slot_point pdcch_slot_, + span slots_with_no_pusch_space_) : ue_ref(ue_ref_), ue_cc(ue_ref.find_cell(cell_index)), + slots_with_no_pusch_space(slots_with_no_pusch_space_), ul_harq(ul_harq_), is_retx(not ul_harq.empty()), pdcch_slot(pdcch_slot_) @@ -199,7 +201,7 @@ class ue_pusch_alloc_param_candidate_searcher iterator end() { return iterator{*this, ss_candidate_list.end(), 0}; } /// Returns whether there are candidates or not. - bool is_empty() { return ss_candidate_list.empty() or valid_ss_pusch_td_res_indices.empty(); } + bool is_empty() { return ss_candidate_list.empty() or valid_ss_pusch_td_res_indices.empty() or begin() == end(); } private: // Generate Search Space candidates for a given HARQ. @@ -247,17 +249,25 @@ class ue_pusch_alloc_param_candidate_searcher return false; } + const slot_point pusch_slot = pdcch_slot + current.pusch_td_res().k2; + // Check whether PUSCH slot is UL enabled. - if (not ue_cc->cfg().cell_cfg_common.is_ul_enabled(pdcch_slot + current.pusch_td_res().k2)) { + if (not ue_cc->cfg().cell_cfg_common.is_ul_enabled(pusch_slot)) { return false; } // Check whether PUSCH time domain resource fits in UL symbols of the slot. - if (ue_cc->cfg().cell_cfg_common.get_nof_ul_symbol_per_slot(pdcch_slot + current.pusch_td_res().k2) != + if (ue_cc->cfg().cell_cfg_common.get_nof_ul_symbol_per_slot(pusch_slot) != current.pusch_td_res().symbols.length()) { return false; } + // Check whether there is any space left in PUSCH slot for allocation. + if (slots_with_no_pusch_space.end() != + std::find(slots_with_no_pusch_space.begin(), slots_with_no_pusch_space.end(), pusch_slot)) { + return false; + } + // If it is a retx, we need to ensure we use a time_domain_resource with the same number of symbols as used for // the first transmission. if (is_retx and current.pusch_td_res().symbols.length() != ul_harq.last_tx_params().nof_symbols) { @@ -298,6 +308,9 @@ class ue_pusch_alloc_param_candidate_searcher // UE cell being allocated. const ue_cell* ue_cc; + // Slots with no RBs left for PUSCH allocation. + span slots_with_no_pusch_space; + // UL HARQ considered for allocation. const ul_harq_process& ul_harq; // Whether the current search is for a newTx or a reTx. diff --git a/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp b/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp index 6e9f43e0a9..be696bad7c 100644 --- a/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp +++ b/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp @@ -29,8 +29,7 @@ ue_scheduler_impl::ue_scheduler_impl(const scheduler_ue_expert_config& expert_cf sched_configuration_notifier& mac_notif, scheduler_metrics_handler& metric_handler) : expert_cfg(expert_cfg_), - sched_strategy(create_scheduler_strategy(scheduler_strategy_params{"time_rr", &srslog::fetch_basic_logger("SCHED")}, - expert_cfg)), + sched_strategy(create_scheduler_strategy(expert_cfg)), ue_alloc(expert_cfg, ue_db, srslog::fetch_basic_logger("SCHED")), event_mng(ue_db, metric_handler), logger(srslog::fetch_basic_logger("SCHED")) diff --git a/lib/srslog/CMakeLists.txt b/lib/srslog/CMakeLists.txt index b8eed9367b..e395a529bd 100644 --- a/lib/srslog/CMakeLists.txt +++ b/lib/srslog/CMakeLists.txt @@ -36,4 +36,4 @@ set(SOURCES add_library(srslog STATIC ${SOURCES}) target_link_libraries(srslog fmt ${CMAKE_THREAD_LIBS_INIT}) -install(TARGETS srslog EXPORT srsran_export) +add_to_exported_libs(srslog) diff --git a/lib/srsvec/CMakeLists.txt b/lib/srsvec/CMakeLists.txt index eb8d7021bc..d682ad18bb 100644 --- a/lib/srsvec/CMakeLists.txt +++ b/lib/srsvec/CMakeLists.txt @@ -39,5 +39,4 @@ set(SOURCES add_library(srsvec STATIC ${SOURCES}) target_link_libraries(srsvec srslog) -install(TARGETS srsvec - EXPORT srsran_export) +add_to_exported_libs(srsvec) diff --git a/lib/support/CMakeLists.txt b/lib/support/CMakeLists.txt index d5b840c64b..b973c6bc6c 100644 --- a/lib/support/CMakeLists.txt +++ b/lib/support/CMakeLists.txt @@ -52,5 +52,4 @@ if (Backward_FOUND AND BACKWARD_HAS_EXTERNAL_LIBRARIES) set_source_files_properties(backtrace.cpp APPEND PROPERTIES INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/external) endif (Backward_FOUND AND BACKWARD_HAS_EXTERNAL_LIBRARIES) -install(TARGETS srsran_support - EXPORT srsran_export) +add_to_exported_libs(srsran_support) diff --git a/lib/support/byte_buffer.cpp b/lib/support/byte_buffer.cpp index d2e357b2bb..0de1f2deab 100644 --- a/lib/support/byte_buffer.cpp +++ b/lib/support/byte_buffer.cpp @@ -127,7 +127,7 @@ expected byte_buffer::deep_copy() const byte_buffer buf; for (node_t* seg = ctrl_blk_ptr->segments.head; seg != nullptr; seg = seg->next) { if (not buf.append(span{seg->data(), seg->length()})) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } } return buf; @@ -585,7 +585,7 @@ expected srsran::make_byte_buffer(const std::string& hex_str) { if (hex_str.size() % 2 != 0) { // Failed to parse hex string. - return default_error_t{}; + return make_unexpected(default_error_t{}); } byte_buffer ret{byte_buffer::fallback_allocation_tag{}}; @@ -593,12 +593,12 @@ expected srsran::make_byte_buffer(const std::string& hex_str) uint8_t val; if (std::sscanf(hex_str.data() + i, "%02hhX", &val) <= 0) { // Failed to parse Hex digit. - return default_error_t{}; + return make_unexpected(default_error_t{}); } bool success = ret.append(val); if (not success) { // Note: This shouldn't generally happen as we use a fallback allocator. - return default_error_t{}; + return make_unexpected(default_error_t{}); } } return ret; diff --git a/lib/support/byte_buffer_chain.cpp b/lib/support/byte_buffer_chain.cpp index e0f2d9f8d9..8f95fcfa47 100644 --- a/lib/support/byte_buffer_chain.cpp +++ b/lib/support/byte_buffer_chain.cpp @@ -43,10 +43,10 @@ expected byte_buffer_chain::create() auto* mem_block = detail::get_default_byte_buffer_segment_pool().allocate_node(); if (mem_block == nullptr) { srslog::fetch_basic_logger("ALL").warning("POOL: Failed to allocate memory block for byte_buffer_chain"); - return default_error_t{}; + return make_unexpected(default_error_t{}); } - return {mem_block}; + return byte_buffer_chain{mem_block}; } void byte_buffer_chain::block_deleter::operator()(void* p) diff --git a/lib/support/network/CMakeLists.txt b/lib/support/network/CMakeLists.txt index 366ea27f5d..022048c743 100644 --- a/lib/support/network/CMakeLists.txt +++ b/lib/support/network/CMakeLists.txt @@ -27,5 +27,4 @@ add_library(srsran_network sockets.cpp) target_link_libraries(srsran_network srslog) -install(TARGETS srsran_network - EXPORT srsran_export) \ No newline at end of file +add_to_exported_libs(srsran_network) diff --git a/lib/support/network/sctp_socket.cpp b/lib/support/network/sctp_socket.cpp index 01fe185abd..6ddfa3d4ca 100644 --- a/lib/support/network/sctp_socket.cpp +++ b/lib/support/network/sctp_socket.cpp @@ -172,7 +172,7 @@ expected sctp_socket::create(const sctp_socket_params& params) sctp_socket socket; if (params.if_name.empty()) { socket.logger.error("Failed to create SCTP socket. Cause: No interface name was provided"); - return default_error_t{}; + return make_unexpected(default_error_t{}); } socket.if_name = params.if_name; socket.sock_fd = unique_fd{::socket(params.ai_family, params.ai_socktype, IPPROTO_SCTP)}; @@ -189,13 +189,13 @@ expected sctp_socket::create(const sctp_socket_params& params) socket.if_name, strerror(ret)); } - return default_error_t{}; + return make_unexpected(default_error_t{}); } socket.logger.debug("{}: SCTP socket created with fd={}", socket.if_name, socket.sock_fd.value()); if (not socket.set_sockopts(params)) { socket.close(); - return default_error_t{}; + return make_unexpected(default_error_t{}); } // Save non-blocking mode to apply after bind/connect. We do not yet support async bind/connect. diff --git a/tests/benchmarks/du_high/du_high_benchmark.cpp b/tests/benchmarks/du_high/du_high_benchmark.cpp index d3c88dee72..45ef1f02ae 100644 --- a/tests/benchmarks/du_high/du_high_benchmark.cpp +++ b/tests/benchmarks/du_high/du_high_benchmark.cpp @@ -85,12 +85,14 @@ struct bench_params { units::bytes pdu_size{1500}; /// \brief Logical cores used by the "du_cell" thread. std::vector du_cell_cores = {}; + /// \brief Policy scheduler type. + policy_scheduler_expert_config strategy_cfg = time_rr_scheduler_expert_config{}; }; static void usage(const char* prog, const bench_params& params) { fmt::print("Usage: {} [-R repetitions] [-U nof. ues] [-D Duplex mode] [-d DL bytes per slot] [-u UL BSR] [-r Max RBs " - "per UE DL grant] [-a CPU affinity] [-p F1-U PDU size]\n", + "per UE DL grant] [-a CPU affinity] [-p F1-U PDU size] [-P Policy scheduler type]\n", prog); fmt::print("\t-R Repetitions [Default {}]\n", params.nof_repetitions); fmt::print("\t-U Nof. DU UEs for each simulation (e.g. \"1,5,10\" would run three benchmarks with 1, 5 and 10 UEs) " @@ -105,7 +107,8 @@ static void usage(const char* prog, const bench_params& params) params.ul_bsr_bytes); fmt::print("\t-r Max RBs per UE DL grant per slot [Default 275]\n"); fmt::print("\t-a \"du_cell\" cores that the benchmark should use [Default \"no CPU affinity\"]\n"); - fmt::print("\t-p F1-U PDU size used [Default {}]", params.pdu_size); + fmt::print("\t-p F1-U PDU size used [Default {}]\n", params.pdu_size); + fmt::print("\t-P Policy scheduler the bechmark should use (\"time_rr\", \"time_pf\") [Default \"time_rr\"]\n"); fmt::print("\t-h Show this message\n"); } @@ -124,7 +127,7 @@ static std::vector tokenize(const std::string& s, Func&& func) static void parse_args(int argc, char** argv, bench_params& params) { int opt = 0; - while ((opt = getopt(argc, argv, "R:U:D:d:u:r:a:p:h")) != -1) { + while ((opt = getopt(argc, argv, "R:U:D:d:u:r:a:p:P:h")) != -1) { switch (opt) { case 'R': params.nof_repetitions = std::strtol(optarg, nullptr, 10); @@ -169,6 +172,16 @@ static void parse_args(int argc, char** argv, bench_params& params) case 'p': params.pdu_size = units::bytes{(unsigned)std::strtol(optarg, nullptr, 10)}; break; + case 'P': { + if (std::string(optarg) == "time_pf") { + params.strategy_cfg = time_pf_scheduler_expert_config{}; + } else if (std::string(optarg) == "time_rr") { + params.strategy_cfg = time_rr_scheduler_expert_config{}; + } else { + usage(argv[0], params); + exit(0); + } + } break; case 'h': default: usage(argv[0], params); @@ -203,6 +216,11 @@ static void print_args(const bench_params& params) fmt::print("- F1-U DL PDU size [bytes]: {}\n", params.pdu_size); fmt::print("- BSR size [bytes]: {}\n", params.ul_bsr_bytes); fmt::print("- Max DL RB grant size [RBs]: {}\n", params.max_dl_rb_grant); + if (std::holds_alternative(params.strategy_cfg)) { + fmt::print("- Policys scheduler: time_pf\n"); + } else { + fmt::print("- Policys scheduler: time_rr\n"); + } } class dummy_metrics_handler : public scheduler_metrics_notifier @@ -572,12 +590,13 @@ class du_high_bench static const unsigned DEFAULT_DL_PDU_SIZE = 1500; public: - du_high_bench(unsigned dl_buffer_state_bytes_, - unsigned ul_bsr_bytes_, - unsigned max_nof_rbs_per_dl_grant, - units::bytes f1u_pdu_size_, - span du_cell_cores, - const cell_config_builder_params& builder_params = {}) : + du_high_bench(unsigned dl_buffer_state_bytes_, + unsigned ul_bsr_bytes_, + unsigned max_nof_rbs_per_dl_grant, + units::bytes f1u_pdu_size_, + span du_cell_cores, + const policy_scheduler_expert_config& strategy_cfg, + const cell_config_builder_params& builder_params = {}) : params(builder_params), f1u_dl_pdu_bytes_per_slot(dl_buffer_state_bytes_), f1u_pdu_size(f1u_pdu_size_), @@ -608,6 +627,7 @@ class du_high_bench cfg.timers = &timers; cfg.cells = {config_helpers::make_default_du_cell_config(params)}; cfg.sched_cfg = config_helpers::make_default_scheduler_expert_config(); + cfg.sched_cfg.ue.strategy_cfg = strategy_cfg; cfg.sched_cfg.ue.pdsch_nof_rbs = {1, max_nof_rbs_per_dl_grant}; cfg.mac_cfg = mac_expert_config{.configs = {{10000, 10000, 10000}}}; cfg.qos = config_helpers::make_default_du_qos_config_list(/* warn_on_drop */ true, 1000); @@ -652,7 +672,8 @@ class du_high_bench ~du_high_bench() { stop(); } - static rnti_t du_ue_index_to_rnti(du_ue_index_t ue_idx) { return to_rnti(0x4601 + ue_idx); } + static rnti_t du_ue_index_to_rnti(du_ue_index_t ue_idx) { return to_rnti(0x4601 + ue_idx); } + static du_ue_index_t rnti_to_du_ue_index(rnti_t rnti) { return to_du_ue_index(static_cast(rnti) - 0x4601); } /// \brief Run a slot indication until completion. void run_slot() @@ -816,7 +837,7 @@ class du_high_bench // We perform a deep-copy of the byte buffer to better simulate a real deployment, where there is stress over // the byte buffer pool. auto pdu_copy = pdcp_pdu.deep_copy(); - if (pdu_copy.is_error()) { + if (not pdu_copy.has_value()) { test_logger.warning("Byte buffer segment pool depleted"); return; } @@ -940,30 +961,33 @@ class du_high_bench return; } - // Prepare MAC SDU for LCID 4. - static const lcid_t drb_lcid = uint_to_lcid(4); - mac_rx_pdu rx_pdu{pusch.pusch_cfg.rnti, 0, pusch.pusch_cfg.harq_id, {}}; - // Pack header and payload length. - // Subtract BSR length. - payload_len -= mac_header_size + bsr_mac_subpdu.length(); - if (payload_len > 255) { - report_fatal_error_if_not(rx_pdu.pdu.append(0x40 | drb_lcid), "Failed to allocate PDU"); - report_fatal_error_if_not(rx_pdu.pdu.append((payload_len & 0xff00) >> 8), "Failed to allocate PDU"); - report_fatal_error_if_not(rx_pdu.pdu.append(payload_len & 0x00ff), "Failed to allocate PDU"); - } else { - report_fatal_error_if_not(rx_pdu.pdu.append(drb_lcid), "Failed to allocate PDU"); - report_fatal_error_if_not(rx_pdu.pdu.append(payload_len & 0x00ff), "Failed to allocate PDU"); + // Encode MAC SDU for LCID 4 only if UE Context Modification Response has arrived to CU and LCID 4 is configured. + if (sim_cu_cp.ue_created_flag_list[rnti_to_du_ue_index(pusch.pusch_cfg.rnti)]) { + // Prepare MAC SDU for LCID 4. + static const lcid_t drb_lcid = uint_to_lcid(4); + mac_rx_pdu rx_pdu{pusch.pusch_cfg.rnti, 0, pusch.pusch_cfg.harq_id, {}}; + // Pack header and payload length. + // Subtract BSR length. + payload_len -= mac_header_size + bsr_mac_subpdu.length(); + if (payload_len > 255) { + report_fatal_error_if_not(rx_pdu.pdu.append(0x40 | drb_lcid), "Failed to allocate PDU"); + report_fatal_error_if_not(rx_pdu.pdu.append((payload_len & 0xff00) >> 8), "Failed to allocate PDU"); + report_fatal_error_if_not(rx_pdu.pdu.append(payload_len & 0x00ff), "Failed to allocate PDU"); + } else { + report_fatal_error_if_not(rx_pdu.pdu.append(drb_lcid), "Failed to allocate PDU"); + report_fatal_error_if_not(rx_pdu.pdu.append(payload_len & 0x00ff), "Failed to allocate PDU"); + } + static const uint8_t rlc_um_complete_pdu_header = 0x00; + report_fatal_error_if_not(rx_pdu.pdu.append(rlc_um_complete_pdu_header), "Failed to allocate PDU"); + // Exclude RLC header from payload length. + report_fatal_error_if_not(rx_pdu.pdu.append(mac_pdu.begin(), mac_pdu.begin() + (payload_len - 1)), + "Failed to allocate PDU"); + // Append Long BSR bytes. + report_fatal_error_if_not(rx_pdu.pdu.append(bsr_mac_subpdu.begin(), bsr_mac_subpdu.end()), + "Failed to allocate PDU"); + + rx_ind.pdus.push_back(rx_pdu); } - static const uint8_t rlc_um_complete_pdu_header = 0x00; - report_fatal_error_if_not(rx_pdu.pdu.append(rlc_um_complete_pdu_header), "Failed to allocate PDU"); - // Exclude RLC header from payload length. - report_fatal_error_if_not(rx_pdu.pdu.append(mac_pdu.begin(), mac_pdu.begin() + (payload_len - 1)), - "Failed to allocate PDU"); - // Append Long BSR bytes. - report_fatal_error_if_not(rx_pdu.pdu.append(bsr_mac_subpdu.begin(), bsr_mac_subpdu.end()), - "Failed to allocate PDU"); - - rx_ind.pdus.push_back(rx_pdu); // Remove PUCCH UCI if UCI mltplxd on PUSCH. if (pusch.uci.has_value()) { @@ -1132,14 +1156,15 @@ static cell_config_builder_params generate_custom_cell_config_builder_params(dup } /// \brief Benchmark DU-high with DL and/or UL only traffic using an RLC UM bearer. -void benchmark_dl_ul_only_rlc_um(benchmarker& bm, - unsigned nof_ues, - duplex_mode dplx_mode, - unsigned dl_buffer_state_bytes, - unsigned ul_bsr_bytes, - unsigned max_nof_rbs_per_dl_grant, - units::bytes dl_pdu_size, - span du_cell_cores) +void benchmark_dl_ul_only_rlc_um(benchmarker& bm, + unsigned nof_ues, + duplex_mode dplx_mode, + unsigned dl_buffer_state_bytes, + unsigned ul_bsr_bytes, + unsigned max_nof_rbs_per_dl_grant, + units::bytes dl_pdu_size, + span du_cell_cores, + const policy_scheduler_expert_config& strategy_cfg) { auto benchname = fmt::format("{}{}{}, {} UEs, RLC UM", dl_buffer_state_bytes > 0 ? "DL" : "", @@ -1152,6 +1177,7 @@ void benchmark_dl_ul_only_rlc_um(benchmarker& bm, max_nof_rbs_per_dl_grant, dl_pdu_size, du_cell_cores, + strategy_cfg, generate_custom_cell_config_builder_params(dplx_mode)}; for (unsigned ue_count = 0; ue_count < nof_ues; ++ue_count) { bench.add_ue(to_du_ue_index(ue_count)); @@ -1272,7 +1298,8 @@ int main(int argc, char** argv) params.ul_bsr_bytes, params.max_dl_rb_grant, params.pdu_size, - params.du_cell_cores); + params.du_cell_cores, + params.strategy_cfg); } if (not tracing_filename.empty()) { diff --git a/tests/benchmarks/pdcp/pdcp_rx_benchmark.cpp b/tests/benchmarks/pdcp/pdcp_rx_benchmark.cpp index 5db1c02019..93ac5d1c42 100644 --- a/tests/benchmarks/pdcp/pdcp_rx_benchmark.cpp +++ b/tests/benchmarks/pdcp/pdcp_rx_benchmark.cpp @@ -44,8 +44,11 @@ class pdcp_tx_gen_frame : public pdcp_tx_lower_notifier, public pdcp_tx_upper_co void on_protocol_failure() final {} /// PDCP TX lower layer data notifier - void on_new_pdu(byte_buffer pdu) final { pdu_list.push_back(byte_buffer_chain::create(std::move(pdu)).value()); } - void on_discard_pdu(uint32_t pdcp_sn) final {} + void on_new_pdu(byte_buffer pdu, bool is_retx) final + { + pdu_list.push_back(byte_buffer_chain::create(std::move(pdu)).value()); + } + void on_discard_pdu(uint32_t pdcp_sn) final {} std::vector pdu_list; }; @@ -73,9 +76,9 @@ struct bench_params { }; struct app_params { - int algo = -1; - std::string log_level = "error"; - std::string log_filename = "stdout"; + int algo = -1; + srslog::basic_levels log_level = srslog::basic_levels::error; + std::string log_filename = "stdout"; }; } // namespace @@ -104,9 +107,11 @@ static void parse_args(int argc, char** argv, bench_params& params, app_params& case 't': params.print_timing_info = true; break; - case 'l': - app.log_level = std::string(optarg); + case 'l': { + auto value = srslog::str_to_basic_level(std::string(optarg)); + app.log_level = value.has_value() ? value.value() : srslog::basic_levels::none; break; + } case 'f': app.log_filename = std::string(optarg); break; @@ -289,7 +294,7 @@ int main(int argc, char** argv) return -1; } srslog::set_default_sink(*log_sink); - srslog::fetch_basic_logger("PDCP").set_level(srslog::str_to_basic_level(app_params.log_level)); + srslog::fetch_basic_logger("PDCP").set_level(app_params.log_level); if (app_params.algo != -1 && app_params.algo != 0 && app_params.algo != 1 && app_params.algo != 2 && app_params.algo != 3) { diff --git a/tests/benchmarks/pdcp/pdcp_tx_benchmark.cpp b/tests/benchmarks/pdcp/pdcp_tx_benchmark.cpp index 5ae2e5cf45..52a212aaf0 100644 --- a/tests/benchmarks/pdcp/pdcp_tx_benchmark.cpp +++ b/tests/benchmarks/pdcp/pdcp_tx_benchmark.cpp @@ -41,7 +41,7 @@ class pdcp_tx_gen_frame : public pdcp_tx_lower_notifier, public pdcp_tx_upper_co void on_protocol_failure() final {} /// PDCP TX lower layer data notifier - void on_new_pdu(byte_buffer pdu) final {} + void on_new_pdu(byte_buffer pdu, bool is_retx) final {} void on_discard_pdu(uint32_t pdcp_sn) final {} }; @@ -51,9 +51,9 @@ struct bench_params { }; struct app_params { - int algo = -1; - std::string log_level = "error"; - std::string log_filename = "stdout"; + int algo = -1; + srslog::basic_levels log_level = srslog::basic_levels::error; + std::string log_filename = "stdout"; }; static void usage(const char* prog, const bench_params& params, const app_params& app) @@ -81,9 +81,11 @@ static void parse_args(int argc, char** argv, bench_params& params, app_params& case 't': params.print_timing_info = true; break; - case 'l': - app.log_level = std::string(optarg); + case 'l': { + auto value = srslog::str_to_basic_level(std::string(optarg)); + app.log_level = value.has_value() ? value.value() : srslog::basic_levels::none; break; + } case 'f': app.log_filename = std::string(optarg); break; @@ -215,7 +217,7 @@ int main(int argc, char** argv) return -1; } srslog::set_default_sink(*log_sink); - srslog::fetch_basic_logger("PDCP").set_level(srslog::str_to_basic_level(app_params.log_level)); + srslog::fetch_basic_logger("PDCP").set_level(app_params.log_level); if (app_params.algo != -1) { run_benchmark(params, app_params.algo); diff --git a/tests/benchmarks/phy/upper/channel_processors/pdsch_encoder_hwacc_benchmark.cpp b/tests/benchmarks/phy/upper/channel_processors/pdsch_encoder_hwacc_benchmark.cpp index 7378c8c809..df95832027 100644 --- a/tests/benchmarks/phy/upper/channel_processors/pdsch_encoder_hwacc_benchmark.cpp +++ b/tests/benchmarks/phy/upper/channel_processors/pdsch_encoder_hwacc_benchmark.cpp @@ -56,11 +56,11 @@ static bounded_bitset dmrs_symbol_mask = {false, false, true, false, false, false, false, false, false, false, false, false, false, false}; #ifdef DPDK_FOUND -static bool dedicated_queue = true; -static bool cb_mode = false; -static std::string hal_log_level = "ERROR"; -static bool std_out_sink = true; -static std::string eal_arguments = ""; +static bool dedicated_queue = true; +static bool cb_mode = false; +static srslog::basic_levels hal_log_level = srslog::basic_levels::error; +static bool std_out_sink = true; +static std::string eal_arguments = ""; #endif // DPDK_FOUND // Test profile structure, initialized with default profile values. @@ -147,9 +147,11 @@ static int parse_args(int argc, char** argv) case 'y': std_out_sink = false; break; - case 'z': - hal_log_level = std::string(optarg); + case 'z': { + auto level = srslog::str_to_basic_level(std::string(optarg)); + hal_log_level = level.has_value() ? level.value() : srslog::basic_levels::error; break; + } #endif // DPDK_FOUND case 'h': default: @@ -189,7 +191,7 @@ static std::shared_ptr create_hw_accelera srslog::set_default_sink(*log_sink); srslog::init(); srslog::basic_logger& logger = srslog::fetch_basic_logger("HAL", false); - logger.set_level(srslog::str_to_basic_level(hal_log_level)); + logger.set_level(hal_log_level); // Pointer to a dpdk-based hardware-accelerator interface. static std::unique_ptr dpdk_interface = nullptr; 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 53ea761e9c..5ebf88bea3 100644 --- a/tests/benchmarks/phy/upper/channel_processors/pdsch_processor_benchmark.cpp +++ b/tests/benchmarks/phy/upper/channel_processors/pdsch_processor_benchmark.cpp @@ -109,11 +109,11 @@ static unsigned pending_count = 0; static unsigned finish_count = 0; #ifdef HWACC_PDSCH_ENABLED -static bool dedicated_queue = true; -static bool cb_mode = false; -static bool std_out_sink = true; -static std::string hal_log_level = "error"; -static std::string eal_arguments = ""; +static bool dedicated_queue = true; +static bool cb_mode = false; +static bool std_out_sink = true; +static srslog::basic_levels hal_log_level = srslog::basic_levels::error; +static std::string eal_arguments = ""; #endif // HWACC_PDSCH_ENABLED // Test profile structure, initialized with default profile values. @@ -385,9 +385,11 @@ static int parse_args(int argc, char** argv) case 'y': std_out_sink = false; break; - case 'z': - hal_log_level = std::string(optarg); + case 'z': { + auto level = srslog::str_to_basic_level(std::string(optarg)); + hal_log_level = level.has_value() ? level.value() : srslog::basic_levels::error; break; + } #endif // HWACC_PDSCH_ENABLED case 'h': default: @@ -503,7 +505,7 @@ static std::shared_ptr create_hw_accelera #ifdef HWACC_PDSCH_ENABLED // Intefacing to the bbdev-based hardware-accelerator. srslog::basic_logger& logger = srslog::fetch_basic_logger("HWACC", false); - logger.set_level(srslog::str_to_basic_level(hal_log_level)); + logger.set_level(hal_log_level); dpdk::bbdev_acc_configuration bbdev_config; bbdev_config.id = 0; bbdev_config.nof_ldpc_enc_lcores = nof_threads; @@ -619,6 +621,12 @@ static pdsch_processor_factory& get_processor_factory() dmrs_pdsch_gen_factory); } + // Create synchronous PDSCH processor pool if the processor is synchronous. + if (pdsch_proc_factory) { + // Only valid for generic and lite. + pdsch_proc_factory = create_pdsch_processor_pool(std::move(pdsch_proc_factory), nof_threads); + } + // Create concurrent PDSCH processor. // Note that currently hardware-acceleration is limited to "generic" processor types. if ((pdsch_processor_type.find("concurrent") != std::string::npos) && ldpc_encoder_type != "acc100") { @@ -640,11 +648,11 @@ static pdsch_processor_factory& get_processor_factory() dmrs_pdsch_gen_factory, *executor, nof_pdsch_processor_concurrent_threads); - } - TESTASSERT(pdsch_proc_factory); - // Create PDSCH processor pool. - pdsch_proc_factory = create_pdsch_processor_pool(std::move(pdsch_proc_factory), nof_threads, true); + // Create asynchronous PDSCH processor pool. + pdsch_proc_factory = create_pdsch_processor_asynchronous_pool(std::move(pdsch_proc_factory), nof_threads); + TESTASSERT(pdsch_proc_factory); + } TESTASSERT(pdsch_proc_factory); return *pdsch_proc_factory; @@ -750,7 +758,7 @@ int main(int argc, char** argv) srslog::set_default_sink(*log_sink); srslog::init(); srslog::basic_logger& logger = srslog::fetch_basic_logger("EAL", false); - logger.set_level(srslog::str_to_basic_level(hal_log_level)); + logger.set_level(hal_log_level); dpdk_interface = dpdk::create_dpdk_eal(eal_arguments, logger); TESTASSERT(dpdk_interface, "Failed to open DPDK EAL with arguments."); } diff --git a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_decoder_hwacc_benchmark.cpp b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_decoder_hwacc_benchmark.cpp index 7f5bff66e6..974a96e0d3 100644 --- a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_decoder_hwacc_benchmark.cpp +++ b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_decoder_hwacc_benchmark.cpp @@ -61,11 +61,11 @@ static bounded_bitset dmrs_symbol_mask = {false, false, true, false, false, false, false, false, false, false, false, false, false, false}; #ifdef DPDK_FOUND -static bool dedicated_queue = true; -static bool test_harq = false; -static std::string hal_log_level = "ERROR"; -static bool std_out_sink = true; -static std::string eal_arguments = ""; +static bool dedicated_queue = true; +static bool test_harq = false; +static srslog::basic_levels hal_log_level = srslog::basic_levels::error; +static bool std_out_sink = true; +static std::string eal_arguments = ""; #endif // DPDK_FOUND // Test profile structure, initialized with default profile values. @@ -160,9 +160,11 @@ static int parse_args(int argc, char** argv) case 'y': std_out_sink = false; break; - case 'z': - hal_log_level = std::string(optarg); + case 'z': { + auto level = srslog::str_to_basic_level(std::string(optarg)); + hal_log_level = level.has_value() ? level.value() : srslog::basic_levels::error; break; + } #endif // DPDK_FOUND case 'h': default: @@ -206,7 +208,7 @@ static std::shared_ptr create_hw_accelera srslog::set_default_sink(*log_sink); srslog::init(); srslog::basic_logger& logger = srslog::fetch_basic_logger("HAL", false); - logger.set_level(srslog::str_to_basic_level(hal_log_level)); + logger.set_level(hal_log_level); // Pointer to a dpdk-based hardware-accelerator interface. static std::unique_ptr dpdk_interface = nullptr; diff --git a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp index 88439af93e..a04ef1ed5a 100644 --- a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp +++ b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp @@ -136,11 +136,11 @@ static std::atomic pending_count = {0}; static std::atomic finish_count = {0}; #ifdef HWACC_PUSCH_ENABLED -static bool dedicated_queue = true; -static bool ext_softbuffer = true; -static bool std_out_sink = true; -static std::string hal_log_level = "error"; -static std::string eal_arguments = ""; +static bool dedicated_queue = true; +static bool ext_softbuffer = true; +static bool std_out_sink = true; +static srslog::basic_levels hal_log_level = srslog::basic_levels::error; +static std::string eal_arguments = ""; #endif // HWACC_PUSCH_ENABLED // Test profile structure, initialized with default profile values. @@ -324,9 +324,11 @@ static int parse_args(int argc, char** argv) case 'y': std_out_sink = false; break; - case 'z': - hal_log_level = std::string(optarg); + case 'z': { + auto level = srslog::str_to_basic_level(std::string(optarg)); + hal_log_level = level.has_value() ? level.value() : srslog::basic_levels::error; break; + } #endif // HWACC_PUSCH_ENABLED case 'h': default: @@ -442,7 +444,7 @@ static std::shared_ptr create_hw_accelera #ifdef HWACC_PUSCH_ENABLED // Intefacing to the bbdev-based hardware-accelerator. srslog::basic_logger& logger = srslog::fetch_basic_logger("HWACC", false); - logger.set_level(srslog::str_to_basic_level(hal_log_level)); + logger.set_level(hal_log_level); dpdk::bbdev_acc_configuration bbdev_config; bbdev_config.id = 0; bbdev_config.nof_ldpc_enc_lcores = 0; @@ -723,7 +725,7 @@ int main(int argc, char** argv) srslog::set_default_sink(*log_sink); srslog::init(); srslog::basic_logger& logger = srslog::fetch_basic_logger("EAL", false); - logger.set_level(srslog::str_to_basic_level(hal_log_level)); + logger.set_level(hal_log_level); dpdk_interface = dpdk::create_dpdk_eal(eal_arguments, logger); TESTASSERT(dpdk_interface, "Failed to open DPDK EAL with arguments."); } diff --git a/tests/benchmarks/rlc/rlc_am_rx_benchmark.cpp b/tests/benchmarks/rlc/rlc_am_rx_benchmark.cpp index 1c85af92dd..461ff85b84 100644 --- a/tests/benchmarks/rlc/rlc_am_rx_benchmark.cpp +++ b/tests/benchmarks/rlc/rlc_am_rx_benchmark.cpp @@ -47,8 +47,9 @@ 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) override {} - void on_delivered_sdu(uint32_t max_deliv_pdcp_sn) override {} + void on_retransmitted_sdu(uint32_t max_retx_pdcp_sn) override {} + void on_delivered_retransmitted_sdu(uint32_t max_deliv_retx_pdcp_sn) override {} // rlc_tx_upper_layer_control_notifier interface void on_protocol_failure() override {} @@ -148,7 +149,7 @@ std::vector generate_pdus(bench_params params, rx_order order) std::unique_ptr rlc_tx = nullptr; auto& logger = srslog::fetch_basic_logger("RLC"); - logger.set_level(srslog::str_to_basic_level("warning")); + logger.set_level(srslog::basic_levels::warning); null_rlc_pcap pcap; @@ -177,14 +178,14 @@ std::vector generate_pdus(bench_params params, rx_order order) int pdu_size = params.pdu_size; for (int i = 0; i < num_sdus; i++) { byte_buffer sdu = test_helpers::create_pdcp_pdu(config.pdcp_sn_len, i, num_bytes, i); - rlc_tx->handle_sdu(std::move(sdu)); + rlc_tx->handle_sdu(std::move(sdu), false); while (rlc_tx->get_buffer_state() > 0) { std::vector pdu_buf; pdu_buf.resize(pdu_size); size_t pdu_len = rlc_tx->pull_pdu(pdu_buf); pdu_buf.resize(pdu_len); auto buf = byte_buffer::create(pdu_buf); - report_error_if_not(!buf.is_error(), "Failed to allocate byte_buffer"); + report_error_if_not(buf.has_value(), "Failed to allocate byte_buffer"); pdus.emplace_back(std::move(buf.value())); num_pdus++; } diff --git a/tests/benchmarks/rlc/rlc_handle_status_report.cpp b/tests/benchmarks/rlc/rlc_handle_status_report.cpp index 2be637a3bc..c4ee32bac2 100644 --- a/tests/benchmarks/rlc/rlc_handle_status_report.cpp +++ b/tests/benchmarks/rlc/rlc_handle_status_report.cpp @@ -46,8 +46,9 @@ 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) override {} - void on_delivered_sdu(uint32_t max_deliv_pdcp_sn) override {} + void on_retransmitted_sdu(uint32_t max_retx_pdcp_sn) override {} + void on_delivered_retransmitted_sdu(uint32_t max_deliv_retx_pdcp_sn) override {} // rlc_tx_upper_layer_control_notifier interface void on_protocol_failure() override {} @@ -117,7 +118,7 @@ void benchmark_status_pdu_handling(rlc_am_status_pdu status, const bench_params& std::unique_ptr rlc = nullptr; auto& logger = srslog::fetch_basic_logger("RLC"); - logger.set_level(srslog::str_to_basic_level("warning")); + logger.set_level(srslog::basic_levels::warning); null_rlc_pcap pcap; @@ -141,7 +142,7 @@ void benchmark_status_pdu_handling(rlc_am_status_pdu status, const bench_params& for (int i = 0; i < 2048; i++) { byte_buffer sdu = test_helpers::create_pdcp_pdu(config.pdcp_sn_len, i, 7, 0); - rlc->handle_sdu(std::move(sdu)); + rlc->handle_sdu(std::move(sdu), false); std::array pdu_buf; rlc->pull_pdu(pdu_buf); } diff --git a/tests/e2e/tests/handover.py b/tests/e2e/tests/handover.py index d0e6313471..3eb3b2ef43 100644 --- a/tests/e2e/tests/handover.py +++ b/tests/e2e/tests/handover.py @@ -214,6 +214,7 @@ def _handover_multi_ues( noise_spd=noise_spd, num_cells=2, cell_position_offset=cell_position_offset, + log_ip_level="debug", ) configure_artifacts( diff --git a/tests/e2e/tests/reestablishment.py b/tests/e2e/tests/reestablishment.py index 1c57001024..43be0459de 100644 --- a/tests/e2e/tests/reestablishment.py +++ b/tests/e2e/tests/reestablishment.py @@ -99,6 +99,7 @@ def test_zmq_reestablishment_sequentially( time_alignment_calibration=0, always_download_artifacts=True, noise_spd=noise_spd, + log_ip_level="debug", warning_as_errors=True, ): # Launch pings @@ -168,6 +169,7 @@ def test_zmq_reestablishment_sequentially_full_rate( time_alignment_calibration=0, always_download_artifacts=True, noise_spd=noise_spd, + log_ip_level="", warning_as_errors=True, ): # Launch iperf for all UEs @@ -234,6 +236,7 @@ def test_zmq_reestablishment_parallel( time_alignment_calibration=0, always_download_artifacts=True, noise_spd=noise_spd, + log_ip_level="debug", warning_as_errors=True, ) as ue_attach_info_dict: @@ -301,6 +304,7 @@ def test_zmq_reestablishment_parallel_full_rate( time_alignment_calibration=0, always_download_artifacts=True, noise_spd=noise_spd, + log_ip_level="", warning_as_errors=True, ) as ue_attach_info_dict: @@ -341,6 +345,7 @@ def _iterator_over_attached_ues( time_alignment_calibration: Union[int, str], always_download_artifacts: bool, noise_spd: int, + log_ip_level: str, warning_as_errors: bool = True, ) -> Generator[Tuple[Dict[UEStub, UEAttachedInfo], Dict[UEStub, UEAttachedInfo]], None, None]: @@ -359,6 +364,7 @@ def _iterator_over_attached_ues( time_alignment_calibration=time_alignment_calibration, always_download_artifacts=always_download_artifacts, noise_spd=noise_spd, + log_ip_level=log_ip_level, warning_as_errors=warning_as_errors, ) as ue_attach_info_dict: @@ -396,6 +402,7 @@ def _test_reestablishments( time_alignment_calibration: Union[int, str], always_download_artifacts: bool, noise_spd: int, + log_ip_level: str, warning_as_errors: bool = True, ) -> Generator[Dict[UEStub, UEAttachedInfo], None, None]: @@ -412,6 +419,7 @@ def _test_reestablishments( time_alignment_calibration=time_alignment_calibration, noise_spd=noise_spd, enable_qos_reestablishment=True, + log_ip_level=log_ip_level, ) configure_artifacts( diff --git a/tests/e2e/tests/test_mode.py b/tests/e2e/tests/test_mode.py index c0b4a0f332..2d9b8c4bbb 100644 --- a/tests/e2e/tests/test_mode.py +++ b/tests/e2e/tests/test_mode.py @@ -82,18 +82,26 @@ def test_ue( """ # Configuration - retina_data.test_config = { - "gnb": { - "parameters": { - "gnb_id": 1, - "log_level": "warning", - "pcap": False, + with tempfile.NamedTemporaryFile(mode="w+") as tmp_file: + tmp_file.write(" ") # Make it not empty to overwrite default one + tmp_file.flush() + + retina_data.test_config = { + "gnb": { + "parameters": { + "gnb_id": 1, + "log_level": "warning", + "pcap": False, + }, + "templates": { + "cu": str(Path(__file__).joinpath("../test_mode/config_ue.yml").resolve()), + "du": tmp_file.name, + "ru": tmp_file.name, + }, }, - "templates": {"cell": str(Path(__file__).joinpath("../test_mode/config_ue.yml").resolve())}, - }, - } - retina_manager.parse_configuration(retina_data.test_config) - retina_manager.push_all_config() + } + retina_manager.parse_configuration(retina_data.test_config) + retina_manager.push_all_config() configure_artifacts( retina_data=retina_data, @@ -209,7 +217,8 @@ def _test_ru( "pcap": False, }, "templates": { - "cell": str(Path(__file__).joinpath("../test_mode/config_ru.yml").resolve()), + "cu": str(Path(__file__).joinpath("../test_mode/config_ru.yml").resolve()), + "du": tmp_file.name, "ru": tmp_file.name, }, }, diff --git a/tests/e2e/tests/test_mode/config_ru.yml b/tests/e2e/tests/test_mode/config_ru.yml index 3380218b46..7c2a6a545d 100644 --- a/tests/e2e/tests/test_mode/config_ru.yml +++ b/tests/e2e/tests/test_mode/config_ru.yml @@ -18,7 +18,11 @@ # and at http://www.gnu.org/licenses/. # -ru_dummy: +cu_up: + warn_on_drop: False + +buffer_pool: + nof_segments: 1048576 cell_cfg: dl_arfcn: 381500 @@ -42,6 +46,8 @@ cell_cfg: cells: - pci: 1 +ru_dummy: + test_mode: test_ue: rnti: 0x1234 @@ -50,9 +56,6 @@ test_mode: ri: 4 nof_ues: 16 -buffer_pool: - nof_segments: 1048576 - expert_execution: affinities: low_priority_cpus: {{low_priority_cpus}} diff --git a/tests/e2e/tests/test_mode/config_ue.yml b/tests/e2e/tests/test_mode/config_ue.yml index b3df54b05b..9a579fe085 100644 --- a/tests/e2e/tests/test_mode/config_ue.yml +++ b/tests/e2e/tests/test_mode/config_ue.yml @@ -18,6 +18,9 @@ # and at http://www.gnu.org/licenses/. # +cu_up: + warn_on_drop: False + cu_cp: pdu_session_setup_timeout: 5 inactivity_timer: 3 diff --git a/tests/e2e/tests/validate_configuration.py b/tests/e2e/tests/validate_configuration.py index 4da0e770bc..0aed036fd3 100644 --- a/tests/e2e/tests/validate_configuration.py +++ b/tests/e2e/tests/validate_configuration.py @@ -25,13 +25,14 @@ import tempfile from pathlib import Path from pprint import pformat +from time import sleep from google.protobuf.empty_pb2 import Empty from pytest import mark, param from retina.client.manager import RetinaTestManager from retina.launcher.artifacts import RetinaTestData from retina.launcher.utils import configure_artifacts -from retina.protocol.base_pb2 import FiveGCDefinition, GNBDefinition, PLMN, StartInfo, UEDefinition +from retina.protocol.base_pb2 import FiveGCDefinition, PLMN, StartInfo, UEDefinition from retina.protocol.fivegc_pb2 import FiveGCStartInfo from retina.protocol.fivegc_pb2_grpc import FiveGCStub from retina.protocol.gnb_pb2 import GNBStartInfo @@ -100,7 +101,8 @@ def run_config( "gnb": { "templates": { "main": str(Path(__file__).joinpath(f"../../../../{config_file}").resolve()), - "cell": tmp_file.name, + "cu": tmp_file.name, + "du": tmp_file.name, "ru": tmp_file.name, } } @@ -117,7 +119,7 @@ def run_config( plmn = PLMN(mcc="001", mnc="01") - gnb_def: GNBDefinition = gnb.GetDefinition(Empty()) + gnb.GetDefinition(Empty()) fivegc_def: FiveGCDefinition = fivegc.GetDefinition(Empty()) fivegc.Start( @@ -135,13 +137,16 @@ def run_config( fivegc_definition=fivegc_def, start_info=StartInfo( timeout=timeout, - post_commands=( - f"amf --addr {fivegc_def.amf_ip} --bind_addr {gnb_def.zmq_ip} " - f"log --filename stdout {extra_config}" - ), + post_commands=(f"log --filename stdout {extra_config}"), ), ) ) logging.info("GNB started") + # Sleep here because the N300 requires more time (~1minute) for initialization. + # Otherwise we send SIGTERM to the gNB, it won't finish within 5s and will raise SIGKILL to itself, + # causing this test to fail. + logging.info("Waiting 60s...") + sleep(60) + stop(tuple(), gnb, fivegc, retina_data, log_search=False) diff --git a/tests/e2e/tests/viavi/config.yml b/tests/e2e/tests/viavi/config.yml index 14e8f4b6e4..1fc527099e 100644 --- a/tests/e2e/tests/viavi/config.yml +++ b/tests/e2e/tests/viavi/config.yml @@ -52,6 +52,6 @@ cell_cfg: nof_dl_slots: 7 nof_ul_slots: 2 -cu_up: +cu_up: gtpu_queue_size: 32768 - gtpu_reordering_timer: 20 \ No newline at end of file + gtpu_reordering_timer: 20 diff --git a/tests/e2e/tests/viavi/test_declaration.yml b/tests/e2e/tests/viavi/test_declaration.yml index 2d144ca058..ce6d15cc46 100644 --- a/tests/e2e/tests/viavi/test_declaration.yml +++ b/tests/e2e/tests/viavi/test_declaration.yml @@ -32,7 +32,7 @@ # warning_as_errors: treat warnings as errors campaign_filename: &campaign_filename "C:\\ci\\CI 4x4 ORAN-FH-complete.xml" -gnb_extra_commands: &gnb_extra_commands "log --mac_level=info" +gnb_extra_commands: &gnb_extra_commands "" expected_dl_bitrate_high: &expected_dl_bitrate_high 1400000000 expected_ul_bitrate_high: &expected_ul_bitrate_high 100000000 expected_dl_bitrate_low: &expected_dl_bitrate_low 14000 @@ -138,19 +138,19 @@ tests: fail_if_kos: false warning_as_errors: true - # - campaign_filename: *campaign_filename - # test_name: "32UE fading TCP bidirectional" - # test_timeout: *test_timeout - # gnb_extra_commands: *gnb_extra_commands - # id: "32UE fading TCP bidirectional" - # max_pdschs_per_slot: 1 - # max_puschs_per_slot: 1 - # enable_qos_viavi: false - # # test/fail criteria - # expected_dl_bitrate: *expected_dl_bitrate_low - # expected_ul_bitrate: *expected_ul_bitrate_low - # fail_if_kos: false - # warning_as_errors: false + - campaign_filename: *campaign_filename + test_name: "32UE fading TCP bidirectional" + test_timeout: *test_timeout + gnb_extra_commands: *gnb_extra_commands + id: "32UE fading TCP bidirectional" + max_pdschs_per_slot: 1 + max_puschs_per_slot: 1 + enable_qos_viavi: false + # test/fail criteria + expected_dl_bitrate: 1 + expected_ul_bitrate: 1 + fail_if_kos: false + warning_as_errors: false - campaign_filename: *campaign_filename test_name: "32UE ideal UDP attach-detach with traffic" @@ -211,7 +211,7 @@ tests: - campaign_filename: *campaign_filename test_name: "32UE ideal ping" test_timeout: *test_timeout - gnb_extra_commands: *gnb_extra_commands + gnb_extra_commands: "log --mac_level=info" id: "32UE ideal ping" max_pdschs_per_slot: 1 max_puschs_per_slot: 1 @@ -225,7 +225,7 @@ tests: - campaign_filename: *campaign_filename test_name: "32UE ideal ping with traffic" test_timeout: *test_timeout - gnb_extra_commands: *gnb_extra_commands + gnb_extra_commands: "log --mac_level=info" id: "32UE ideal ping with traffic" max_pdschs_per_slot: 1 max_puschs_per_slot: 1 diff --git a/tests/integrationtests/du_high/paging_test.cpp b/tests/integrationtests/du_high/paging_test.cpp index 6e2f1c5b44..cbc42a3d08 100644 --- a/tests/integrationtests/du_high/paging_test.cpp +++ b/tests/integrationtests/du_high/paging_test.cpp @@ -29,7 +29,7 @@ #include "srsran/asn1/f1ap/common.h" #include "srsran/asn1/f1ap/f1ap_pdu_contents.h" #include "srsran/f1ap/common/f1ap_message.h" -#include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/bcd_helper.h" #include "srsran/support/test_utils.h" using namespace srsran; @@ -59,8 +59,8 @@ f1ap_message generate_paging_message(uint64_t five_g_tmsi, const nr_cell_global_ // Add paging cell list. asn1::protocol_ie_single_container_s asn1_paging_cell_item_container; auto& asn1_paging_cell_item = asn1_paging_cell_item_container->paging_cell_item(); - asn1_paging_cell_item.nr_cgi.nr_cell_id.from_number(nr_cgi.nci); - asn1_paging_cell_item.nr_cgi.plmn_id.from_number(plmn_string_to_bcd(nr_cgi.plmn)); + asn1_paging_cell_item.nr_cgi.nr_cell_id.from_number(nr_cgi.nci.value()); + asn1_paging_cell_item.nr_cgi.plmn_id = nr_cgi.plmn_id.to_bytes(); paging->paging_cell_list.push_back(asn1_paging_cell_item_container); return msg; diff --git a/tests/integrationtests/du_high/test_utils/du_high_env_simulator.cpp b/tests/integrationtests/du_high/test_utils/du_high_env_simulator.cpp index 37a343a751..c0a1682d66 100644 --- a/tests/integrationtests/du_high/test_utils/du_high_env_simulator.cpp +++ b/tests/integrationtests/du_high/test_utils/du_high_env_simulator.cpp @@ -181,7 +181,7 @@ du_high_env_simulator::du_high_env_simulator(du_high_env_sim_params params) : for (unsigned i = 0; i < params.nof_cells; ++i) { builder_params.pci = (pci_t)i; cfg.cells.push_back(config_helpers::make_default_du_cell_config(builder_params)); - cfg.cells.back().nr_cgi.nci = i; + cfg.cells.back().nr_cgi.nci = nr_cell_identity::create(i).value(); } cfg.qos = config_helpers::make_default_du_qos_config_list(/* warn_on_drop */ true, 0); diff --git a/tests/integrationtests/du_high_cu/cu_du_test.cpp b/tests/integrationtests/du_high_cu/cu_du_test.cpp index d53a307e3d..3b324e4b60 100644 --- a/tests/integrationtests/du_high_cu/cu_du_test.cpp +++ b/tests/integrationtests/du_high_cu/cu_du_test.cpp @@ -66,7 +66,7 @@ class cu_du_test : public ::testing::Test cu_cfg.n2_gw = &*amf; cu_cfg.timers = &timers; cu_cfg.statistics_report_period = std::chrono::seconds(1); - cu_cfg.ue_config.max_nof_supported_ues = cu_cfg.max_nof_dus * srsran::srs_cu_cp::MAX_NOF_UES_PER_DU; + cu_cfg.ue_config.max_nof_supported_ues = cu_cfg.max_nof_ues; cu_cfg.rrc_config.gnb_id = {411, 22}; // create CU-CP. @@ -101,6 +101,12 @@ class cu_du_test : public ::testing::Test du_obj->start(); } + ~cu_du_test() override + { + // flush logger after each test + srslog::flush(); + } + public: du_high_worker_manager workers; timer_manager timers; 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 f87dbc2ddd..fdd411683e 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 @@ -107,7 +107,7 @@ du_high_cu_test_simulator::du_high_cu_test_simulator(const du_high_cu_cp_test_si cu_cfg.n2_gw = &n2_gw; cu_cfg.timers = &timers; cu_cfg.ngap_config.ran_node_name = "srsgnb01"; - cu_cfg.ngap_config.plmn = "00101"; + cu_cfg.ngap_config.plmn = plmn_identity::test_value(); cu_cfg.ngap_config.tac = 7; s_nssai_t slice_cfg; slice_cfg.sst = 1; diff --git a/tests/integrationtests/ngap/ngap_integration_test.cpp b/tests/integrationtests/ngap/ngap_integration_test.cpp index c6f2c542b0..94ed65d7f0 100644 --- a/tests/integrationtests/ngap/ngap_integration_test.cpp +++ b/tests/integrationtests/ngap/ngap_integration_test.cpp @@ -125,7 +125,7 @@ class ngap_integration_test : public ::testing::Test cfg.gnb_id = {411, 22}; cfg.ran_node_name = "srsgnb01"; - cfg.plmn = "00101"; + cfg.plmn = plmn_identity::test_value(); cfg.tac = 7; s_nssai_t slice_cfg; slice_cfg.sst = 1; diff --git a/tests/integrationtests/ofh/ofh_integration_test.cpp b/tests/integrationtests/ofh/ofh_integration_test.cpp index 2425b1d135..1e105046e9 100644 --- a/tests/integrationtests/ofh/ofh_integration_test.cpp +++ b/tests/integrationtests/ofh/ofh_integration_test.cpp @@ -84,7 +84,7 @@ namespace { /// User-defined test parameters. struct test_parameters { bool silent = false; - std::string log_level = "info"; + srslog::basic_levels log_level = srslog::basic_levels::info; std::string log_filename = "stdout"; bool is_prach_control_plane_enabled = true; bool is_downlink_broadcast_enabled = false; @@ -251,9 +251,11 @@ static void parse_args(int argc, char** argv) case 's': test_params.silent = (!test_params.silent); break; - case 'v': - test_params.log_level = std::string(optarg); + case 'v': { + auto value = srslog::str_to_basic_level(std::string(optarg)); + test_params.log_level = value.has_value() ? value.value() : srslog::basic_levels::none; break; + } case 'f': test_params.log_filename = std::string(optarg); break; @@ -1096,7 +1098,7 @@ int main(int argc, char** argv) srslog::init(); srslog::basic_logger& logger = srslog::fetch_basic_logger("OFH_TEST", false); - logger.set_level(srslog::str_to_basic_level(test_params.log_level)); + logger.set_level(test_params.log_level); unsigned nof_prb = get_max_Nprb(bs_channel_bandwidth_to_MHz(test_params.bw), test_params.scs, frequency_range::FR1); diff --git a/tests/integrationtests/ofh/ru_emulator.cpp b/tests/integrationtests/ofh/ru_emulator.cpp index ff449b6304..b2e15a0da6 100644 --- a/tests/integrationtests/ofh/ru_emulator.cpp +++ b/tests/integrationtests/ofh/ru_emulator.cpp @@ -472,7 +472,7 @@ int main(int argc, char** argv) srslog::init(); srslog::basic_logger& logger = srslog::fetch_basic_logger("RU_EMU", false); - logger.set_level(srslog::str_to_basic_level(ru_emulator_parsed_cfg.log_cfg.level)); + logger.set_level(ru_emulator_parsed_cfg.log_cfg.level); #ifdef DPDK_FOUND bool uses_dpdk = ru_emulator_parsed_cfg.dpdk_config.has_value(); diff --git a/tests/integrationtests/ofh/ru_emulator_appconfig.h b/tests/integrationtests/ofh/ru_emulator_appconfig.h index d91c520f56..cac963398f 100644 --- a/tests/integrationtests/ofh/ru_emulator_appconfig.h +++ b/tests/integrationtests/ofh/ru_emulator_appconfig.h @@ -52,7 +52,7 @@ struct ru_emulator_ofh_appconfig { /// RU emulator logging parameters. struct ru_emulator_log_appconfig { /// Log level - std::string level = "info"; + srslog::basic_levels level = srslog::basic_levels::info; /// Path to log file or "stdout" to print to console. std::string filename = "stdout"; }; diff --git a/tests/integrationtests/rlc/rlc_stress_test_args.h b/tests/integrationtests/rlc/rlc_stress_test_args.h index ab500ef09e..3707fe0cd8 100644 --- a/tests/integrationtests/rlc/rlc_stress_test_args.h +++ b/tests/integrationtests/rlc/rlc_stress_test_args.h @@ -216,30 +216,42 @@ inline bool parse_args(stress_test_args& args, int argc, char* argv[]) args.pdcp_ciphering_algo = std::strtol(optarg, nullptr, 10); fprintf(stdout, "PDCP ciphering algorithm %s\n", optarg); break; - case to_number(long_only::log_stack_level): - args.log_level_stack = srslog::str_to_basic_level(std::string(optarg)); + case to_number(long_only::log_stack_level): { + auto value = srslog::str_to_basic_level(std::string(optarg)); + args.log_level_stack = value.has_value() ? value.value() : srslog::basic_levels::none; fprintf(stdout, "STACK log level %s\n", optarg); break; - case to_number(long_only::log_traff_level): - args.log_level_traff = srslog::str_to_basic_level(std::string(optarg)); + } + case to_number(long_only::log_traff_level): { + auto value = srslog::str_to_basic_level(std::string(optarg)); + args.log_level_traff = value.has_value() ? value.value() : srslog::basic_levels::none; fprintf(stdout, "TRAFF log level %s\n", optarg); break; - case to_number(long_only::log_pdcp_level): - args.log_level_pdcp = srslog::str_to_basic_level(std::string(optarg)); + } + case to_number(long_only::log_pdcp_level): { + auto value = srslog::str_to_basic_level(std::string(optarg)); + args.log_level_pdcp = value.has_value() ? value.value() : srslog::basic_levels::none; fprintf(stdout, "PDCP log level %s\n", optarg); break; - case to_number(long_only::log_f1_level): - args.log_level_f1 = srslog::str_to_basic_level(std::string(optarg)); + } + case to_number(long_only::log_f1_level): { + auto value = srslog::str_to_basic_level(std::string(optarg)); + args.log_level_f1 = value.has_value() ? value.value() : srslog::basic_levels::none; fprintf(stdout, "F1 log level %s\n", optarg); break; - case to_number(long_only::log_rlc_level): - args.log_level_rlc = srslog::str_to_basic_level(std::string(optarg)); + } + case to_number(long_only::log_rlc_level): { + auto value = srslog::str_to_basic_level(std::string(optarg)); + args.log_level_rlc = value.has_value() ? value.value() : srslog::basic_levels::none; fprintf(stdout, "RLC log level %s\n", optarg); break; - case to_number(long_only::log_mac_level): - args.log_level_mac = srslog::str_to_basic_level(std::string(optarg)); + } + case to_number(long_only::log_mac_level): { + auto value = srslog::str_to_basic_level(std::string(optarg)); + args.log_level_mac = value.has_value() ? value.value() : srslog::basic_levels::none; fprintf(stdout, "MAC log level %s\n", optarg); break; + } case 'H': args.log_hex_limit = std::strtol(optarg, nullptr, 10); fprintf(stdout, "Log hex limit %d\n", args.log_hex_limit); diff --git a/tests/integrationtests/rlc/rlc_stress_test_f1.h b/tests/integrationtests/rlc/rlc_stress_test_f1.h index fcc9d04767..9ab786ca93 100644 --- a/tests/integrationtests/rlc/rlc_stress_test_f1.h +++ b/tests/integrationtests/rlc/rlc_stress_test_f1.h @@ -40,13 +40,13 @@ class f1ap_dummy : public pdcp_tx_lower_notifier, pdcp_rx_lower_interface* pdcp_rx_lower = nullptr; public: - f1ap_dummy(uint32_t id) : logger("F1AP", {gnb_du_id_t::min, id, drb_id_t::drb1, "DL"}) {} + f1ap_dummy(uint32_t id) : logger("F1AP", {gnb_du_id_t::min, id, srb_id_t::srb1, "DL"}) {} // PDCP -> F1 -> RLC - void on_new_pdu(byte_buffer pdu) final + void on_new_pdu(byte_buffer pdu, bool is_retx) final { logger.log_info("Passing F1AP PDU to RLC"); - rlc_tx_upper->handle_sdu(std::move(pdu)); + rlc_tx_upper->handle_sdu(std::move(pdu), is_retx); } // PDCP -> F1AP -> RLC @@ -70,6 +70,18 @@ class f1ap_dummy : public pdcp_tx_lower_notifier, // TODO } + void on_retransmitted_sdu(uint32_t max_retx_pdcp_sn) final + { + logger.log_error("Retransmitted SDU called"); + // TODO + } + + void on_delivered_retransmitted_sdu(uint32_t max_deliv_retx_pdcp_sn) final + { + logger.log_error("Delivered retransmitted SDU called"); + // TODO + } + // RLC -> F1AP -> PDCP void on_new_sdu(byte_buffer_chain pdu) final { diff --git a/tests/test_doubles/f1ap/f1ap_test_message_validators.cpp b/tests/test_doubles/f1ap/f1ap_test_message_validators.cpp index c95d6e60c1..eeb6b16d26 100644 --- a/tests/test_doubles/f1ap/f1ap_test_message_validators.cpp +++ b/tests/test_doubles/f1ap/f1ap_test_message_validators.cpp @@ -51,7 +51,7 @@ bool srsran::test_helpers::is_init_ul_rrc_msg_transfer_valid(const f1ap_message& TRUE_OR_RETURN(to_rnti(rrcmsg->c_rnti) == rnti); - if (nci.has_value() and cgi_from_asn1(rrcmsg->nr_cgi) != nci) { + if (nci.has_value() and cgi_from_asn1(rrcmsg->nr_cgi).value() != nci) { return false; } diff --git a/tests/test_doubles/f1ap/f1ap_test_messages.cpp b/tests/test_doubles/f1ap/f1ap_test_messages.cpp index ad6f6c8633..bbbd94a90e 100644 --- a/tests/test_doubles/f1ap/f1ap_test_messages.cpp +++ b/tests/test_doubles/f1ap/f1ap_test_messages.cpp @@ -33,11 +33,11 @@ using namespace srsran; using namespace asn1::f1ap; gnb_du_served_cells_item_s -srsran::test_helpers::generate_served_cells_item(unsigned nrcell_id, pci_t nrpci, unsigned tac) +srsran::test_helpers::generate_served_cells_item(nr_cell_identity nci, pci_t nrpci, unsigned tac) { gnb_du_served_cells_item_s served_cells_item; served_cells_item.served_cell_info.nr_cgi.plmn_id.from_string("00f110"); - served_cells_item.served_cell_info.nr_cgi.nr_cell_id.from_number(nrcell_id); + served_cells_item.served_cell_info.nr_cgi.nr_cell_id.from_number(nci.value()); served_cells_item.served_cell_info.nr_pci = nrpci; served_cells_item.served_cell_info.five_gs_tac_present = true; served_cells_item.served_cell_info.five_gs_tac.from_number(tac); @@ -69,7 +69,7 @@ srsran::test_helpers::generate_served_cells_item(unsigned nrcell_id, pci_t nrpci } f1ap_message -srsran::test_helpers::generate_f1_setup_request(gnb_du_id_t gnb_du_id, unsigned nrcell_id, pci_t pci, unsigned tac) +srsran::test_helpers::generate_f1_setup_request(gnb_du_id_t gnb_du_id, nr_cell_identity nci, pci_t pci, unsigned tac) { f1ap_message msg; msg.pdu.set_init_msg(); @@ -84,8 +84,7 @@ srsran::test_helpers::generate_f1_setup_request(gnb_du_id_t gnb_du_id, unsigned setup_req->gnb_du_served_cells_list_present = true; setup_req->gnb_du_served_cells_list.resize(1); setup_req->gnb_du_served_cells_list[0].load_info_obj(ASN1_F1AP_ID_GNB_DU_SERVED_CELLS_ITEM); - setup_req->gnb_du_served_cells_list[0].value().gnb_du_served_cells_item() = - generate_served_cells_item(nrcell_id, pci, tac); + setup_req->gnb_du_served_cells_list[0].value().gnb_du_served_cells_item() = generate_served_cells_item(nci, pci, tac); return msg; } @@ -229,8 +228,8 @@ f1ap_message srsran::test_helpers::create_init_ul_rrc_message_transfer(gnb_du_ue init_ul_rrc_msg_transfer_s& init_ul_rrc = init_ul_rrc_msg.pdu.init_msg().value.init_ul_rrc_msg_transfer(); init_ul_rrc->gnb_du_ue_f1ap_id = (unsigned)du_ue_id; - nr_cell_id_t nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); - init_ul_rrc->nr_cgi.nr_cell_id.from_number(nci); + nr_cell_identity nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); + init_ul_rrc->nr_cgi.nr_cell_id.from_number(nci.value()); init_ul_rrc->nr_cgi.plmn_id.from_string("00f110"); init_ul_rrc->c_rnti = to_value(crnti); diff --git a/tests/test_doubles/f1ap/f1ap_test_messages.h b/tests/test_doubles/f1ap/f1ap_test_messages.h index 6d0bfa57fd..256ceaca90 100644 --- a/tests/test_doubles/f1ap/f1ap_test_messages.h +++ b/tests/test_doubles/f1ap/f1ap_test_messages.h @@ -31,7 +31,7 @@ #include "srsran/f1ap/common/f1ap_ue_id.h" #include "srsran/ran/gnb_du_id.h" #include "srsran/ran/lcid.h" -#include "srsran/ran/nr_cgi_helpers.h" +#include "srsran/ran/nr_cell_identity.h" #include "srsran/ran/pci.h" #include "srsran/ran/rnti.h" @@ -42,14 +42,13 @@ struct f1ap_message; namespace test_helpers { /// \brief Generate a dummy F1AP Served Cell Item. -asn1::f1ap::gnb_du_served_cells_item_s generate_served_cells_item(unsigned nrcell_id, pci_t nrpci, unsigned tac = 7); +asn1::f1ap::gnb_du_served_cells_item_s generate_served_cells_item(nr_cell_identity nci, pci_t nrpci, unsigned tac = 7); /// \brief Generates dummy F1AP SETUP REQUEST message. -f1ap_message generate_f1_setup_request(gnb_du_id_t gnb_du_id = int_to_gnb_du_id(0x11), - unsigned nrcell_id = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, - 0U), - pci_t pci = 0, - unsigned tac = 7); +f1ap_message generate_f1_setup_request(gnb_du_id_t gnb_du_id = int_to_gnb_du_id(0x11), + nr_cell_identity nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0U).value(), + pci_t pci = 0, + unsigned tac = 7); /// \brief Generates dummy F1 SETUP RESPONSE message based on the request. f1ap_message generate_f1_setup_response(const f1ap_message& f1_setup_request); diff --git a/tests/unittests/adt/byte_buffer_chain_test.cpp b/tests/unittests/adt/byte_buffer_chain_test.cpp index d4511919a4..642cc503bd 100644 --- a/tests/unittests/adt/byte_buffer_chain_test.cpp +++ b/tests/unittests/adt/byte_buffer_chain_test.cpp @@ -22,7 +22,6 @@ #include "srsran/adt/byte_buffer_chain.h" #include "srsran/adt/detail/byte_buffer_segment_pool.h" -#include "srsran/support/test_utils.h" #include using namespace srsran; @@ -52,7 +51,7 @@ class byte_buffer_chain_test : public testing::Test TEST_F(byte_buffer_chain_test, empty_container_in_valid_state) { auto buffer = byte_buffer_chain::create(); - ASSERT_FALSE(buffer.is_error()); + ASSERT_TRUE(buffer.has_value()); byte_buffer_chain& chain = buffer.value(); ASSERT_TRUE(chain.empty()); ASSERT_EQ(chain.length(), 0U); @@ -79,7 +78,7 @@ TEST_F(byte_buffer_chain_test, empty_container_in_valid_state) TEST_F(byte_buffer_chain_test, append_byte_buffer) { auto buffer = byte_buffer_chain::create(); - ASSERT_FALSE(buffer.is_error()); + ASSERT_TRUE(buffer.has_value()); byte_buffer_chain& buf = buffer.value(); byte_buffer other_buffer = byte_buffer::create({1, 2, 3, 4, 5}).value(); @@ -120,7 +119,7 @@ TEST_F(byte_buffer_chain_test, prepend_buffer) { std::vector vec = {1, 2, 3}; auto buffer = byte_buffer_chain::create(); - ASSERT_FALSE(buffer.is_error()); + ASSERT_TRUE(buffer.has_value()); byte_buffer_chain& buf = buffer.value(); byte_buffer buf2 = byte_buffer::create(vec).value(); @@ -167,7 +166,7 @@ TEST_F(byte_buffer_chain_test, prepend_buffer) TEST_F(byte_buffer_chain_test, prepend_header_and_append_payload) { auto buffer = byte_buffer_chain::create(); - ASSERT_FALSE(buffer.is_error()); + ASSERT_TRUE(buffer.has_value()); byte_buffer_chain& buf = buffer.value(); byte_buffer header_bytes = byte_buffer::create({1, 2, 3}).value(); @@ -201,7 +200,7 @@ TEST_F(byte_buffer_chain_test, prepend_header_and_append_payload) TEST_F(byte_buffer_chain_test, payload_lifetime) { auto buffer = byte_buffer_chain::create(); - ASSERT_FALSE(buffer.is_error()); + ASSERT_TRUE(buffer.has_value()); byte_buffer_chain& buf = buffer.value(); std::vector all_bytes; @@ -232,7 +231,7 @@ TEST_F(byte_buffer_chain_test, slice_chain_formatter) ASSERT_TRUE(pdu2.append(bytes)); auto buffer = byte_buffer_chain::create(); - ASSERT_FALSE(buffer.is_error()); + ASSERT_TRUE(buffer.has_value()); byte_buffer_chain& chain = buffer.value(); ASSERT_TRUE(chain.append(byte_buffer_slice{std::move(pdu), 3, 2})); ASSERT_TRUE(chain.append(byte_buffer_slice{std::move(pdu2), 0, 2})); diff --git a/tests/unittests/adt/byte_buffer_test.cpp b/tests/unittests/adt/byte_buffer_test.cpp index 867264eacd..a700c9e144 100644 --- a/tests/unittests/adt/byte_buffer_test.cpp +++ b/tests/unittests/adt/byte_buffer_test.cpp @@ -826,7 +826,7 @@ TEST_P(byte_buffer_stress_tester, concurrent_alloc_dealloc_test) // Allocation of new buffer. auto buf = byte_buffer::create(span(randbytes.data(), test_rgen::uniform_int(1U, max_buffer_size))); - if (buf.is_error()) { + if (not buf.has_value()) { // pool is depleted. continue; } @@ -877,7 +877,7 @@ TEST_F(byte_buffer_tester, concurrent_alloc_dealloc_test) // Deplete the pool while (true) { auto pdu = byte_buffer::create(span{randbytes.data(), 10U}); - if (pdu.is_error()) { + if (not pdu.has_value()) { break; } allocated_buffers.push_back(std::move(pdu.value())); @@ -885,7 +885,7 @@ TEST_F(byte_buffer_tester, concurrent_alloc_dealloc_test) // Pool is still empty. auto pdu = byte_buffer::create(span{randbytes.data(), test_rgen::uniform_int(1U, max_buffer_size)}); - ASSERT_TRUE(pdu.is_error()); + ASSERT_FALSE(pdu.has_value()); // Test if a span can be added to a byte_buffer with heap as fallback allocator. size_t sz = test_rgen::uniform_int(1U, max_buffer_size); diff --git a/tests/unittests/adt/expected_test.cpp b/tests/unittests/adt/expected_test.cpp index 08d362e622..d4e6a2c48a 100644 --- a/tests/unittests/adt/expected_test.cpp +++ b/tests/unittests/adt/expected_test.cpp @@ -25,6 +25,13 @@ using namespace srsran; +// test trivially_destructible +static_assert(std::is_trivially_destructible_v>, "expected should be trivially destructible"); +static_assert(not std::is_trivially_destructible_v>, + "expected should not be trivially destructible"); +static_assert(not std::is_trivially_destructible_v>, + "expected should not be trivially destructible"); + void test_expected_trivial() { expected exp; @@ -36,7 +43,7 @@ void test_expected_trivial() TESTASSERT(exp.value() == 5); TESTASSERT(exp); - exp.set_error(default_error_t{}); + exp = make_unexpected(default_error_t{}); TESTASSERT(not exp.has_value()); TESTASSERT(not exp); @@ -45,7 +52,7 @@ void test_expected_trivial() TESTASSERT(exp); TESTASSERT(exp.value() == 2); - exp.set_error(); + exp = make_unexpected(default_error_t{}); TESTASSERT(not exp); exp = 3; @@ -58,7 +65,7 @@ void test_expected_trivial() } TESTASSERT(exp and exp.value() == 3); - exp.set_error(); + exp = make_unexpected(default_error_t{}); { expected exp2{exp}; TESTASSERT(not exp2); @@ -120,7 +127,7 @@ void test_expected_struct() TESTASSERT(exp and exp.value().val == 0); } - exp.set_error(2); + exp = make_unexpected(2); TESTASSERT(not exp and exp.error() == 2); } diff --git a/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test.cpp b/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test.cpp index e069213c53..caf7877b61 100644 --- a/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test.cpp +++ b/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test.cpp @@ -34,8 +34,8 @@ TEST_F(cell_meas_manager_test, when_empty_cell_config_is_used_validation_fails) TEST_F(cell_meas_manager_test, when_valid_cell_config_is_used_validation_succeeds) { cell_meas_config cell_cfg; - cell_cfg.serving_cell_cfg.nci = 0x19b0; - cell_cfg.serving_cell_cfg.gnb_id = gnb_id_t{411, 32}; + cell_cfg.serving_cell_cfg.nci = nr_cell_identity::create(0x19b0).value(); + cell_cfg.serving_cell_cfg.gnb_id = cell_cfg.serving_cell_cfg.nci.gnb_id(32); cell_cfg.serving_cell_cfg.pci = 1; cell_cfg.serving_cell_cfg.band.emplace() = nr_band::n78; cell_cfg.serving_cell_cfg.ssb_arfcn.emplace() = 632628; @@ -59,7 +59,7 @@ TEST_F(cell_meas_manager_test, when_empty_config_is_used_then_no_neighbor_cells_ create_empty_manager(); ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); - nr_cell_id_t nci = 0x19b0; + nr_cell_identity nci = nr_cell_identity::create(0x19b0).value(); std::optional meas_cfg = manager->get_measurement_config(ue_index, nci); // Make sure meas_cfg is empty. @@ -71,7 +71,7 @@ TEST_F(cell_meas_manager_test, when_serving_cell_not_found_no_neighbor_cells_are create_default_manager(); ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); - nr_cell_id_t nci = 0x19b5; + nr_cell_identity nci = nr_cell_identity::create(0x19b5).value(); std::optional meas_cfg = manager->get_measurement_config(ue_index, nci); // Make sure meas_cfg is empty. @@ -84,8 +84,9 @@ TEST_F(cell_meas_manager_test, when_serving_cell_found_then_neighbor_cells_are_a ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); - for (unsigned nci = 0x19b0; nci < 0x19b2; ++nci) { - std::optional meas_cfg = manager->get_measurement_config(ue_index, nci); + for (unsigned nci_val = 0x19b0; nci_val < 0x19b2; ++nci_val) { + std::optional meas_cfg = + manager->get_measurement_config(ue_index, nr_cell_identity::create(nci_val).value()); check_default_meas_cfg(meas_cfg, meas_obj_id_t::min); verify_meas_cfg(meas_cfg); } @@ -95,8 +96,8 @@ TEST_F(cell_meas_manager_test, when_inexisting_cell_config_is_updated_then_confi { create_default_manager(); - ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); - const nr_cell_id_t nci = 0x19b1; + ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); + const nr_cell_identity nci = nr_cell_identity::create(0x19b1).value(); // get current config std::optional cell_cfg = manager->get_cell_config(nci); @@ -105,7 +106,7 @@ TEST_F(cell_meas_manager_test, when_inexisting_cell_config_is_updated_then_confi // update config for cell 3 auto& cell_cfg_val = cell_cfg.value(); cell_cfg_val.serving_cell_cfg.gnb_id = gnb_id_t{411, 32}; - cell_cfg_val.serving_cell_cfg.nci = 0x19b3; + cell_cfg_val.serving_cell_cfg.nci = nr_cell_identity::create(0x19b3).value(); cell_cfg_val.serving_cell_cfg.band.emplace() = nr_band::n78; cell_cfg_val.serving_cell_cfg.ssb_arfcn.emplace() = 632628; cell_cfg_val.serving_cell_cfg.ssb_scs.emplace() = subcarrier_spacing::kHz30; @@ -120,8 +121,8 @@ TEST_F(cell_meas_manager_test, when_incomplete_cell_config_is_updated_then_valid { create_default_manager(); - ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); - const nr_cell_id_t nci = 0x19b1; + ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); + const nr_cell_identity nci = nr_cell_identity::create(0x19b1).value(); // get current config std::optional cell_cfg = manager->get_cell_config(nci); @@ -145,7 +146,7 @@ TEST_F(cell_meas_manager_test, when_empty_cell_config_is_used_then_meas_cfg_is_n create_manager_without_ncells_and_periodic_report(); ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); - nr_cell_id_t nci = 0x19b0; + nr_cell_identity nci = nr_cell_identity::create(0x19b0).value(); std::optional meas_cfg = manager->get_measurement_config(ue_index, nci); // Make sure meas_cfg is empty. @@ -156,15 +157,15 @@ TEST_F(cell_meas_manager_test, when_old_meas_config_is_provided_old_ids_are_remo { create_default_manager(); - ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); - const nr_cell_id_t initial_nci = 0x19b0; + ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); + const nr_cell_identity initial_nci = nr_cell_identity::create(0x19b0).value(); // Make sure meas_cfg is created (no previous meas config provided) std::optional initial_meas_cfg = manager->get_measurement_config(ue_index, initial_nci); check_default_meas_cfg(initial_meas_cfg, meas_obj_id_t::min); verify_meas_cfg(initial_meas_cfg); - const nr_cell_id_t target_nci = 0x19b1; + const nr_cell_identity target_nci = nr_cell_identity::create(0x19b1).value(); std::optional target_meas_cfg = manager->get_measurement_config(ue_index, target_nci, initial_meas_cfg); // Make sure initial IDs are release again. @@ -186,9 +187,9 @@ TEST_F(cell_meas_manager_test, when_only_event_based_reports_configured_then_mea { create_manager_with_incomplete_cells_and_periodic_report_at_target_cell(); - ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); - const nr_cell_id_t initial_nci = 0x19b0; - const nr_cell_id_t target_nci = 0x19b1; + ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); + const nr_cell_identity initial_nci = nr_cell_identity::create(0x19b0).value(); + const nr_cell_identity target_nci = nr_cell_identity::create(0x19b1).value(); // Make sure no meas_cfg is created (incomplete cell config) ASSERT_FALSE(manager->get_measurement_config(ue_index, initial_nci).has_value()); @@ -238,9 +239,9 @@ TEST_F(cell_meas_manager_test, when_invalid_cell_config_update_received_then_con { create_manager_with_incomplete_cells_and_periodic_report_at_target_cell(); - ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); - const nr_cell_id_t initial_nci = 0x19b0; - const nr_cell_id_t target_nci = 0x19b1; + ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); + const nr_cell_identity initial_nci = nr_cell_identity::create(0x19b0).value(); + const nr_cell_identity target_nci = nr_cell_identity::create(0x19b1).value(); // Make sure no meas_cfg is created (incomplete cell config) ASSERT_FALSE(manager->get_measurement_config(ue_index, initial_nci).has_value()); diff --git a/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test_helpers.cpp b/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test_helpers.cpp index b6d6a11022..86c3660585 100644 --- a/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test_helpers.cpp +++ b/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test_helpers.cpp @@ -52,15 +52,18 @@ void cell_meas_manager_test::create_default_manager() cell_meas_manager_cfg cfg; // Add 2 cells - one being the neighbor of the other one + gnb_id_t gnb_id{0x19b, 32}; + nr_cell_identity nci1 = nr_cell_identity::create(gnb_id, 0).value(); + nr_cell_identity nci2 = nr_cell_identity::create(gnb_id, 1).value(); cell_meas_config cell_cfg; - cell_cfg.serving_cell_cfg.gnb_id = gnb_id_t{411, 32}; - cell_cfg.serving_cell_cfg.nci = 0x19b0; + cell_cfg.serving_cell_cfg.gnb_id = gnb_id; + cell_cfg.serving_cell_cfg.nci = nci1; cell_cfg.serving_cell_cfg.pci = 1; cell_cfg.periodic_report_cfg_id = uint_to_report_cfg_id(1); neighbor_cell_meas_config ncell_meas_cfg; - ncell_meas_cfg.nci = 0x19b1; + ncell_meas_cfg.nci = nci2; ncell_meas_cfg.report_cfg_ids.push_back(uint_to_report_cfg_id(2)); cell_cfg.ncells.push_back(ncell_meas_cfg); @@ -77,10 +80,10 @@ void cell_meas_manager_test::create_default_manager() cfg.cells.emplace(cell_cfg.serving_cell_cfg.nci, cell_cfg); // Reuse config to setup config for next cell. - cell_cfg.serving_cell_cfg.nci = 0x19b1; + cell_cfg.serving_cell_cfg.nci = nci2; cell_cfg.ncells.clear(); - ncell_meas_cfg.nci = 0x19b0; + ncell_meas_cfg.nci = nci1; ncell_meas_cfg.report_cfg_ids.clear(); ncell_meas_cfg.report_cfg_ids.push_back(uint_to_report_cfg_id(2)); cell_cfg.ncells.push_back(ncell_meas_cfg); @@ -147,24 +150,27 @@ void cell_meas_manager_test::create_manager_with_incomplete_cells_and_periodic_r cell_meas_manager_cfg cfg; // Add 2 cells - one being the neighbor of the other one + gnb_id_t gnb_id{0x19b, 32}; + nr_cell_identity nci1 = nr_cell_identity::create(gnb_id, 0).value(); + nr_cell_identity nci2 = nr_cell_identity::create(gnb_id, 1).value(); cell_meas_config cell_cfg; - cell_cfg.serving_cell_cfg.gnb_id = gnb_id_t{411, 32}; - cell_cfg.serving_cell_cfg.nci = 0x19b0; + cell_cfg.serving_cell_cfg.gnb_id = gnb_id; + cell_cfg.serving_cell_cfg.nci = nci1; cell_cfg.serving_cell_cfg.pci = 1; neighbor_cell_meas_config ncell_meas_cfg; - ncell_meas_cfg.nci = 0x19b1; + ncell_meas_cfg.nci = nci2; ncell_meas_cfg.report_cfg_ids.push_back(uint_to_report_cfg_id(2)); cell_cfg.ncells.push_back(ncell_meas_cfg); cfg.cells.emplace(cell_cfg.serving_cell_cfg.nci, cell_cfg); // Reuse config to setup config for next cell. - cell_cfg.serving_cell_cfg.nci = 0x19b1; + cell_cfg.serving_cell_cfg.nci = nci2; cell_cfg.periodic_report_cfg_id = uint_to_report_cfg_id(1); cell_cfg.ncells.clear(); - ncell_meas_cfg.nci = 0x19b0; + ncell_meas_cfg.nci = nci1; ncell_meas_cfg.report_cfg_ids.clear(); ncell_meas_cfg.report_cfg_ids.push_back(uint_to_report_cfg_id(2)); cell_cfg.ncells.push_back(ncell_meas_cfg); @@ -222,7 +228,7 @@ void cell_meas_manager_test::create_manager_without_ncells_and_periodic_report() // Add serving cell cell_meas_config cell_cfg; cell_cfg.serving_cell_cfg.gnb_id = gnb_id_t{411, 32}; - cell_cfg.serving_cell_cfg.nci = 0x19b0; + cell_cfg.serving_cell_cfg.nci = nr_cell_identity::create(cell_cfg.serving_cell_cfg.gnb_id, 0).value(); cell_cfg.serving_cell_cfg.band.emplace() = nr_band::n78; cell_cfg.serving_cell_cfg.ssb_arfcn.emplace() = 632628; diff --git a/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test_helpers.h b/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test_helpers.h index c94d2a85e3..a31989e39c 100644 --- a/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test_helpers.h +++ b/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test_helpers.h @@ -32,10 +32,10 @@ namespace srs_cu_cp { class dummy_mobility_manager : public cell_meas_mobility_manager_notifier { public: - void on_neighbor_better_than_spcell(ue_index_t ue_index, - gnb_id_t neighbor_gnb_id, - nr_cell_id_t neighbor_nci, - pci_t neighbor_pci) override + void on_neighbor_better_than_spcell(ue_index_t ue_index, + gnb_id_t neighbor_gnb_id, + nr_cell_identity neighbor_nci, + pci_t neighbor_pci) override { fmt::print("on_neighbor_better_than_spcell() called.\n"); } diff --git a/tests/unittests/cu_cp/cu_cp_connectivity_test.cpp b/tests/unittests/cu_cp/cu_cp_connectivity_test.cpp index e306fabe2c..0fe110ab79 100644 --- a/tests/unittests/cu_cp/cu_cp_connectivity_test.cpp +++ b/tests/unittests/cu_cp/cu_cp_connectivity_test.cpp @@ -39,7 +39,7 @@ using namespace srs_cu_cp; class cu_cp_connectivity_test : public cu_cp_test_environment, public ::testing::Test { public: - cu_cp_connectivity_test() : cu_cp_test_environment(cu_cp_test_env_params{8, 8, create_mock_amf()}) {} + cu_cp_connectivity_test() : cu_cp_test_environment(cu_cp_test_env_params{8, 8, 8192, create_mock_amf()}) {} }; //----------------------------------------------------------------------------------// @@ -179,7 +179,8 @@ TEST_F(cu_cp_connectivity_test, when_a_du_with_non_matching_gnb_id_connects_then // DU sends F1 Setup Request. gnb_du_id_t du_id = int_to_gnb_du_id(0x55); - f1ap_message f1ap_pdu = test_helpers::generate_f1_setup_request(du_id, 0x0); // the gnb-id does not match. + f1ap_message f1ap_pdu = test_helpers::generate_f1_setup_request( + du_id, nr_cell_identity::create(0x0).value()); // the gnb-id does not match. get_du(du_idx).push_ul_pdu(f1ap_pdu); // Ensure the F1 Setup Failure is received for the DU. diff --git a/tests/unittests/cu_cp/cu_cp_reestablishment_test.cpp b/tests/unittests/cu_cp/cu_cp_reestablishment_test.cpp index 387f356a4f..e035a16338 100644 --- a/tests/unittests/cu_cp/cu_cp_reestablishment_test.cpp +++ b/tests/unittests/cu_cp/cu_cp_reestablishment_test.cpp @@ -33,7 +33,7 @@ using namespace srs_cu_cp; class cu_cp_reestablishment_test : public cu_cp_test_environment, public ::testing::Test { public: - cu_cp_reestablishment_test() : cu_cp_test_environment(cu_cp_test_env_params{8, 8, create_mock_amf()}) + cu_cp_reestablishment_test() : cu_cp_test_environment(cu_cp_test_env_params{8, 8, 8192, create_mock_amf()}) { // Run NG setup to completion. run_ng_setup(); diff --git a/tests/unittests/cu_cp/cu_cp_setup_test.cpp b/tests/unittests/cu_cp/cu_cp_setup_test.cpp index 3663a41c37..6647502b20 100644 --- a/tests/unittests/cu_cp/cu_cp_setup_test.cpp +++ b/tests/unittests/cu_cp/cu_cp_setup_test.cpp @@ -31,7 +31,7 @@ using namespace srs_cu_cp; class cu_cp_setup_test : public cu_cp_test_environment, public ::testing::Test { public: - cu_cp_setup_test() : cu_cp_test_environment(cu_cp_test_env_params{8, 8, create_mock_amf()}) + cu_cp_setup_test() : cu_cp_test_environment(cu_cp_test_env_params{8, 8, 8192, create_mock_amf()}) { // Run NG setup to completion. run_ng_setup(); diff --git a/tests/unittests/cu_cp/cu_cp_test.cpp b/tests/unittests/cu_cp/cu_cp_test.cpp index 0cf27c8ef7..e3b808dae4 100644 --- a/tests/unittests/cu_cp/cu_cp_test.cpp +++ b/tests/unittests/cu_cp/cu_cp_test.cpp @@ -86,7 +86,8 @@ TEST_F(cu_cp_test, when_valid_paging_message_received_then_paging_is_only_sent_t f1ap_message_notifier* f1c_rx_pdu_notif2 = this->f1c_gw.request_new_du_connection(); // Generate F1SetupRequest - f1ap_message f1setup_msg2 = test_helpers::generate_f1_setup_request(int_to_gnb_du_id(0x12), 6577, 1, 8); + f1ap_message f1setup_msg2 = + test_helpers::generate_f1_setup_request(int_to_gnb_du_id(0x12), nr_cell_identity::create(6577).value(), 1, 8); // Pass message to CU-CP f1c_rx_pdu_notif2->on_new_message(f1setup_msg2); @@ -115,7 +116,8 @@ TEST_F(cu_cp_test, when_valid_paging_message_received_then_paging_is_only_sent_t f1ap_message_notifier* f1c_rx_pdu_notif2 = this->f1c_gw.request_new_du_connection(); // Generate F1SetupRequest - f1ap_message f1setup_msg2 = test_helpers::generate_f1_setup_request(int_to_gnb_du_id(0x12), 6577, 1, 7); + f1ap_message f1setup_msg2 = + test_helpers::generate_f1_setup_request(int_to_gnb_du_id(0x12), nr_cell_identity::create(6577).value(), 1, 7); // Pass message to CU-CP f1c_rx_pdu_notif2->on_new_message(f1setup_msg2); diff --git a/tests/unittests/cu_cp/cu_cp_test_environment.cpp b/tests/unittests/cu_cp/cu_cp_test_environment.cpp index 325254f57c..d5be97db7a 100644 --- a/tests/unittests/cu_cp/cu_cp_test_environment.cpp +++ b/tests/unittests/cu_cp/cu_cp_test_environment.cpp @@ -82,7 +82,7 @@ cu_cp_test_environment::cu_cp_test_environment(cu_cp_test_env_params params_) : cu_cp_cfg.ngap_config = config_helpers::make_default_ngap_config(); cu_cp_cfg.max_nof_dus = params.max_nof_dus; cu_cp_cfg.max_nof_cu_ups = params.max_nof_cu_ups; - cu_cp_cfg.ue_config.max_nof_supported_ues = params.max_nof_dus * MAX_NOF_UES_PER_DU; + cu_cp_cfg.ue_config.max_nof_supported_ues = params.max_nof_ues; // > RRC config. cu_cp_cfg.rrc_config.gnb_id = cu_cp_cfg.ngap_config.gnb_id; cu_cp_cfg.rrc_config.drb_config = config_helpers::make_default_cu_cp_qos_config_list(); diff --git a/tests/unittests/cu_cp/cu_cp_test_environment.h b/tests/unittests/cu_cp/cu_cp_test_environment.h index 68880296bc..11c5ca4250 100644 --- a/tests/unittests/cu_cp/cu_cp_test_environment.h +++ b/tests/unittests/cu_cp/cu_cp_test_environment.h @@ -37,6 +37,7 @@ namespace srs_cu_cp { struct cu_cp_test_env_params { unsigned max_nof_cu_ups = 4; unsigned max_nof_dus = 4; + unsigned max_nof_ues = 8192; /// Injected AMF stub to handle CU-CP PDUs. std::unique_ptr amf_stub = create_mock_amf(); }; diff --git a/tests/unittests/cu_cp/cu_cp_test_helpers.cpp b/tests/unittests/cu_cp/cu_cp_test_helpers.cpp index 9f8465b1c0..f9bceb9ce6 100644 --- a/tests/unittests/cu_cp/cu_cp_test_helpers.cpp +++ b/tests/unittests/cu_cp/cu_cp_test_helpers.cpp @@ -70,7 +70,7 @@ cu_cp_test::cu_cp_test() // NGAP config cfg.ngap_config.gnb_id = {411, 22}; cfg.ngap_config.ran_node_name = "srsgnb01"; - cfg.ngap_config.plmn = "00101"; + cfg.ngap_config.plmn = plmn_identity::test_value(); cfg.ngap_config.tac = 7; s_nssai_t slice_cfg; slice_cfg.sst = 1; @@ -90,7 +90,7 @@ cu_cp_test::cu_cp_test() // UE config cfg.ue_config.inactivity_timer = std::chrono::seconds{7200}; - cfg.ue_config.max_nof_supported_ues = cfg.max_nof_dus * MAX_NOF_UES_PER_DU; + cfg.ue_config.max_nof_supported_ues = cfg.max_nof_ues; // periodic statistic logging cfg.statistics_report_period = std::chrono::seconds(1); @@ -99,11 +99,11 @@ cu_cp_test::cu_cp_test() cfg.mobility_config.mobility_manager_config.trigger_handover_from_measurements = true; // Generate NCIs. - gnb_id_t gnb_id1 = cfg.rrc_config.gnb_id; - nr_cell_id_t nci1 = config_helpers::make_nr_cell_identity(cfg.rrc_config.gnb_id, 0); - nr_cell_id_t nci2 = config_helpers::make_nr_cell_identity(cfg.rrc_config.gnb_id, 1); - gnb_id_t gnb_id2 = {cfg.rrc_config.gnb_id.id + 1, cfg.rrc_config.gnb_id.bit_length}; - nr_cell_id_t nci3 = config_helpers::make_nr_cell_identity(gnb_id2, 0); + gnb_id_t gnb_id1 = cfg.rrc_config.gnb_id; + nr_cell_identity nci1 = nr_cell_identity::create(cfg.rrc_config.gnb_id, 0).value(); + nr_cell_identity nci2 = nr_cell_identity::create(cfg.rrc_config.gnb_id, 1).value(); + gnb_id_t gnb_id2 = {cfg.rrc_config.gnb_id.id + 1, cfg.rrc_config.gnb_id.bit_length}; + nr_cell_identity nci3 = nr_cell_identity::create(gnb_id2, 0).value(); cell_meas_config cell_cfg_1; cell_cfg_1.periodic_report_cfg_id = uint_to_report_cfg_id(1); @@ -297,7 +297,7 @@ void cu_cp_test::test_e1ap_attach() cu_cp_obj->get_e1_handler().get_cu_up(uint_to_cu_up_index(0)).get_message_handler().handle_message(e1setup_msg); } -void cu_cp_test::test_du_attach(du_index_t du_index, gnb_du_id_t gnb_du_id, unsigned nrcell_id, pci_t pci) +void cu_cp_test::test_du_attach(du_index_t du_index, gnb_du_id_t gnb_du_id, nr_cell_identity nrcell_id, pci_t pci) { // Store current number of DUs. size_t nof_dus = f1c_gw.nof_connections(); @@ -319,7 +319,7 @@ void cu_cp_test::test_preamble_all_connected(du_index_t du_index, pci_t pci) test_e1ap_attach(); - test_du_attach(du_index, int_to_gnb_du_id(0x11), config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0), pci); + test_du_attach(du_index, int_to_gnb_du_id(0x11), nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(), pci); } void cu_cp_test::test_preamble_ue_creation(du_index_t du_index, @@ -474,9 +474,9 @@ bool cu_cp_test::check_minimal_paging_result() test_logger.error("Paging cell list size mismatch {} != {}", paging_msg->paging_cell_list.size(), 1); return false; } - auto& paging_cell_item = paging_msg->paging_cell_list[0].value().paging_cell_item(); - nr_cell_id_t nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); - if (paging_cell_item.nr_cgi.nr_cell_id.to_number() != nci) { + auto& paging_cell_item = paging_msg->paging_cell_list[0].value().paging_cell_item(); + nr_cell_identity nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); + if (paging_cell_item.nr_cgi.nr_cell_id.to_number() != nci.value()) { test_logger.error("NR CGI NCI mismatch {} != {}", paging_cell_item.nr_cgi.nr_cell_id.to_number(), nci); return false; } diff --git a/tests/unittests/cu_cp/cu_cp_test_helpers.h b/tests/unittests/cu_cp/cu_cp_test_helpers.h index c24d5bb2d0..761507474f 100644 --- a/tests/unittests/cu_cp/cu_cp_test_helpers.h +++ b/tests/unittests/cu_cp/cu_cp_test_helpers.h @@ -46,7 +46,7 @@ class cu_cp_test : public ::testing::Test void test_amf_connection(); void test_e1ap_attach(); - void test_du_attach(du_index_t du_index, gnb_du_id_t gnb_du_id, unsigned nrcell_id, pci_t pci); + void test_du_attach(du_index_t du_index, gnb_du_id_t gnb_du_id, nr_cell_identity nrcell_id, pci_t pci); void attach_ue(gnb_du_ue_f1ap_id_t du_ue_id, gnb_cu_ue_f1ap_id_t cu_ue_id, rnti_t crnti, du_index_t du_index); void authenticate_ue(amf_ue_id_t amf_ue_id, diff --git a/tests/unittests/cu_cp/du_processor/du_configuration_manager_test.cpp b/tests/unittests/cu_cp/du_processor/du_configuration_manager_test.cpp index a0a9e350fb..5c06ef077d 100644 --- a/tests/unittests/cu_cp/du_processor/du_configuration_manager_test.cpp +++ b/tests/unittests/cu_cp/du_processor/du_configuration_manager_test.cpp @@ -21,7 +21,6 @@ */ #include "lib/cu_cp/du_processor/du_configuration_manager.h" -#include "srsran/ran/nr_cgi_helpers.h" #include "srsran/rrc/rrc_config.h" #include @@ -38,13 +37,10 @@ static rrc_cfg_t create_basic_rrc_config() static cu_cp_served_cell_info create_basic_served_cell_info() { cu_cp_served_cell_info cell_info; - cell_info.nr_cgi.mcc = 1; - cell_info.nr_cgi.mnc = 1; - cell_info.nr_cgi.plmn = "00101"; - cell_info.nr_cgi.plmn_hex = "00f110"; - cell_info.nr_cgi.nci = config_helpers::make_nr_cell_identity({411, 22}, 0x1); - cell_info.five_gs_tac = 7; - cell_info.nr_pci = 1; + cell_info.nr_cgi.plmn_id = plmn_identity::test_value(); + cell_info.nr_cgi.nci = nr_cell_identity::create({411, 22}, 0x1).value(); + cell_info.five_gs_tac = 7; + cell_info.nr_pci = 1; return cell_info; } diff --git a/tests/unittests/cu_cp/du_processor/du_processor_test.cpp b/tests/unittests/cu_cp/du_processor/du_processor_test.cpp index 65145d6132..6094535e8a 100644 --- a/tests/unittests/cu_cp/du_processor/du_processor_test.cpp +++ b/tests/unittests/cu_cp/du_processor/du_processor_test.cpp @@ -25,6 +25,7 @@ #include "lib/cu_cp/du_processor/du_processor.h" #include "tests/unittests/f1ap/common/f1ap_cu_test_messages.h" #include "srsran/asn1/f1ap/f1ap_pdu_contents.h" +#include "srsran/cu_cp/cu_cp_types.h" #include using namespace srsran; @@ -109,14 +110,13 @@ TEST_F(du_processor_test, when_ue_creation_msg_valid_then_ue_added) test_helpers::generate_f1_setup_request()); // Generate ue_creation message - ue_index_t ue_index = du_processor_obj->get_f1ap_interface().allocate_new_ue_index(); - ue_rrc_context_creation_request req = generate_ue_rrc_context_creation_request( - ue_index, rnti_t::MIN_CRNTI, config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0)); + ue_rrc_context_creation_request req = generate_ue_rrc_context_creation_request( + ue_index_t::invalid, rnti_t::MIN_CRNTI, nr_cell_identity::create(gnb_id_t{411, 22}, 0).value()); // Pass message to DU processor - ue_rrc_context_creation_response resp = + ue_rrc_context_creation_outcome outcome = du_processor_obj->get_f1ap_interface().handle_ue_rrc_context_creation_request(req); - ASSERT_NE(resp.f1ap_rrc_notifier, nullptr); + ASSERT_TRUE(outcome.has_value()); ASSERT_EQ(du_processor_obj->get_statistics_handler().get_nof_ues(), 1); } @@ -128,13 +128,13 @@ TEST_F(du_processor_test, when_cell_id_invalid_then_ue_creation_fails) test_helpers::generate_f1_setup_request()); // Generate ue_creation message - ue_index_t ue_index = du_processor_obj->get_f1ap_interface().allocate_new_ue_index(); - ue_rrc_context_creation_request req = generate_ue_rrc_context_creation_request(ue_index, rnti_t::MIN_CRNTI, 1); + ue_rrc_context_creation_request req = generate_ue_rrc_context_creation_request( + ue_index_t::invalid, rnti_t::MIN_CRNTI, nr_cell_identity::create(1).value()); // Pass message to DU processor - ue_rrc_context_creation_response resp = + ue_rrc_context_creation_outcome outcome = du_processor_obj->get_f1ap_interface().handle_ue_rrc_context_creation_request(req); - ASSERT_EQ(resp.f1ap_rrc_notifier, nullptr); + ASSERT_TRUE(not outcome.has_value()); } TEST_F(du_processor_test, when_ue_rrc_context_exists_then_new_ue_rrc_context_not_added) @@ -148,20 +148,21 @@ TEST_F(du_processor_test, when_ue_rrc_context_exists_then_new_ue_rrc_context_not test_helpers::generate_f1_setup_request()); // Generate ue_creation message - ue_index_t ue_index = du_processor_obj->get_f1ap_interface().allocate_new_ue_index(); - ue_rrc_context_creation_request req = generate_ue_rrc_context_creation_request( - ue_index, rnti_t::MIN_CRNTI, config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0)); + ue_rrc_context_creation_request req = generate_ue_rrc_context_creation_request( + ue_index_t::invalid, rnti_t::MIN_CRNTI, nr_cell_identity::create(gnb_id_t{411, 22}, 0).value()); // Pass message to DU processor - ue_rrc_context_creation_response resp = + ue_rrc_context_creation_outcome outcome = du_processor_obj->get_f1ap_interface().handle_ue_rrc_context_creation_request(req); - ASSERT_NE(resp.f1ap_rrc_notifier, nullptr); + ASSERT_TRUE(outcome.has_value()); ASSERT_EQ(du_processor_obj->get_statistics_handler().get_nof_ues(), 1); // Pass same message to DU processor again - resp = du_processor_obj->get_f1ap_interface().handle_ue_rrc_context_creation_request(req); - ASSERT_EQ(resp.f1ap_rrc_notifier, nullptr); + req.ue_index = outcome->ue_index; + ue_rrc_context_creation_outcome outcome2 = + du_processor_obj->get_f1ap_interface().handle_ue_rrc_context_creation_request(req); + ASSERT_TRUE(not outcome2.has_value()); ASSERT_EQ(du_processor_obj->get_statistics_handler().get_nof_ues(), 1); } @@ -174,33 +175,38 @@ TEST_F(du_processor_test, when_max_nof_ues_exceeded_then_ue_not_added) // Reduce logger loglevel to warning to reduce console output srslog::fetch_basic_logger("CU-CP").set_level(srslog::basic_levels::warning); + srslog::fetch_basic_logger("RRC").set_level(srslog::basic_levels::warning); srslog::fetch_basic_logger("CU-UEMNG").set_level(srslog::basic_levels::warning); srslog::fetch_basic_logger("TEST").set_level(srslog::basic_levels::warning); // Add the maximum number of UEs - for (unsigned it = 0; it < MAX_NOF_UES_PER_DU; it++) { + for (unsigned it = 0; it < ue_mng.get_ue_config().max_nof_supported_ues; it++) { // Generate ue_creation message - rnti_t c_rnti = to_rnti(it + 1); // 0 is not a valid RNTI - ue_index_t ue_index = du_processor_obj->get_f1ap_interface().allocate_new_ue_index(); - ue_rrc_context_creation_request req = generate_ue_rrc_context_creation_request( - ue_index, c_rnti, config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0)); + rnti_t c_rnti = to_rnti(it + 1); // 0 is not a valid RNTI + ue_rrc_context_creation_request req = generate_ue_rrc_context_creation_request( + ue_index_t::invalid, c_rnti, nr_cell_identity::create(gnb_id_t{411, 22}, 0).value()); // Pass message to DU processor - ue_rrc_context_creation_response resp = + ue_rrc_context_creation_outcome outcome = du_processor_obj->get_f1ap_interface().handle_ue_rrc_context_creation_request(req); - ASSERT_NE(resp.f1ap_rrc_notifier, nullptr); + ASSERT_TRUE(outcome.has_value()); } // Reset logger loglevel srslog::fetch_basic_logger("CU-CP").set_level(srslog::basic_levels::debug); + srslog::fetch_basic_logger("RRC").set_level(srslog::basic_levels::debug); srslog::fetch_basic_logger("CU-UEMNG").set_level(srslog::basic_levels::debug); srslog::fetch_basic_logger("TEST").set_level(srslog::basic_levels::debug); - ASSERT_EQ(du_processor_obj->get_statistics_handler().get_nof_ues(), MAX_NOF_UES_PER_DU); + ASSERT_EQ(du_processor_obj->get_statistics_handler().get_nof_ues(), ue_mng.get_ue_config().max_nof_supported_ues); - // Try to allocate additional UE index - ue_index_t ue_index = du_processor_obj->get_f1ap_interface().allocate_new_ue_index(); - ASSERT_EQ(ue_index, ue_index_t::invalid); + // Try to add additional UE + rnti_t c_rnti = to_rnti(ue_mng.get_ue_config().max_nof_supported_ues + 1); + ue_rrc_context_creation_request req = generate_ue_rrc_context_creation_request( + ue_index_t::invalid, c_rnti, nr_cell_identity::create(gnb_id_t{411, 22}, 0).value()); + ue_rrc_context_creation_outcome outcome = + du_processor_obj->get_f1ap_interface().handle_ue_rrc_context_creation_request(req); + ASSERT_TRUE(not outcome.has_value()); - ASSERT_EQ(du_processor_obj->get_statistics_handler().get_nof_ues(), MAX_NOF_UES_PER_DU); + ASSERT_EQ(du_processor_obj->get_statistics_handler().get_nof_ues(), ue_mng.get_ue_config().max_nof_supported_ues); } 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 eb1b6c4d69..849273eb9b 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 @@ -84,7 +84,7 @@ void du_processor_test::attach_ue() // Generate ue_creation message ue_index_t ue_index = ue_index_t::min; ue_rrc_context_creation_request req = generate_ue_rrc_context_creation_request( - ue_index, rnti_t::MIN_CRNTI, config_helpers::make_nr_cell_identity(rrc_cfg.gnb_id, 0)); + ue_index, rnti_t::MIN_CRNTI, nr_cell_identity::create(rrc_cfg.gnb_id, 0).value()); // Pass message to DU processor du_processor_obj->get_f1ap_interface().handle_ue_rrc_context_creation_request(req); } diff --git a/tests/unittests/cu_cp/du_processor/du_processor_test_helpers.h b/tests/unittests/cu_cp/du_processor/du_processor_test_helpers.h index 46a6470246..01a5a8ece0 100644 --- a/tests/unittests/cu_cp/du_processor/du_processor_test_helpers.h +++ b/tests/unittests/cu_cp/du_processor/du_processor_test_helpers.h @@ -55,7 +55,7 @@ class du_processor_test : public ::testing::Test rrc_cfg_t rrc_cfg; timer_manager timers; manual_task_worker ctrl_worker{128}; - ue_configuration ue_config; + ue_configuration ue_config{{}, 8192}; up_resource_manager_cfg up_config; security_manager_config sec_config{{security::integrity_algorithm::nia2, diff --git a/tests/unittests/cu_cp/du_processor_test_messages.cpp b/tests/unittests/cu_cp/du_processor_test_messages.cpp index 6d8c0cea50..7a234d3470 100644 --- a/tests/unittests/cu_cp/du_processor_test_messages.cpp +++ b/tests/unittests/cu_cp/du_processor_test_messages.cpp @@ -32,17 +32,17 @@ using namespace srs_cu_cp; void srsran::srs_cu_cp::generate_valid_f1_setup_request(du_setup_request& setup_request, gnb_du_id_t gnb_du_id, - nr_cell_id_t nrcell_id, + nr_cell_identity nci, pci_t pci) { - f1ap_message f1setup_msg = test_helpers::generate_f1_setup_request(gnb_du_id, nrcell_id, pci); + f1ap_message f1setup_msg = test_helpers::generate_f1_setup_request(gnb_du_id, nci, pci); setup_request = create_du_setup_request(f1setup_msg.pdu.init_msg().value.f1_setup_request()); } void srsran::srs_cu_cp::generate_f1_setup_request_base(du_setup_request& setup_request) { f1ap_message f1setup_msg = test_helpers::generate_f1_setup_request( - int_to_gnb_du_id(0x11), config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0), 0); + int_to_gnb_du_id(0x11), nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(), 0); f1setup_msg.pdu.init_msg().value.f1_setup_request()->gnb_du_served_cells_list_present = false; f1setup_msg.pdu.init_msg().value.f1_setup_request()->gnb_du_served_cells_list.clear(); setup_request = create_du_setup_request(f1setup_msg.pdu.init_msg().value.f1_setup_request()); @@ -58,7 +58,8 @@ f1ap_message srsran::srs_cu_cp::create_f1_setup_request_with_too_many_cells(cons cells.resize(MAX_NOF_DU_CELLS + 1); for (unsigned i = 0; i != cells.size(); ++i) { cells[i].load_info_obj(ASN1_F1AP_ID_GNB_DU_SERVED_CELLS_ITEM); - cells[i]->gnb_du_served_cells_item() = test_helpers::generate_served_cells_item(i, i); + cells[i]->gnb_du_served_cells_item() = + test_helpers::generate_served_cells_item(nr_cell_identity::create(gnb_id_t{411, 22}, i).value(), i); } return msg; @@ -66,9 +67,8 @@ f1ap_message srsran::srs_cu_cp::create_f1_setup_request_with_too_many_cells(cons void srsran::srs_cu_cp::generate_f1_setup_request_with_too_many_cells(du_setup_request& setup_request) { - f1ap_message f1setup_msg = test_helpers::generate_f1_setup_request( - int_to_gnb_du_id(0x11), config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0), 0); - auto& f1_setup_req = f1setup_msg.pdu.init_msg().value.f1_setup_request(); + f1ap_message f1setup_msg = test_helpers::generate_f1_setup_request(); + auto& f1_setup_req = f1setup_msg.pdu.init_msg().value.f1_setup_request(); f1_setup_req->gnb_du_served_cells_list.clear(); f1_setup_req->gnb_du_served_cells_list_present = true; @@ -78,22 +78,21 @@ void srsran::srs_cu_cp::generate_f1_setup_request_with_too_many_cells(du_setup_r f1_setup_req->gnb_du_served_cells_list.push_back({}); f1_setup_req->gnb_du_served_cells_list.back().load_info_obj(ASN1_F1AP_ID_GNB_DU_SERVED_CELLS_ITEM); f1_setup_req->gnb_du_served_cells_list.back()->gnb_du_served_cells_item() = - test_helpers::generate_served_cells_item(du_cell_idx_int, du_cell_idx_int); + test_helpers::generate_served_cells_item(nr_cell_identity::create(gnb_id_t{411, 22}, du_cell_idx_int).value(), + du_cell_idx_int); } setup_request = create_du_setup_request(f1setup_msg.pdu.init_msg().value.f1_setup_request()); } -ue_rrc_context_creation_request -srsran::srs_cu_cp::generate_ue_rrc_context_creation_request(ue_index_t ue_index, rnti_t c_rnti, nr_cell_id_t nrcell_id) +ue_rrc_context_creation_request srsran::srs_cu_cp::generate_ue_rrc_context_creation_request(ue_index_t ue_index, + rnti_t c_rnti, + nr_cell_identity nrcell_id) { ue_rrc_context_creation_request req = {}; req.ue_index = ue_index; req.c_rnti = c_rnti; - req.cgi.mcc = 61441; - req.cgi.mnc = 65281; - req.cgi.plmn = "00101"; - req.cgi.plmn_hex = "00f110"; + req.cgi.plmn_id = plmn_identity::test_value(); req.cgi.nci = nrcell_id; asn1::unbounded_octstring tmp; tmp.from_string( diff --git a/tests/unittests/cu_cp/du_processor_test_messages.h b/tests/unittests/cu_cp/du_processor_test_messages.h index 143e672ff9..7d05e936f0 100644 --- a/tests/unittests/cu_cp/du_processor_test_messages.h +++ b/tests/unittests/cu_cp/du_processor_test_messages.h @@ -26,17 +26,16 @@ #include "srsran/f1ap/common/f1ap_message.h" #include "srsran/f1ap/cu_cp/du_setup_notifier.h" #include "srsran/f1ap/cu_cp/f1ap_cu.h" -#include "srsran/ran/nr_cgi_helpers.h" namespace srsran { namespace srs_cu_cp { /// \brief Generate a valid dummy F1 Setup Request. -void generate_valid_f1_setup_request(du_setup_request& setup_request, - gnb_du_id_t gnb_du_id = int_to_gnb_du_id(0x11), - nr_cell_id_t nrcell_id = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, - 0), - pci_t pci = 0); +void generate_valid_f1_setup_request( + du_setup_request& setup_request, + gnb_du_id_t gnb_du_id = int_to_gnb_du_id(0x11), + nr_cell_identity nrcell_id = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(), + pci_t pci = 0); /// \brief Generate a dummy F1 Setup Request base to extend. void generate_f1_setup_request_base(du_setup_request& setup_request); @@ -51,9 +50,9 @@ void generate_f1_setup_request_with_too_many_cells(du_setup_request& set /// \param[in] nrcell_id The NR Cell Id to use. /// \return The dummy UE Creation Message. ue_rrc_context_creation_request generate_ue_rrc_context_creation_request( - ue_index_t ue_index, - rnti_t c_rnti, - nr_cell_id_t nrcell_id = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0)); + ue_index_t ue_index, + rnti_t c_rnti, + nr_cell_identity nrcell_id = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value()); } // namespace srs_cu_cp } // namespace srsran diff --git a/tests/unittests/cu_cp/metrics_handler/metrics_handler_test.cpp b/tests/unittests/cu_cp/metrics_handler/metrics_handler_test.cpp index a622e29ca2..d716cfd32b 100644 --- a/tests/unittests/cu_cp/metrics_handler/metrics_handler_test.cpp +++ b/tests/unittests/cu_cp/metrics_handler/metrics_handler_test.cpp @@ -60,7 +60,8 @@ TEST(metrics_handler_test, get_periodic_metrics_report_while_session_is_active) metrics_hdlr.next_metrics.ues.emplace_back(metrics_report::ue_info{to_rnti(1), int_to_gnb_du_id(0), pci_t{2}}); metrics_hdlr.next_metrics.dus.emplace_back(metrics_report::du_info{ int_to_gnb_du_id(0), - {metrics_report::cell_info{nr_cell_global_id_t{001, 01, "00101", "00f110", 0x22}, pci_t{2}}}}); + {metrics_report::cell_info{ + nr_cell_global_id_t{plmn_identity::test_value(), nr_cell_identity::create(0x22).value()}, pci_t{2}}}}); for (unsigned i = 0; i != period.count(); ++i) { ASSERT_FALSE(metrics_notifier.last_metrics_report.has_value()); timers.tick(); diff --git a/tests/unittests/cu_cp/mobility/handover_reconfiguration_routine_test.cpp b/tests/unittests/cu_cp/mobility/handover_reconfiguration_routine_test.cpp index 64c458d85b..25e29d5ba0 100644 --- a/tests/unittests/cu_cp/mobility/handover_reconfiguration_routine_test.cpp +++ b/tests/unittests/cu_cp/mobility/handover_reconfiguration_routine_test.cpp @@ -37,14 +37,16 @@ class handover_reconfiguration_routine_test : public mobility_test void create_ues(bool procedure_outcome, unsigned transaction_id_) { - ue_index_t source_ue_index = get_ue_manager()->add_ue(source_du_index); - source_ue = get_ue_manager()->set_ue_du_context(source_ue_index, int_to_gnb_du_id(0), source_pci, source_rnti); + ue_index_t source_ue_index = + get_ue_manager()->add_ue(source_du_index, int_to_gnb_du_id(0), source_pci, source_rnti, du_cell_index_t::min); + source_ue = get_ue_manager()->find_ue(source_ue_index); ASSERT_NE(source_ue, nullptr); source_rrc_ue_notifier.set_transaction_id(transaction_id_); source_ue->set_rrc_ue_notifier(source_rrc_ue_notifier); - ue_index_t target_ue_index = get_ue_manager()->add_ue(target_du_index); - target_ue = get_ue_manager()->set_ue_du_context(target_ue_index, int_to_gnb_du_id(0), target_pci, target_rnti); + ue_index_t target_ue_index = + get_ue_manager()->add_ue(target_du_index, int_to_gnb_du_id(0), target_pci, target_rnti, du_cell_index_t::min); + target_ue = get_ue_manager()->find_ue(target_ue_index); ASSERT_NE(target_ue, nullptr); cu_cp_handler.set_rrc_reconfiguration_outcome(procedure_outcome); target_ue->set_rrc_ue_notifier(target_rrc_ue_notifier); @@ -73,7 +75,7 @@ class handover_reconfiguration_routine_test : public mobility_test private: // source UE parameters. du_index_t source_du_index = uint_to_du_index(0); - unsigned source_pci = 1; + pci_t source_pci = 1; rnti_t source_rnti = to_rnti(0x4601); dummy_du_processor_rrc_ue_control_message_notifier source_rrc_ue_notifier; dummy_f1ap_ue_context_manager source_f1ap_ue_ctxt_mng; @@ -81,7 +83,7 @@ class handover_reconfiguration_routine_test : public mobility_test // target UE parameters. du_index_t target_du_index = uint_to_du_index(1); - unsigned target_pci = 2; + pci_t target_pci = 2; rnti_t target_rnti = to_rnti(0x4601); dummy_du_processor_rrc_ue_control_message_notifier target_rrc_ue_notifier; cu_cp_ue* target_ue = nullptr; diff --git a/tests/unittests/cu_cp/mobility/inter_du_handover_routine_test.cpp b/tests/unittests/cu_cp/mobility/inter_du_handover_routine_test.cpp index 532009e352..8add282064 100644 --- a/tests/unittests/cu_cp/mobility/inter_du_handover_routine_test.cpp +++ b/tests/unittests/cu_cp/mobility/inter_du_handover_routine_test.cpp @@ -21,7 +21,6 @@ */ #include "mobility_test_helpers.h" -#include "srsran/ran/nr_cgi_helpers.h" #include "srsran/support/async/async_test_utils.h" #include "srsran/support/test_utils.h" #include @@ -198,8 +197,8 @@ class inter_du_handover_routine_test : public mobility_test // target DU parameters. du_index_t target_du_index = uint_to_du_index(1); gnb_du_id_t target_du_id = int_to_gnb_du_id(0x22); - nr_cell_id_t target_nrcell_id = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 1); - nr_cell_global_id_t target_cgi = {001, 01, "00101", "00f110", target_nrcell_id}; + nr_cell_identity target_nrcell_id = nr_cell_identity::create(gnb_id_t{411, 22}, 1).value(); + nr_cell_global_id_t target_cgi = {plmn_identity::test_value(), target_nrcell_id}; unsigned target_pci = 2; ue_index_t source_ue_index = uint_to_ue_index(0); diff --git a/tests/unittests/cu_cp/routines/ue_context_release_routine_test.cpp b/tests/unittests/cu_cp/routines/ue_context_release_routine_test.cpp index 444cc4db67..697f70d8fa 100644 --- a/tests/unittests/cu_cp/routines/ue_context_release_routine_test.cpp +++ b/tests/unittests/cu_cp/routines/ue_context_release_routine_test.cpp @@ -45,10 +45,9 @@ class ue_context_release_test : public cu_cp_routine_manager_test ue_index_t add_ue(pci_t pci, rnti_t c_rnti) { - ue_index_t ue_index = ue_mng.add_ue(du_index_t::min); - cu_cp_ue* ue = ue_mng.set_ue_du_context(ue_index, int_to_gnb_du_id(0), pci, c_rnti); + ue_index_t ue_index = ue_mng.add_ue(du_index_t::min, int_to_gnb_du_id(0), pci, c_rnti, du_cell_index_t::min); + cu_cp_ue* ue = ue_mng.find_ue(ue_index); // Set parameters from creation message - ue->set_pcell_index(du_cell_index_t::min); ue->set_rrc_ue_notifier(rrc_ue_ctrl_notifier); ue->set_rrc_ue_srb_notifier(rrc_ue_srb_ctrl_notifier); diff --git a/tests/unittests/cu_cp/ue_manager/ue_manager_test.cpp b/tests/unittests/cu_cp/ue_manager/ue_manager_test.cpp index 1d663416e9..694c85b0fa 100644 --- a/tests/unittests/cu_cp/ue_manager/ue_manager_test.cpp +++ b/tests/unittests/cu_cp/ue_manager/ue_manager_test.cpp @@ -21,7 +21,6 @@ */ #include "ue_manager_test_helpers.h" -#include "srsran/rrc/rrc_ue.h" #include using namespace srsran; @@ -34,13 +33,13 @@ using namespace srs_cu_cp; /// Test allocation of multiple UE indexes TEST_F(ue_manager_test, when_multiple_ue_indexes_allocated_then_ue_indexes_valid) { - du_index_t du_index = uint_to_du_index(0); + du_index_t du_index = du_index_t::min; // reduce log level to avoid flooding the log ue_mng_logger.set_level(srslog::basic_levels::warning); test_logger.set_level(srslog::basic_levels::warning); - for (unsigned it = 0; it < MAX_NOF_UES_PER_DU; it++) { + for (unsigned it = 0; it < ue_mng.get_ue_config().max_nof_supported_ues; it++) { // Check that the ue index is valid ASSERT_NE(ue_mng.add_ue(du_index), ue_index_t::invalid); } @@ -49,13 +48,13 @@ TEST_F(ue_manager_test, when_multiple_ue_indexes_allocated_then_ue_indexes_valid /// Test allocation of unsupported number of UE indexes TEST_F(ue_manager_test, when_more_than_max_ue_indexes_allocated_then_ue_index_invalid) { - du_index_t du_index = uint_to_du_index(0); + du_index_t du_index = du_index_t::min; // reduce log level to avoid flooding the log ue_mng_logger.set_level(srslog::basic_levels::warning); test_logger.set_level(srslog::basic_levels::warning); - for (unsigned it = 0; it < MAX_NOF_UES_PER_DU; it++) { + for (unsigned it = 0; it < ue_mng.get_ue_config().max_nof_supported_ues; it++) { // Check that the ue index is valid ASSERT_NE(ue_mng.add_ue(du_index), ue_index_t::invalid); } @@ -69,13 +68,49 @@ TEST_F(ue_manager_test, when_more_than_max_ue_indexes_allocated_then_ue_index_in } /// Test successful creation of a DU UE -TEST_F(ue_manager_test, when_rnti_valid_then_ue_added) +TEST_F(ue_manager_test, when_valid_du_context_added_at_creation_then_ue_added) { - du_index_t du_index = uint_to_du_index(0); - ue_index_t ue_index = ue_mng.add_ue(du_index); - rnti_t rnti = to_rnti(0x4601); + du_index_t du_index = du_index_t::min; + rnti_t rnti = to_rnti(0x4601); + du_cell_index_t pcell_index = du_cell_index_t::min; + ue_index_t ue_index = ue_mng.add_ue(du_index, gnb_du_id_t::min, MIN_PCI, rnti, pcell_index); + auto* ue = ue_mng.find_ue(ue_index); + + // check that the UE has been created + ASSERT_NE(ue, nullptr); + ASSERT_NE(ue_mng.find_du_ue(ue->get_ue_index()), nullptr); + + // check that the UE index is valid + ASSERT_NE(ue->get_ue_index(), ue_index_t::invalid); + + // check that the gNB-DU ID has been set + ASSERT_NE(ue->get_du_id(), gnb_du_id_t::invalid); + + // check that the PCI has been set + ASSERT_EQ(ue->get_pci(), MIN_PCI); + + // check that the RNTI has been set + ASSERT_EQ(ue->get_c_rnti(), rnti); + + // check that the Pcell index has been set + ASSERT_EQ(ue->get_pcell_index(), pcell_index); + + // check that the lookup by PCI and RNTI works + ASSERT_EQ(ue->get_ue_index(), ue_mng.get_ue_index(ue->get_pci(), ue->get_c_rnti())); + + // check that the number of DU UEs is 1 + ASSERT_EQ(ue_mng.get_nof_du_ues(du_index), 1U); +} + +/// Test successful creation of a DU UE +TEST_F(ue_manager_test, when_du_context_valid_then_ue_updated) +{ + du_index_t du_index = du_index_t::min; + ue_index_t ue_index = ue_mng.add_ue(du_index); + rnti_t rnti = to_rnti(0x4601); + du_cell_index_t pcell_index = du_cell_index_t::min; - auto* ue = ue_mng.set_ue_du_context(ue_index, int_to_gnb_du_id(0), MIN_PCI, rnti); + auto* ue = ue_mng.set_ue_du_context(ue_index, gnb_du_id_t::min, MIN_PCI, rnti, pcell_index); // check that the UE has been created ASSERT_NE(ue, nullptr); @@ -84,12 +119,18 @@ TEST_F(ue_manager_test, when_rnti_valid_then_ue_added) // check that the UE index is valid ASSERT_NE(ue->get_ue_index(), ue_index_t::invalid); + // check that the gNB-DU ID has been set + ASSERT_NE(ue->get_du_id(), gnb_du_id_t::invalid); + // check that the PCI has been set ASSERT_EQ(ue->get_pci(), MIN_PCI); // check that the RNTI has been set ASSERT_EQ(ue->get_c_rnti(), rnti); + // check that the Pcell index has been set + ASSERT_EQ(ue->get_pcell_index(), pcell_index); + // check that the lookup by PCI and RNTI works ASSERT_EQ(ue->get_ue_index(), ue_mng.get_ue_index(ue->get_pci(), ue->get_c_rnti())); @@ -100,11 +141,11 @@ TEST_F(ue_manager_test, when_rnti_valid_then_ue_added) /// Test finding invalid UE index TEST_F(ue_manager_test, when_ue_index_invalid_then_ue_not_found) { - du_index_t du_index = uint_to_du_index(0); - ue_index_t ue_index = ue_mng.add_ue(du_index); - rnti_t rnti = to_rnti(0x4601); - - auto* ue = ue_mng.set_ue_du_context(ue_index, int_to_gnb_du_id(0), MIN_PCI, rnti); + du_index_t du_index = du_index_t::min; + rnti_t rnti = to_rnti(0x4601); + du_cell_index_t pcell_index = du_cell_index_t::min; + ue_index_t ue_index = ue_mng.add_ue(du_index, gnb_du_id_t::min, MIN_PCI, rnti, pcell_index); + auto* ue = ue_mng.find_ue(ue_index); // check that the UE has been created ASSERT_NE(ue, nullptr); @@ -116,16 +157,15 @@ TEST_F(ue_manager_test, when_ue_index_invalid_then_ue_not_found) /// Test duplicate UE creation TEST_F(ue_manager_test, when_rnti_already_exits_then_ue_not_added) { - du_index_t du_index = uint_to_du_index(0); - ue_index_t ue_index = ue_mng.add_ue(du_index); - rnti_t rnti = to_rnti(0x4601); - - ue_mng.set_ue_du_context(ue_index, int_to_gnb_du_id(0), MIN_PCI, rnti); + du_index_t du_index = du_index_t::min; + rnti_t rnti = to_rnti(0x4601); + du_cell_index_t pcell_index = du_cell_index_t::min; + ue_index_t ue_index = ue_mng.add_ue(du_index, gnb_du_id_t::min, MIN_PCI, rnti, pcell_index); // check that the number of DU UEs is 1 ASSERT_EQ(ue_mng.get_nof_du_ues(du_index), 1U); - auto* ue2 = ue_mng.set_ue_du_context(ue_index, int_to_gnb_du_id(0), MIN_PCI, rnti); + auto* ue2 = ue_mng.set_ue_du_context(ue_index, gnb_du_id_t::min, MIN_PCI, rnti, pcell_index); // check that the UE has not been added ASSERT_EQ(ue2, nullptr); @@ -135,11 +175,11 @@ TEST_F(ue_manager_test, when_rnti_already_exits_then_ue_not_added) /// Test successful removal of a DU UE TEST_F(ue_manager_test, when_ue_exists_then_removal_successful) { - du_index_t du_index = uint_to_du_index(0); - ue_index_t ue_index = ue_mng.add_ue(du_index); - rnti_t rnti = to_rnti(0x4601); - - auto* ue = ue_mng.set_ue_du_context(ue_index, int_to_gnb_du_id(0), MIN_PCI, rnti); + du_index_t du_index = du_index_t::min; + rnti_t rnti = to_rnti(0x4601); + du_cell_index_t pcell_index = du_cell_index_t::min; + ue_index_t ue_index = ue_mng.add_ue(du_index, gnb_du_id_t::min, MIN_PCI, rnti, pcell_index); + auto* ue = ue_mng.find_ue(ue_index); ue_mng.remove_ue(ue->get_ue_index()); @@ -150,17 +190,19 @@ TEST_F(ue_manager_test, when_ue_exists_then_removal_successful) /// Test creation of multiple DU UEs TEST_F(ue_manager_test, when_multiple_ues_added_then_ues_exist) { - du_index_t du_index = uint_to_du_index(0); + du_index_t du_index = du_index_t::min; + du_cell_index_t pcell_index = du_cell_index_t::min; // reduce log level to avoid flooding the log ue_mng_logger.set_level(srslog::basic_levels::warning); test_logger.set_level(srslog::basic_levels::warning); - for (unsigned it = to_value(rnti_t::MIN_CRNTI); it < unsigned(to_value(rnti_t::MIN_CRNTI) + MAX_NOF_UES_PER_DU); + for (unsigned it = to_value(rnti_t::MIN_CRNTI); + it < unsigned(to_value(rnti_t::MIN_CRNTI) + ue_mng.get_ue_config().max_nof_supported_ues); it++) { rnti_t rnti = to_rnti(it); - ue_index_t ue_index = ue_mng.add_ue(du_index); - auto* ue = ue_mng.set_ue_du_context(ue_index, int_to_gnb_du_id(0), MIN_PCI, rnti); + ue_index_t ue_index = ue_mng.add_ue(du_index, gnb_du_id_t::min, MIN_PCI, rnti, pcell_index); + auto* ue = ue_mng.find_ue(ue_index); // check that the UE has been created ASSERT_NE(ue, nullptr); @@ -186,23 +228,25 @@ TEST_F(ue_manager_test, when_multiple_ues_added_then_ues_exist) ue_mng_logger.set_level(srslog::basic_levels::debug); // check that the maximum number of DU UEs has been reached - ASSERT_EQ(ue_mng.get_nof_du_ues(du_index), MAX_NOF_UES_PER_DU); + ASSERT_EQ(ue_mng.get_nof_du_ues(du_index), ue_mng.get_ue_config().max_nof_supported_ues); } /// Test creation of unsupported number of DU UEs TEST_F(ue_manager_test, when_more_than_max_ues_added_then_ue_not_created) { - du_index_t du_index = uint_to_du_index(0); + du_index_t du_index = du_index_t::min; + du_cell_index_t pcell_index = du_cell_index_t::min; // reduce log level to avoid flooding the log ue_mng_logger.set_level(srslog::basic_levels::warning); test_logger.set_level(srslog::basic_levels::warning); - for (unsigned it = to_value(rnti_t::MIN_CRNTI); it < unsigned(to_value(rnti_t::MIN_CRNTI) + MAX_NOF_UES_PER_DU); + for (unsigned it = to_value(rnti_t::MIN_CRNTI); + it < unsigned(to_value(rnti_t::MIN_CRNTI) + ue_mng.get_ue_config().max_nof_supported_ues); it++) { rnti_t rnti = to_rnti(it); - ue_index_t ue_index = ue_mng.add_ue(du_index); - auto* ue = ue_mng.set_ue_du_context(ue_index, int_to_gnb_du_id(0), MIN_PCI, rnti); + ue_index_t ue_index = ue_mng.add_ue(du_index, gnb_du_id_t::min, MIN_PCI, rnti, pcell_index); + auto* ue = ue_mng.find_ue(ue_index); // check that the UE has been created ASSERT_NE(ue, nullptr); @@ -229,11 +273,11 @@ TEST_F(ue_manager_test, when_more_than_max_ues_added_then_ue_not_created) test_logger.set_level(srslog::basic_levels::debug); // check that the maximum number of DU UEs has been reached - ASSERT_EQ(ue_mng.get_nof_du_ues(du_index), MAX_NOF_UES_PER_DU); + ASSERT_EQ(ue_mng.get_nof_du_ues(du_index), ue_mng.get_ue_config().max_nof_supported_ues); ue_index_t ue_index = ue_mng.add_ue(du_index); ASSERT_EQ(ue_index, ue_index_t::invalid); // check that the UE has not been added - ASSERT_EQ(ue_mng.get_nof_du_ues(du_index), MAX_NOF_UES_PER_DU); + ASSERT_EQ(ue_mng.get_nof_du_ues(du_index), ue_mng.get_ue_config().max_nof_supported_ues); } diff --git a/tests/unittests/cu_cp/ue_manager/ue_manager_test_helpers.cpp b/tests/unittests/cu_cp/ue_manager/ue_manager_test_helpers.cpp index 5381d162a9..0df56d4c88 100644 --- a/tests/unittests/cu_cp/ue_manager/ue_manager_test_helpers.cpp +++ b/tests/unittests/cu_cp/ue_manager/ue_manager_test_helpers.cpp @@ -21,6 +21,7 @@ */ #include "ue_manager_test_helpers.h" +#include "srsran/cu_cp/cu_cp_types.h" #include #include @@ -39,15 +40,3 @@ ue_manager_test::~ue_manager_test() // flush logger after each test srslog::flush(); } - -ue_index_t ue_manager_test::create_ue(du_index_t du_index, gnb_du_id_t du_id, pci_t pci, rnti_t rnti) -{ - ue_index_t ue_index = ue_mng.add_ue(du_index); - auto* ue = ue_mng.set_ue_du_context(ue_index, du_id, pci, rnti); - if (ue == nullptr) { - test_logger.error("Failed to create UE with pci={} and rnti={}", pci, rnti); - return ue_index_t::invalid; - } - - return ue->get_ue_index(); -} diff --git a/tests/unittests/cu_cp/ue_manager/ue_manager_test_helpers.h b/tests/unittests/cu_cp/ue_manager/ue_manager_test_helpers.h index 4ad5a15b78..8562c82589 100644 --- a/tests/unittests/cu_cp/ue_manager/ue_manager_test_helpers.h +++ b/tests/unittests/cu_cp/ue_manager/ue_manager_test_helpers.h @@ -41,14 +41,13 @@ class ue_manager_test : public ::testing::Test ue_manager_test(); ~ue_manager_test() override; - ue_index_t create_ue(du_index_t du_index, gnb_du_id_t du_id, pci_t pci, rnti_t rnti); - srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); srslog::basic_logger& ue_mng_logger = srslog::fetch_basic_logger("CU-UEMNG"); unsigned max_nof_dus = 6; + unsigned ues_per_du = 1024; - ue_configuration ue_config{std::chrono::seconds{7200}, max_nof_dus* MAX_NOF_UES_PER_DU}; + ue_configuration ue_config{std::chrono::seconds{7200}, max_nof_dus* ues_per_du}; up_resource_manager_cfg up_config; security_manager_config sec_config; timer_manager timers; 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 7926af4f80..669902f86a 100644 --- a/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp +++ b/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp @@ -21,13 +21,12 @@ */ #include "e1ap_cu_cp_test_messages.h" -#include "srsran/ran/nr_cgi_helpers.h" using namespace srsran; using namespace srs_cu_cp; using namespace asn1::e1ap; -asn1::e1ap::supported_plmns_item_s srsran::srs_cu_cp::generate_supported_plmns_item(unsigned nrcell_id) +asn1::e1ap::supported_plmns_item_s srsran::srs_cu_cp::generate_supported_plmns_item(nr_cell_identity nrcell_id) { asn1::e1ap::supported_plmns_item_s supported_plmns_item = {}; supported_plmns_item.plmn_id.from_string("00f110"); @@ -38,7 +37,7 @@ asn1::e1ap::supported_plmns_item_s srsran::srs_cu_cp::generate_supported_plmns_i asn1::e1ap::nr_cgi_support_item_s nr_cgi_support_item; nr_cgi_support_item.nr_cgi.plmn_id.from_string("00f110"); - nr_cgi_support_item.nr_cgi.nr_cell_id.from_number(nrcell_id); + nr_cgi_support_item.nr_cgi.nr_cell_id.from_number(nrcell_id.value()); supported_plmns_item.nr_cgi_support_list.push_back(nr_cgi_support_item); supported_plmns_item.qos_params_support_list_present = false; @@ -69,7 +68,7 @@ e1ap_message srsran::srs_cu_cp::generate_valid_cu_up_e1_setup_request() auto& setup_req = e1_setup_request.pdu.init_msg().value.gnb_cu_up_e1_setup_request(); setup_req->supported_plmns.push_back( - generate_supported_plmns_item(config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0))); + generate_supported_plmns_item(nr_cell_identity::create(gnb_id_t{411, 22}, 0).value())); return e1_setup_request; } @@ -96,7 +95,7 @@ e1ap_bearer_context_setup_request srsran::srs_cu_cp::generate_bearer_context_set request.security_info.security_algorithm.ciphering_algo = srsran::security::ciphering_algorithm::nea0; request.security_info.up_security_key.encryption_key = make_byte_buffer("9950ab8083ed034257d900e9a6a06236").value(); request.ue_dl_aggregate_maximum_bit_rate = 300000000; - request.serving_plmn = "00101"; + request.serving_plmn = plmn_identity::test_value(); request.activity_notif_level = "ue"; e1ap_pdu_session_res_to_setup_item res_to_setup_item; 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 e4929b2e74..9c8054c8dc 100644 --- a/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.h +++ b/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.h @@ -32,7 +32,7 @@ namespace srs_cu_cp { /// \brief Generate dummy Supported PLMNs Item. /// \param[in] nrcell_id The NR Cell Id to use. /// \return The dummy Supported PLMNs Item. -asn1::e1ap::supported_plmns_item_s generate_supported_plmns_item(unsigned nrcell_id); +asn1::e1ap::supported_plmns_item_s generate_supported_plmns_item(nr_cell_identity nrcell_id); /// \brief Generate a dummy CU-CP E1 Setup Request base to extend. /// \return The CU-CP E1 Setup Request base. diff --git a/tests/unittests/e1ap/common/test_helpers.h b/tests/unittests/e1ap/common/test_helpers.h index 3d04194559..e2eb14ba48 100644 --- a/tests/unittests/e1ap/common/test_helpers.h +++ b/tests/unittests/e1ap/common/test_helpers.h @@ -32,6 +32,7 @@ #include "srsran/e1ap/cu_up/e1ap_cu_up.h" #include "srsran/e1ap/cu_up/e1ap_cu_up_bearer_context_update.h" #include "srsran/gateways/network_gateway.h" +#include "srsran/support/async/fifo_async_task_scheduler.h" namespace srsran { @@ -64,7 +65,7 @@ class dummy_e1ap_cu_up_processor_notifier : public srs_cu_cp::e1ap_cu_up_process class dummy_e1ap_cu_up_notifier : public srs_cu_up::e1ap_cu_up_notifier { public: - dummy_e1ap_cu_up_notifier() : logger(srslog::fetch_basic_logger("TEST")) {} + dummy_e1ap_cu_up_notifier() : logger(srslog::fetch_basic_logger("TEST")), task_loop(1024) {} srs_cu_up::e1ap_bearer_context_setup_response on_bearer_context_setup_request_received(const srs_cu_up::e1ap_bearer_context_setup_request& msg) override @@ -102,7 +103,7 @@ class dummy_e1ap_cu_up_notifier : public srs_cu_up::e1ap_cu_up_notifier return response; } - srs_cu_up::e1ap_bearer_context_modification_response on_bearer_context_modification_request_received( + async_task on_bearer_context_modification_request_received( const srs_cu_up::e1ap_bearer_context_modification_request& msg) override { logger.info("Received BearerContextModificationRequest"); @@ -153,15 +154,22 @@ class dummy_e1ap_cu_up_notifier : public srs_cu_up::e1ap_cu_up_notifier response.ue_index = ue_index; response.success = true; - return response; + return launch_async( + [response](coro_context>& ctx) { + CORO_BEGIN(ctx); + CORO_RETURN(response); + }); } void on_bearer_context_release_command_received(const srs_cu_up::e1ap_bearer_context_release_command& msg) override { logger.info("Received BearerContextReleaseCommand"); last_bearer_context_release_command = msg; + } - return; + void on_schedule_ue_async_task(srs_cu_up::ue_index_t ue_index_, async_task task) override + { + task_loop.schedule(std::move(task)); // schedule ue task in dummy task loop } void set_ue_index(uint16_t ue_index_) { ue_index = srs_cu_up::int_to_ue_index(ue_index_); } @@ -173,6 +181,8 @@ class dummy_e1ap_cu_up_notifier : public srs_cu_up::e1ap_cu_up_notifier private: srslog::basic_logger& logger; srs_cu_up::ue_index_t ue_index = srs_cu_up::MIN_UE_INDEX; + + fifo_async_task_scheduler task_loop; }; /// Reusable class implementing the notifier interface. diff --git a/tests/unittests/e1ap/cu_up/e1ap_cu_up_test.cpp b/tests/unittests/e1ap/cu_up/e1ap_cu_up_test.cpp index 9a209f6d84..77b8cfbe20 100644 --- a/tests/unittests/e1ap/cu_up/e1ap_cu_up_test.cpp +++ b/tests/unittests/e1ap/cu_up/e1ap_cu_up_test.cpp @@ -232,3 +232,9 @@ TEST_F(e1ap_cu_up_test, when_valid_bearer_context_release_command_received_then_ ASSERT_EQ(asn1::e1ap::e1ap_elem_procs_o::successful_outcome_c::types_opts::options::bearer_context_release_complete, e1ap_gw.last_tx_e1ap_pdu.pdu.successful_outcome().value.type()); } + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/unittests/f1ap/common/f1ap_asn1_helpers_test.cpp b/tests/unittests/f1ap/common/f1ap_asn1_helpers_test.cpp index d3c6b0ce17..1a46e90cfd 100644 --- a/tests/unittests/f1ap/common/f1ap_asn1_helpers_test.cpp +++ b/tests/unittests/f1ap/common/f1ap_asn1_helpers_test.cpp @@ -23,7 +23,6 @@ #include "lib/f1ap/common/asn1_helpers.h" #include "srsran/asn1/f1ap/f1ap.h" #include "srsran/ran/nr_cgi.h" -#include "srsran/ran/nr_cgi_helpers.h" #include "srsran/ran/up_transport_layer_info.h" #include "srsran/support/test_utils.h" #include @@ -39,30 +38,20 @@ TEST(f1ap_asn1_helpers_test, test_ngi_converter_for_valid_plmn) asn1_cgi.nr_cell_id.from_number(6576); // convert to internal NGI representation - nr_cell_global_id_t ngi = cgi_from_asn1(asn1_cgi); - - ASSERT_TRUE(srsran::config_helpers::is_valid(ngi)); - ASSERT_EQ(0xf001, ngi.mcc); // BCD-encoded MCC - ASSERT_EQ(0xff01, ngi.mnc); // BCD-encoded MNC - ASSERT_EQ("00101", ngi.plmn); // human-readable PLMN - ASSERT_EQ("00f110", ngi.plmn_hex); // hex-encoded PLMN (like above) + nr_cell_global_id_t ngi = cgi_from_asn1(asn1_cgi).value(); + ASSERT_EQ("00101", ngi.plmn_id.to_string()); // human-readable PLMN } TEST(f1ap_asn1_helpers_test, test_ngi_converter_for_invalid_plmn) { // use known a PLMN asn1::f1ap::nr_cgi_s asn1_cgi; - asn1_cgi.plmn_id.from_string("00f000"); // 000.00 + asn1_cgi.plmn_id.from_string("00f00a"); // 000.0a asn1_cgi.nr_cell_id.from_number(6576); // convert to internal NGI representation - nr_cell_global_id_t ngi = cgi_from_asn1(asn1_cgi); - - ASSERT_FALSE(srsran::config_helpers::is_valid(ngi)); - ASSERT_EQ(0xf000, ngi.mcc); // BCD-encoded MCC - ASSERT_EQ(0xff00, ngi.mnc); // BCD-encoded MNC - ASSERT_EQ("00000", ngi.plmn); // human-readable PLMN - ASSERT_EQ("00f000", ngi.plmn_hex); // hex-encoded PLMN (like above) + auto ngi = cgi_from_asn1(asn1_cgi); + ASSERT_FALSE(ngi.has_value()); } static std::string create_random_ipv4_string() diff --git a/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp b/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp index da82e32fd6..8b9c8b1505 100644 --- a/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp +++ b/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp @@ -52,8 +52,8 @@ f1ap_message srsran::srs_cu_cp::generate_init_ul_rrc_message_transfer(gnb_du_ue_ init_ul_rrc_msg_transfer_s& init_ul_rrc = init_ul_rrc_msg.pdu.init_msg().value.init_ul_rrc_msg_transfer(); init_ul_rrc->gnb_du_ue_f1ap_id = (unsigned)du_ue_id; - nr_cell_id_t nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0U); - init_ul_rrc->nr_cgi.nr_cell_id.from_number(nci); + nr_cell_identity nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0U).value(); + init_ul_rrc->nr_cgi.nr_cell_id.from_number(nci.value()); init_ul_rrc->nr_cgi.plmn_id.from_string("00f110"); init_ul_rrc->c_rnti = to_value(crnti); @@ -176,9 +176,9 @@ f1ap_ue_context_modification_request srsran::srs_cu_cp::generate_ue_context_modi // sp cell id nr_cell_global_id_t sp_cell_id; - sp_cell_id.nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); - sp_cell_id.plmn_hex = "00f110"; - msg.sp_cell_id = sp_cell_id; + sp_cell_id.nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); + sp_cell_id.plmn_id = plmn_identity::test_value(); + msg.sp_cell_id = sp_cell_id; // serv cell idx msg.serv_cell_idx = 1; @@ -214,16 +214,16 @@ f1ap_ue_context_modification_request srsran::srs_cu_cp::generate_ue_context_modi // scell to be setup mod list f1ap_scell_to_be_setup_mod_item scell_to_be_setup_mod_item; - scell_to_be_setup_mod_item.scell_id.nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); - scell_to_be_setup_mod_item.scell_id.plmn_hex = "00f110"; - scell_to_be_setup_mod_item.scell_idx = 1; - scell_to_be_setup_mod_item.scell_ul_cfg = f1ap_cell_ul_cfg::ul; + scell_to_be_setup_mod_item.scell_id.nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); + scell_to_be_setup_mod_item.scell_id.plmn_id = plmn_identity::test_value(); + scell_to_be_setup_mod_item.scell_idx = 1; + scell_to_be_setup_mod_item.scell_ul_cfg = f1ap_cell_ul_cfg::ul; msg.scell_to_be_setup_mod_list.push_back(scell_to_be_setup_mod_item); // scell to be remd list f1ap_scell_to_be_remd_item scell_to_be_remd_item; - scell_to_be_remd_item.scell_id.nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); - scell_to_be_remd_item.scell_id.plmn_hex = "00f110"; + scell_to_be_remd_item.scell_id.nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); + scell_to_be_remd_item.scell_id.plmn_id = plmn_identity::test_value(); msg.scell_to_be_remd_list.push_back(scell_to_be_remd_item); // srbs to be setup mod list @@ -393,7 +393,7 @@ f1ap_ue_context_modification_request srsran::srs_cu_cp::generate_ue_context_modi // res coordination transfer info f1ap_res_coordination_transfer_info res_coordination_transfer_info; - res_coordination_transfer_info.m_enb_cell_id = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); + res_coordination_transfer_info.m_enb_cell_id = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); msg.res_coordination_transfer_info = res_coordination_transfer_info; // serving cell mo @@ -463,7 +463,7 @@ cu_cp_paging_message srsran::srs_cu_cp::generate_paging_message() // add tai list for paging cu_cp_tai_list_for_paging_item tai_item; - tai_item.tai.plmn_id = "00f110"; + tai_item.tai.plmn_id = plmn_identity::test_value(); tai_item.tai.tac = 7; paging_msg.tai_list_for_paging.push_back(tai_item); @@ -487,8 +487,8 @@ cu_cp_paging_message srsran::srs_cu_cp::generate_paging_message() cu_cp_recommended_cell_item recommended_cell_item; // add ngran cgi - recommended_cell_item.ngran_cgi.nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); - recommended_cell_item.ngran_cgi.plmn_hex = "00f110"; + recommended_cell_item.ngran_cgi.nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); + recommended_cell_item.ngran_cgi.plmn_id = plmn_identity::test_value(); // add time stayed in cell recommended_cell_item.time_stayed_in_cell = 5; diff --git a/tests/unittests/f1ap/cu_cp/f1ap_cu_paging_test.cpp b/tests/unittests/f1ap/cu_cp/f1ap_cu_paging_test.cpp index f2e8b3f5e9..4aefb4fc43 100644 --- a/tests/unittests/f1ap/cu_cp/f1ap_cu_paging_test.cpp +++ b/tests/unittests/f1ap/cu_cp/f1ap_cu_paging_test.cpp @@ -72,9 +72,9 @@ class f1ap_paging_test : public f1ap_cu_test if (paging_msg->paging_cell_list.size() != 1) { return false; } - auto& paging_cell_item = paging_msg->paging_cell_list[0].value().paging_cell_item(); - nr_cell_id_t nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); - if (paging_cell_item.nr_cgi.nr_cell_id.to_number() != nci) { + auto& paging_cell_item = paging_msg->paging_cell_list[0].value().paging_cell_item(); + nr_cell_identity nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); + if (paging_cell_item.nr_cgi.nr_cell_id.to_number() != nci.value()) { test_logger.error("NR CGI NCI mismatch {} != {}}", paging_cell_item.nr_cgi.nr_cell_id.to_number(), nci); return false; } diff --git a/tests/unittests/f1ap/cu_cp/f1ap_cu_test.cpp b/tests/unittests/f1ap/cu_cp/f1ap_cu_test.cpp index 108f8826dd..d372b24188 100644 --- a/tests/unittests/f1ap/cu_cp/f1ap_cu_test.cpp +++ b/tests/unittests/f1ap/cu_cp/f1ap_cu_test.cpp @@ -42,7 +42,7 @@ static du_setup_result create_du_setup_result_accept(const f1ap_message& f1_msg) accepted.cells_to_be_activ_list.resize(cells.size()); for (unsigned i = 0; i != cells.size(); ++i) { auto& cell = accepted.cells_to_be_activ_list[i]; - cell.nr_cgi = cgi_from_asn1(cells[i]->gnb_du_served_cells_item().served_cell_info.nr_cgi); + cell.nr_cgi = cgi_from_asn1(cells[i]->gnb_du_served_cells_item().served_cell_info.nr_cgi).value(); cell.nr_pci = cells[i]->gnb_du_served_cells_item().served_cell_info.nr_pci; } return resp; @@ -189,7 +189,7 @@ TEST_F(f1ap_cu_test, when_cgi_invalid_then_ue_not_added) // Generate F1 Initial UL RRC Message f1ap_message init_ul_rrc_msg = generate_init_ul_rrc_message_transfer(int_to_gnb_du_ue_f1ap_id(41255)); // Set PLMN to invalid value - init_ul_rrc_msg.pdu.init_msg().value.init_ul_rrc_msg_transfer()->nr_cgi.plmn_id.from_number(0); + init_ul_rrc_msg.pdu.init_msg().value.init_ul_rrc_msg_transfer()->nr_cgi.plmn_id.from_number(0xa); // Pass message to F1AP f1ap->handle_message(init_ul_rrc_msg); @@ -210,16 +210,16 @@ TEST_F(f1ap_cu_test, when_rnti_invalid_then_ue_not_added) EXPECT_EQ(f1ap->get_nof_ues(), 0); } -TEST_F(f1ap_cu_test, when_max_nof_ues_PER_DU_exceeded_then_ue_not_added) +TEST_F(f1ap_cu_test, when_max_nof_ues_exceeded_then_ue_not_added) { // Reduce F1AP and TEST logger loglevel to warning to reduce console output srslog::fetch_basic_logger("CU-CP-F1").set_level(srslog::basic_levels::warning); srslog::fetch_basic_logger("TEST").set_level(srslog::basic_levels::warning); // Add the maximum number of UEs - for (int ue_index = 0; ue_index < MAX_NOF_UES_PER_DU; ue_index++) { + for (unsigned du_ue_id = 0; du_ue_id < max_nof_ues; du_ue_id++) { // Generate ue_creation message - f1ap_message init_ul_rrc_msg = generate_init_ul_rrc_message_transfer(int_to_gnb_du_ue_f1ap_id(ue_index)); + f1ap_message init_ul_rrc_msg = generate_init_ul_rrc_message_transfer(int_to_gnb_du_ue_f1ap_id(du_ue_id)); // Pass message to F1AP f1ap->handle_message(init_ul_rrc_msg); @@ -229,23 +229,22 @@ TEST_F(f1ap_cu_test, when_max_nof_ues_PER_DU_exceeded_then_ue_not_added) srslog::fetch_basic_logger("CU-CP-F1").set_level(srslog::basic_levels::debug); srslog::fetch_basic_logger("TEST").set_level(srslog::basic_levels::debug); - EXPECT_EQ(f1ap->get_nof_ues(), MAX_NOF_UES_PER_DU); + EXPECT_EQ(f1ap->get_nof_ues(), max_nof_ues); // Add one more UE to F1AP // Generate ue_creation message - f1ap_message init_ul_rrc_msg = - generate_init_ul_rrc_message_transfer(int_to_gnb_du_ue_f1ap_id(MAX_NOF_UES_PER_DU + 1)); + f1ap_message init_ul_rrc_msg = generate_init_ul_rrc_message_transfer(int_to_gnb_du_ue_f1ap_id(max_nof_ues + 1)); // Pass message to F1AP f1ap->handle_message(init_ul_rrc_msg); - EXPECT_EQ(f1ap->get_nof_ues(), MAX_NOF_UES_PER_DU); + EXPECT_EQ(f1ap->get_nof_ues(), max_nof_ues); } TEST_F(f1ap_cu_test, when_ue_creation_fails_then_ue_not_added) { // Add maximum number of UEs to dummy DU processor - du_processor_notifier.set_ue_id(MAX_NOF_UES_PER_DU); + du_processor_notifier.set_ue_id(max_nof_ues); // Add one more UE to F1AP // Generate F1 Initial UL RRC Message @@ -254,22 +253,10 @@ TEST_F(f1ap_cu_test, when_ue_creation_fails_then_ue_not_added) // Pass message to F1AP f1ap->handle_message(init_ul_rrc_msg); + EXPECT_TRUE(was_rrc_reject_sent()); EXPECT_EQ(f1ap->get_nof_ues(), 0); } -TEST_F(f1ap_cu_test, when_rrc_setup_complete_present_then_forward_over_srb1) -{ - // Generate F1 Initial UL RRC Message with RRC Setup Complete present - f1ap_message init_ul_rrc_msg = generate_init_ul_rrc_message_transfer(int_to_gnb_du_ue_f1ap_id(41255)); - auto& init_ul_rrc = init_ul_rrc_msg.pdu.init_msg().value.init_ul_rrc_msg_transfer(); - init_ul_rrc->rrc_container_rrc_setup_complete_present = true; - - // Pass message to F1AP - f1ap->handle_message(init_ul_rrc_msg); - - EXPECT_EQ(du_processor_notifier.f1ap_rrc_notifier->last_ul_ccch_pdu, init_ul_rrc->rrc_container_rrc_setup_complete); -} - ////////////////////////////////////////////////////////////////////////////////////// /* F1 Removal Request handling */ ////////////////////////////////////////////////////////////////////////////////////// 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 00ab3d917a..2af098f2d2 100644 --- a/tests/unittests/f1ap/cu_cp/f1ap_cu_test_helpers.cpp +++ b/tests/unittests/f1ap/cu_cp/f1ap_cu_test_helpers.cpp @@ -22,7 +22,10 @@ #include "f1ap_cu_test_helpers.h" #include "srsran/asn1/f1ap/f1ap_pdu_contents_ue.h" +#include "srsran/asn1/rrc_nr/dl_ccch_msg.h" +#include "srsran/asn1/rrc_nr/dl_ccch_msg_ies.h" #include "srsran/cu_cp/cu_cp_types.h" +#include "srsran/f1ap/cu_cp/f1ap_cu_factory.h" #include "srsran/ran/five_qi.h" #include "srsran/ran/nr_cgi.h" #include "srsran/support/async/async_test_utils.h" @@ -98,6 +101,28 @@ f1ap_cu_test::test_ue& f1ap_cu_test::run_ue_context_setup() return test_ues[ue_index]; } +bool f1ap_cu_test::was_rrc_reject_sent() +{ + // Make sure that the last message was a DLRRCMessageTransfer + if (f1ap_pdu_notifier.last_f1ap_msg.pdu.type().value != asn1::f1ap::f1ap_pdu_c::types::init_msg) { + return false; + } + if (f1ap_pdu_notifier.last_f1ap_msg.pdu.init_msg().value.type().value != + asn1::f1ap::f1ap_elem_procs_o::init_msg_c::types::dl_rrc_msg_transfer) { + return false; + } + + // Make sure that the DLRRCMessageTransfer contains an RRC Reject. + asn1::rrc_nr::dl_ccch_msg_s ccch; + { + asn1::cbit_ref bref{f1ap_pdu_notifier.last_f1ap_msg.pdu.init_msg().value.dl_rrc_msg_transfer()->rrc_container}; + if (ccch.unpack(bref) != asn1::SRSASN_SUCCESS) { + return false; + } + } + return ccch.msg.c1().type().value == asn1::rrc_nr::dl_ccch_msg_type_c::c1_c_::types_opts::rrc_reject; +} + void f1ap_cu_test::tick() { this->timers.tick(); @@ -112,8 +137,8 @@ srsran::srs_cu_cp::create_ue_context_setup_request(const std::initializer_list @@ -125,7 +124,10 @@ class dummy_f1ap_rrc_message_notifier : public srs_cu_cp::f1ap_rrc_message_notif class dummy_f1ap_du_processor_notifier : public srs_cu_cp::f1ap_du_processor_notifier { public: - dummy_f1ap_du_processor_notifier() : logger(srslog::fetch_basic_logger("TEST")) {} + dummy_f1ap_du_processor_notifier(const unsigned max_nof_supported_ues_) : + max_nof_supported_ues(max_nof_supported_ues_), logger(srslog::fetch_basic_logger("TEST")) + { + } du_setup_result on_new_du_setup_request(const du_setup_request& msg) override { @@ -134,7 +136,7 @@ class dummy_f1ap_du_processor_notifier : public srs_cu_cp::f1ap_du_processor_not return next_du_setup_resp; } - srs_cu_cp::ue_rrc_context_creation_response + srs_cu_cp::ue_rrc_context_creation_outcome on_ue_rrc_context_creation_request(const ue_rrc_context_creation_request& msg) override { logger.info("Received {}", __FUNCTION__); @@ -143,16 +145,23 @@ class dummy_f1ap_du_processor_notifier : public srs_cu_cp::f1ap_du_processor_not last_ue_creation_msg.du_to_cu_rrc_container = msg.du_to_cu_rrc_container.copy(); last_ue_creation_msg.c_rnti = msg.c_rnti; - srs_cu_cp::ue_rrc_context_creation_response ret = {}; - ret.f1ap_rrc_notifier = f1ap_rrc_notifier.get(); + srs_cu_cp::ue_rrc_context_creation_response response{msg.ue_index, f1ap_rrc_notifier.get()}; + if (msg.ue_index == ue_index_t::invalid) { + response.ue_index = on_new_cu_cp_ue_required(); + } - return ret; + // Return failure if no UE index is available. + if (response.ue_index == ue_index_t::invalid) { + return make_unexpected(byte_buffer::create({0x0, 0x0}).value()); + } + + return response; } - ue_index_t on_new_cu_cp_ue_required() override + ue_index_t on_new_cu_cp_ue_required() { ue_index_t ue_index = srs_cu_cp::ue_index_t::invalid; - if (ue_id < srs_cu_cp::MAX_NOF_UES_PER_DU) { + if (ue_id < max_nof_supported_ues) { ue_index = srs_cu_cp::uint_to_ue_index(ue_id); last_created_ue_index = ue_index; ue_id++; @@ -188,6 +197,7 @@ class dummy_f1ap_du_processor_notifier : public srs_cu_cp::f1ap_du_processor_not std::make_unique(); private: + const unsigned max_nof_supported_ues; srslog::basic_logger& logger; uint16_t ue_id = ue_index_to_uint(srs_cu_cp::ue_index_t::min); fifo_async_task_scheduler task_sched{16}; @@ -215,6 +225,8 @@ class f1ap_cu_test : public ::testing::Test /// \brief Helper method to run F1AP CU UE Context Setup procedure to completion for a given UE. test_ue& run_ue_context_setup(); + bool was_rrc_reject_sent(); + void tick(); srslog::basic_logger& f1ap_logger = srslog::fetch_basic_logger("CU-CP-F1"); @@ -222,8 +234,10 @@ class f1ap_cu_test : public ::testing::Test std::unordered_map test_ues; + const unsigned max_nof_ues = 8192; + dummy_f1ap_pdu_notifier f1ap_pdu_notifier; - dummy_f1ap_du_processor_notifier du_processor_notifier; + dummy_f1ap_du_processor_notifier du_processor_notifier{max_nof_ues}; timer_manager timers; manual_task_worker ctrl_worker{128}; std::unique_ptr f1ap; diff --git a/tests/unittests/f1ap/cu_cp/f1ap_cu_ue_context_test.cpp b/tests/unittests/f1ap/cu_cp/f1ap_cu_ue_context_test.cpp index 385527b29e..8d8294c137 100644 --- a/tests/unittests/f1ap/cu_cp/f1ap_cu_ue_context_test.cpp +++ b/tests/unittests/f1ap/cu_cp/f1ap_cu_ue_context_test.cpp @@ -100,30 +100,6 @@ TEST_F(f1ap_cu_ue_context_test, when_ue_not_added_then_ue_doesnt_exist) ASSERT_FALSE(ue_ctxt_list.contains(ue_index)); } -TEST_F(f1ap_cu_ue_context_test, when_unsupported_number_of_ues_addeded_then_ue_not_added) -{ - // Reduce F1AP logger loglevel to warning to reduce console output - f1ap_logger.set_level(srslog::basic_levels::warning); - - // Add maximum number of supported UEs - for (unsigned it = 0; it < MAX_NOF_UES_PER_DU; ++it) { - gnb_cu_ue_f1ap_id_t cu_ue_f1ap_id = ue_ctxt_list.next_gnb_cu_ue_f1ap_id(); - ASSERT_NE(cu_ue_f1ap_id, gnb_cu_ue_f1ap_id_t::invalid); - ue_index_t ue_index = uint_to_ue_index(it); - - ue_ctxt_list.add_ue(ue_index, cu_ue_f1ap_id); - - ASSERT_TRUE(ue_ctxt_list.contains(cu_ue_f1ap_id)); - ASSERT_TRUE(ue_ctxt_list.contains(ue_index)); - } - - // Reset F1AP logger loglevel - f1ap_logger.set_level(srslog::basic_levels::debug); - - // Try to get another cu_ue_f1ap_id (should fail) - ASSERT_EQ(ue_ctxt_list.next_gnb_cu_ue_f1ap_id(), gnb_cu_ue_f1ap_id_t::invalid); -} - TEST_F(f1ap_cu_ue_context_test, when_ue_exists_then_removal_succeeds) { ue_index_t ue_index = generate_random_ue_index(); @@ -141,7 +117,7 @@ TEST_F(f1ap_cu_ue_context_test, when_ue_exists_then_removal_succeeds) TEST_F(f1ap_cu_ue_context_test, when_ue_is_added_then_next_ue_id_is_increased) { ue_index_t ue_index = generate_random_ue_index(); - gnb_cu_ue_f1ap_id_t cu_ue_f1ap_id = ue_ctxt_list.next_gnb_cu_ue_f1ap_id(); + gnb_cu_ue_f1ap_id_t cu_ue_f1ap_id = ue_ctxt_list.allocate_gnb_cu_ue_f1ap_id(); ASSERT_EQ((unsigned)cu_ue_f1ap_id, (unsigned)gnb_cu_ue_f1ap_id_t::min); @@ -153,45 +129,5 @@ TEST_F(f1ap_cu_ue_context_test, when_ue_is_added_then_next_ue_id_is_increased) ASSERT_FALSE(ue_ctxt_list.contains(cu_ue_f1ap_id)); ASSERT_FALSE(ue_ctxt_list.contains(ue_index)); - ASSERT_EQ((unsigned)ue_ctxt_list.next_gnb_cu_ue_f1ap_id(), (unsigned)gnb_cu_ue_f1ap_id_t::min + 1); -} - -TEST_F(f1ap_cu_ue_context_test, when_next_ue_id_reaches_max_then_unused_values_are_reused) -{ - // Reduce F1AP logger loglevel to warning to reduce console output - f1ap_logger.set_level(srslog::basic_levels::warning); - - // Add one less than the maximum number of supported UEs - for (unsigned it = 0; it < MAX_NOF_UES_PER_DU - 1; ++it) { - gnb_cu_ue_f1ap_id_t cu_ue_f1ap_id = ue_ctxt_list.next_gnb_cu_ue_f1ap_id(); - ASSERT_NE(cu_ue_f1ap_id, gnb_cu_ue_f1ap_id_t::invalid); - ue_index_t ue_index = uint_to_ue_index(it); - - ue_ctxt_list.add_ue(ue_index, cu_ue_f1ap_id); - - ASSERT_TRUE(ue_ctxt_list.contains(cu_ue_f1ap_id)); - ASSERT_TRUE(ue_ctxt_list.contains(ue_index)); - } - - // Reset F1AP logger loglevel - f1ap_logger.set_level(srslog::basic_levels::debug); - - // set next cu ue f1ap id to maximum value - ue_ctxt_list.set_next_cu_ue_f1ap_id(gnb_cu_ue_f1ap_id_t::max); - ASSERT_EQ((uint64_t)ue_ctxt_list.allocate_cu_ue_f1ap_id(), (uint64_t)gnb_cu_ue_f1ap_id_t::max); - - // Add ue with max cu ue f1ap id to let next cu ue f1ap id overflow - ue_ctxt_list.add_ue(ue_index_t::max, gnb_cu_ue_f1ap_id_t::max); - - ASSERT_TRUE(ue_ctxt_list.contains(gnb_cu_ue_f1ap_id_t::max)); - ASSERT_TRUE(ue_ctxt_list.contains(ue_index_t::max)); - - // remove an ue from the context list - gnb_cu_ue_f1ap_id_t rem_ue_id = int_to_gnb_cu_ue_f1ap_id(19); - ASSERT_TRUE(ue_ctxt_list.contains(rem_ue_id)); - ue_ctxt_list.remove_ue(ue_ctxt_list[rem_ue_id].ue_ids.ue_index); - ASSERT_FALSE(ue_ctxt_list.contains(rem_ue_id)); - - // Next available cu ue f1ap id should be the removed one - ASSERT_EQ((uint64_t)ue_ctxt_list.next_gnb_cu_ue_f1ap_id(), (uint64_t)rem_ue_id); + ASSERT_EQ((unsigned)ue_ctxt_list.allocate_gnb_cu_ue_f1ap_id(), (unsigned)gnb_cu_ue_f1ap_id_t::min + 1); } diff --git a/tests/unittests/f1ap/du/f1ap_du_test_helpers.h b/tests/unittests/f1ap/du/f1ap_du_test_helpers.h index 1e413b2122..e25a32613e 100644 --- a/tests/unittests/f1ap/du/f1ap_du_test_helpers.h +++ b/tests/unittests/f1ap/du/f1ap_du_test_helpers.h @@ -188,9 +188,14 @@ class dummy_f1u_rx_sdu_notifier : public f1u_rx_sdu_notifier { public: byte_buffer last_pdu; + bool last_pdu_is_retx; std::optional last_discard_sn; - void on_new_sdu(byte_buffer sdu) override { last_pdu = std::move(sdu); } + void on_new_sdu(byte_buffer sdu, bool is_retx) override + { + last_pdu = std::move(sdu); + last_pdu_is_retx = is_retx; + } void on_discard_sdu(uint32_t pdcp_sn) override { last_discard_sn = pdcp_sn; } }; diff --git a/tests/unittests/f1ap/du/f1ap_du_ue_context_modification_test.cpp b/tests/unittests/f1ap/du/f1ap_du_ue_context_modification_test.cpp index b7379c0c3e..5dad262036 100644 --- a/tests/unittests/f1ap/du/f1ap_du_ue_context_modification_test.cpp +++ b/tests/unittests/f1ap/du/f1ap_du_ue_context_modification_test.cpp @@ -21,6 +21,7 @@ */ #include "f1ap_du_test_helpers.h" +#include "srsran/asn1/f1ap/f1ap_pdu_contents_ue.h" #include "srsran/support/test_utils.h" #include diff --git a/tests/unittests/f1u/common/f1u_connector_test.cpp b/tests/unittests/f1u/common/f1u_connector_test.cpp index 96856f6aac..92ed8d8562 100644 --- a/tests/unittests/f1u/common/f1u_connector_test.cpp +++ b/tests/unittests/f1u/common/f1u_connector_test.cpp @@ -55,13 +55,15 @@ struct dummy_f1u_du_gateway_bearer_rx_notifier final : srsran::srs_du::f1u_du_ga // dummy DU RX bearer interface struct dummy_f1u_du_rx_sdu_notifier final : public srs_du::f1u_rx_sdu_notifier { - void on_new_sdu(byte_buffer sdu) override + void on_new_sdu(byte_buffer sdu, bool is_retx) override { - logger.info(sdu.begin(), sdu.end(), "DU received SDU. sdu_len={}", sdu.length()); - last_sdu = std::move(sdu); + logger.info(sdu.begin(), sdu.end(), "DU received SDU. sdu_len={} is_retx={}", sdu.length(), is_retx); + last_sdu = std::move(sdu); + last_sdu_is_retx = is_retx; } void on_discard_sdu(uint32_t pdcp_sn) override {} byte_buffer last_sdu; + bool last_sdu_is_retx; private: srslog::basic_logger& logger = srslog::fetch_basic_logger("CU-F1-U", false); @@ -124,7 +126,7 @@ void check_dl_path_connected(const byte_buffer& cu_buf, dummy_f1u_du_gateway_bearer_rx_notifier& du_rx) { auto du_exp_buf = byte_buffer_chain::create(cu_buf.deep_copy().value()); - ASSERT_FALSE(du_exp_buf.is_error()); + ASSERT_TRUE(du_exp_buf.has_value()); nru_dl_message du_exp = {}; du_exp.t_pdu = cu_buf.deep_copy().value(); nru_dl_message sdu = {}; @@ -176,13 +178,13 @@ void check_dl_path_disconnected(const byte_buffer& cu_buf, expected make_nru_ul_message(const byte_buffer& du_buf) { expected buf_cpy = du_buf.deep_copy(); - if (buf_cpy.is_error()) { - return default_error_t{}; + if (not buf_cpy.has_value()) { + return make_unexpected(default_error_t{}); } expected du_chain_buf = byte_buffer_chain::create(std::move(buf_cpy.value())); - if (du_chain_buf.is_error()) { - return default_error_t{}; + if (not du_chain_buf.has_value()) { + return make_unexpected(default_error_t{}); } nru_ul_message msg = {}; @@ -197,10 +199,10 @@ void check_ul_path_disconnected(const byte_buffer& du_buf, { srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false); expected sdu = make_nru_ul_message(du_buf); - ASSERT_FALSE(sdu.is_error()); + ASSERT_TRUE(sdu.has_value()); expected cu_not_exp = make_nru_ul_message(du_buf); - ASSERT_FALSE(cu_not_exp.is_error()); + ASSERT_TRUE(cu_not_exp.has_value()); // logger.info("Testing equallity last_sdu T-PDU = {}", *sdu.value().t_pdu->begin()); logger.info("Testing equallity last_sdu T-PDU = {}", cu_not_exp.value() == sdu.value()); diff --git a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp index 0a31128ab9..04609c0026 100644 --- a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp +++ b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp @@ -73,7 +73,7 @@ class dummy_f1u_cu_up_rx_notifier : public f1u_cu_up_gateway_bearer_rx_notifier } if (msg_queue.empty()) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } nru_ul_message pdu = std::move(msg_queue.front()); @@ -314,11 +314,11 @@ TEST_F(f1u_cu_split_connector_test, recv_sdu_with_dl_teid_attached) // Blocking waiting for RX expected rx_sdu = cu_rx.get_rx_pdu_blocking(ue_worker); - ASSERT_FALSE(rx_sdu.is_error()); + ASSERT_TRUE(rx_sdu.has_value()); ASSERT_TRUE(rx_sdu.value().t_pdu.has_value()); expected exp_buf = make_byte_buffer("abcd"); - ASSERT_FALSE(exp_buf.is_error()); + ASSERT_TRUE(exp_buf.has_value()); ASSERT_EQ(rx_sdu.value().t_pdu->length(), exp_buf.value().length()); ASSERT_EQ(rx_sdu.value().t_pdu.value(), exp_buf.value()); } @@ -344,11 +344,11 @@ TEST_F(f1u_cu_split_connector_test, recv_sdu_without_dl_teid_attached) // Blocking waiting for RX expected rx_sdu = cu_rx.get_rx_pdu_blocking(ue_worker); - ASSERT_FALSE(rx_sdu.is_error()); + ASSERT_TRUE(rx_sdu.has_value()); ASSERT_TRUE(rx_sdu.value().t_pdu.has_value()); expected exp_buf = make_byte_buffer("abcd"); - ASSERT_FALSE(exp_buf.is_error()); + ASSERT_TRUE(exp_buf.has_value()); ASSERT_EQ(rx_sdu.value().t_pdu->length(), exp_buf.value().length()); ASSERT_EQ(rx_sdu.value().t_pdu.value(), exp_buf.value()); } @@ -434,11 +434,11 @@ TEST_F(f1u_cu_split_connector_test, destroy_bearer_disconnects_and_stops_rx) // Blocking waiting for RX expected rx_sdu1 = cu_rx.get_rx_pdu_blocking(ue_worker); - ASSERT_FALSE(rx_sdu1.is_error()); + ASSERT_TRUE(rx_sdu1.has_value()); ASSERT_TRUE(rx_sdu1.value().t_pdu.has_value()); expected exp_buf1 = make_byte_buffer("abcd"); - ASSERT_FALSE(exp_buf1.is_error()); + ASSERT_TRUE(exp_buf1.has_value()); ASSERT_EQ(rx_sdu1.value().t_pdu->length(), exp_buf1.value().length()); ASSERT_EQ(rx_sdu1.value().t_pdu.value(), exp_buf1.value()); @@ -452,7 +452,7 @@ TEST_F(f1u_cu_split_connector_test, destroy_bearer_disconnects_and_stops_rx) // Blocking waiting for RX expected rx_sdu2 = cu_rx.get_rx_pdu_blocking(ue_worker); - ASSERT_TRUE(rx_sdu2.is_error()); + ASSERT_FALSE(rx_sdu2.has_value()); } int main(int argc, char** argv) diff --git a/tests/unittests/f1u/common/f1u_du_split_connector_test.cpp b/tests/unittests/f1u/common/f1u_du_split_connector_test.cpp index 0269c77e95..83d9e8c1b3 100644 --- a/tests/unittests/f1u/common/f1u_du_split_connector_test.cpp +++ b/tests/unittests/f1u/common/f1u_du_split_connector_test.cpp @@ -60,7 +60,7 @@ struct dummy_f1u_du_gateway_bearer_rx_notifier final : srsran::srs_du::f1u_du_ga } } if (msg_queue.empty()) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } nru_dl_message pdu = std::move(msg_queue.front()); msg_queue.pop(); @@ -271,7 +271,7 @@ TEST_F(f1u_du_split_connector_test, recv_sdu) // Blocking waiting for RX expected rx_sdu = du_rx.get_rx_pdu_blocking(ue_worker); - ASSERT_FALSE(rx_sdu.is_error()); + ASSERT_TRUE(rx_sdu.has_value()); expected exp_buf = make_byte_buffer("abcd"); ASSERT_TRUE(exp_buf.has_value()); @@ -359,7 +359,7 @@ TEST_F(f1u_du_split_connector_test, destroy_bearer_disconnects_and_stops_rx) // Blocking waiting for RX expected rx_sdu1 = du_rx.get_rx_pdu_blocking(ue_worker); - ASSERT_FALSE(rx_sdu1.is_error()); + ASSERT_TRUE(rx_sdu1.has_value()); expected exp_buf1 = make_byte_buffer("abcd"); ASSERT_TRUE(exp_buf1.has_value()); @@ -376,7 +376,7 @@ TEST_F(f1u_du_split_connector_test, destroy_bearer_disconnects_and_stops_rx) // Blocking waiting for RX expected rx_sdu = du_rx.get_rx_pdu_blocking(ue_worker); - ASSERT_TRUE(rx_sdu.is_error()); + ASSERT_FALSE(rx_sdu.has_value()); } int main(int argc, char** argv) diff --git a/tests/unittests/f1u/cu_up/f1u_cu_up_bearer_test.cpp b/tests/unittests/f1u/cu_up/f1u_cu_up_bearer_test.cpp index 1214080680..3f47e60cc3 100644 --- a/tests/unittests/f1u/cu_up/f1u_cu_up_bearer_test.cpp +++ b/tests/unittests/f1u/cu_up/f1u_cu_up_bearer_test.cpp @@ -36,6 +36,8 @@ class f1u_cu_up_test_frame : public f1u_tx_pdu_notifier, public f1u_rx_delivery_ std::list tx_msg_list; std::list highest_transmitted_pdcp_sn_list; std::list highest_delivered_pdcp_sn_list; + std::list highest_retransmitted_pdcp_sn_list; + std::list highest_delivered_retransmitted_pdcp_sn_list; std::list rx_sdu_list; // f1u_tx_pdu_notifier interface @@ -50,6 +52,14 @@ class f1u_cu_up_test_frame : public f1u_tx_pdu_notifier, public f1u_rx_delivery_ { highest_delivered_pdcp_sn_list.push_back(highest_pdcp_sn); } + void on_retransmit_notification(uint32_t highest_pdcp_sn) override + { + highest_retransmitted_pdcp_sn_list.push_back(highest_pdcp_sn); + } + void on_delivery_retransmitted_notification(uint32_t highest_pdcp_sn) override + { + highest_delivered_retransmitted_pdcp_sn_list.push_back(highest_pdcp_sn); + } // f1u_rx_sdu_notifier interface void on_new_sdu(byte_buffer_chain sdu) override { rx_sdu_list.push_back(std::move(sdu)); } @@ -175,7 +185,7 @@ TEST_F(f1u_cu_up_test, tx_discard) byte_buffer tx_pdcp_pdu1 = create_sdu_byte_buffer(pdu_size, 0xcc); // transmit a PDU to piggy-back previous discard - f1u->handle_sdu(tx_pdcp_pdu1.deep_copy().value()); + f1u->handle_sdu(tx_pdcp_pdu1.deep_copy().value(), /* is_retx = */ false); EXPECT_TRUE(tester->highest_transmitted_pdcp_sn_list.empty()); EXPECT_TRUE(tester->highest_delivered_pdcp_sn_list.empty()); @@ -184,6 +194,7 @@ TEST_F(f1u_cu_up_test, tx_discard) ASSERT_FALSE(tester->tx_msg_list.empty()); EXPECT_FALSE(tester->tx_msg_list.front().t_pdu.empty()); EXPECT_EQ(tester->tx_msg_list.front().t_pdu, tx_pdcp_pdu1); + EXPECT_FALSE(tester->tx_msg_list.front().dl_user_data.retransmission_flag); ASSERT_TRUE(tester->tx_msg_list.front().dl_user_data.discard_blocks.has_value()); ASSERT_EQ(tester->tx_msg_list.front().dl_user_data.discard_blocks.value().size(), 2); EXPECT_EQ(tester->tx_msg_list.front().dl_user_data.discard_blocks.value()[0].pdcp_sn_start, pdcp_sn); @@ -230,10 +241,13 @@ TEST_F(f1u_cu_up_test, tx_pdcp_pdus) constexpr uint32_t pdcp_sn = 123; byte_buffer tx_pdcp_pdu1 = create_sdu_byte_buffer(pdu_size, pdcp_sn); - f1u->handle_sdu(tx_pdcp_pdu1.deep_copy().value()); + f1u->handle_sdu(tx_pdcp_pdu1.deep_copy().value(), /* is_retx = */ false); byte_buffer tx_pdcp_pdu2 = create_sdu_byte_buffer(pdu_size, pdcp_sn + 1); - f1u->handle_sdu(tx_pdcp_pdu2.deep_copy().value()); + f1u->handle_sdu(tx_pdcp_pdu2.deep_copy().value(), /* is_retx = */ false); + + // Also check a ReTx + f1u->handle_sdu(tx_pdcp_pdu2.deep_copy().value(), /* is_retx = */ true); EXPECT_TRUE(tester->highest_transmitted_pdcp_sn_list.empty()); EXPECT_TRUE(tester->highest_delivered_pdcp_sn_list.empty()); @@ -241,12 +255,21 @@ TEST_F(f1u_cu_up_test, tx_pdcp_pdus) ASSERT_FALSE(tester->tx_msg_list.empty()); EXPECT_EQ(tester->tx_msg_list.front().t_pdu, tx_pdcp_pdu1); + EXPECT_FALSE(tester->tx_msg_list.front().dl_user_data.retransmission_flag); + EXPECT_FALSE(tester->tx_msg_list.front().dl_user_data.discard_blocks.has_value()); + + tester->tx_msg_list.pop_front(); + + ASSERT_FALSE(tester->tx_msg_list.empty()); + EXPECT_EQ(tester->tx_msg_list.front().t_pdu, tx_pdcp_pdu2); + EXPECT_FALSE(tester->tx_msg_list.front().dl_user_data.retransmission_flag); EXPECT_FALSE(tester->tx_msg_list.front().dl_user_data.discard_blocks.has_value()); tester->tx_msg_list.pop_front(); ASSERT_FALSE(tester->tx_msg_list.empty()); EXPECT_EQ(tester->tx_msg_list.front().t_pdu, tx_pdcp_pdu2); + EXPECT_TRUE(tester->tx_msg_list.front().dl_user_data.retransmission_flag); EXPECT_FALSE(tester->tx_msg_list.front().dl_user_data.discard_blocks.has_value()); tester->tx_msg_list.pop_front(); @@ -269,7 +292,7 @@ TEST_F(f1u_cu_up_test, rx_pdcp_pdus) byte_buffer rx_pdcp_pdu1 = create_sdu_byte_buffer(pdu_size, pdcp_sn); nru_ul_message msg1; auto chain1 = byte_buffer_chain::create(rx_pdcp_pdu1.deep_copy().value()); - EXPECT_FALSE(chain1.is_error()); + EXPECT_TRUE(chain1.has_value()); msg1.t_pdu = std::move(chain1.value()); f1u->handle_pdu(std::move(msg1)); @@ -280,7 +303,7 @@ TEST_F(f1u_cu_up_test, rx_pdcp_pdus) byte_buffer rx_pdcp_pdu2 = create_sdu_byte_buffer(pdu_size, pdcp_sn + 1); nru_ul_message msg2; auto chain2 = byte_buffer_chain::create(rx_pdcp_pdu2.deep_copy().value()); - EXPECT_FALSE(chain2.is_error()); + EXPECT_TRUE(chain2.has_value()); msg2.t_pdu = std::move(chain2.value()); f1u->handle_pdu(std::move(msg2)); diff --git a/tests/unittests/f1u/du/f1u_du_bearer_test.cpp b/tests/unittests/f1u/du/f1u_du_bearer_test.cpp index a9ee4a6e67..35e3b40348 100644 --- a/tests/unittests/f1u/du/f1u_du_bearer_test.cpp +++ b/tests/unittests/f1u/du/f1u_du_bearer_test.cpp @@ -35,12 +35,12 @@ namespace { class f1u_du_test_frame : public f1u_rx_sdu_notifier, public f1u_tx_pdu_notifier { public: - std::list rx_sdu_list; - std::list rx_discard_sdu_list; - std::list tx_msg_list; + std::list> rx_sdu_list; // stores + std::list rx_discard_sdu_list; + std::list tx_msg_list; // f1u_rx_sdu_notifier interface - void on_new_sdu(byte_buffer sdu) override { rx_sdu_list.push_back(std::move(sdu)); } + void on_new_sdu(byte_buffer sdu, bool is_retx) override { rx_sdu_list.push_back({std::move(sdu), is_retx}); } void on_discard_sdu(uint32_t pdcp_sn) override { rx_discard_sdu_list.push_back(pdcp_sn); } // f1u_tx_pdu_notifier interface @@ -180,26 +180,41 @@ TEST_F(f1u_du_test, rx_pdcp_pdus) { constexpr uint32_t pdu_size = 10; - byte_buffer rx_pdcp_pdu1 = create_sdu_byte_buffer(pdu_size, 0); - nru_dl_message msg1 = {}; - msg1.t_pdu = rx_pdcp_pdu1.deep_copy().value(); + byte_buffer rx_pdcp_pdu1 = create_sdu_byte_buffer(pdu_size, 0); + nru_dl_message msg1 = {}; + msg1.t_pdu = rx_pdcp_pdu1.deep_copy().value(); + msg1.dl_user_data.retransmission_flag = false; f1u->handle_pdu(std::move(msg1)); - byte_buffer rx_pdcp_pdu2 = create_sdu_byte_buffer(pdu_size, 1); - nru_dl_message msg2 = {}; - msg2.t_pdu = rx_pdcp_pdu2.deep_copy().value(); + byte_buffer rx_pdcp_pdu2 = create_sdu_byte_buffer(pdu_size, 1); + nru_dl_message msg2 = {}; + msg2.t_pdu = rx_pdcp_pdu2.deep_copy().value(); + msg2.dl_user_data.retransmission_flag = false; f1u->handle_pdu(std::move(msg2)); + nru_dl_message msg2_retx = {}; + msg2_retx.t_pdu = rx_pdcp_pdu2.deep_copy().value(); + msg2_retx.dl_user_data.retransmission_flag = true; + f1u->handle_pdu(std::move(msg2_retx)); + EXPECT_TRUE(tester->rx_discard_sdu_list.empty()); EXPECT_TRUE(tester->tx_msg_list.empty()); ASSERT_FALSE(tester->rx_sdu_list.empty()); - EXPECT_EQ(tester->rx_sdu_list.front(), rx_pdcp_pdu1); + EXPECT_EQ(tester->rx_sdu_list.front().first, rx_pdcp_pdu1); + EXPECT_FALSE(tester->rx_sdu_list.front().second); + + tester->rx_sdu_list.pop_front(); + + ASSERT_FALSE(tester->rx_sdu_list.empty()); + EXPECT_EQ(tester->rx_sdu_list.front().first, rx_pdcp_pdu2); + EXPECT_FALSE(tester->rx_sdu_list.front().second); tester->rx_sdu_list.pop_front(); ASSERT_FALSE(tester->rx_sdu_list.empty()); - EXPECT_EQ(tester->rx_sdu_list.front(), rx_pdcp_pdu2); + EXPECT_EQ(tester->rx_sdu_list.front().first, rx_pdcp_pdu2); + EXPECT_TRUE(tester->rx_sdu_list.front().second); tester->rx_sdu_list.pop_front(); @@ -213,12 +228,12 @@ TEST_F(f1u_du_test, tx_pdcp_pdus) byte_buffer tx_pdcp_pdu1 = create_sdu_byte_buffer(pdu_size, pdcp_sn); auto chain1 = byte_buffer_chain::create(tx_pdcp_pdu1.deep_copy().value()); - ASSERT_FALSE(chain1.is_error()); + ASSERT_TRUE(chain1.has_value()); f1u->handle_sdu(std::move(chain1.value())); byte_buffer tx_pdcp_pdu2 = create_sdu_byte_buffer(pdu_size, pdcp_sn + 1); auto chain2 = byte_buffer_chain::create(tx_pdcp_pdu2.deep_copy().value()); - ASSERT_FALSE(chain2.is_error()); + ASSERT_TRUE(chain2.has_value()); f1u->handle_sdu(std::move(chain2.value())); EXPECT_TRUE(tester->rx_discard_sdu_list.empty()); @@ -252,12 +267,12 @@ TEST_F(f1u_du_test, tx_pdcp_pdus_with_transmit_notification) byte_buffer tx_pdcp_pdu1 = create_sdu_byte_buffer(pdu_size, pdcp_sn); auto chain1 = byte_buffer_chain::create(tx_pdcp_pdu1.deep_copy().value()); - ASSERT_FALSE(chain1.is_error()); + ASSERT_TRUE(chain1.has_value()); f1u->handle_sdu(std::move(chain1.value())); byte_buffer tx_pdcp_pdu2 = create_sdu_byte_buffer(pdu_size, pdcp_sn + 1); auto chain2 = byte_buffer_chain::create(tx_pdcp_pdu2.deep_copy().value()); - ASSERT_FALSE(chain2.is_error()); + ASSERT_TRUE(chain2.has_value()); f1u->handle_sdu(std::move(chain2.value())); EXPECT_TRUE(tester->rx_discard_sdu_list.empty()); @@ -309,12 +324,12 @@ TEST_F(f1u_du_test, tx_pdcp_pdus_with_delivery_notification) byte_buffer tx_pdcp_pdu1 = create_sdu_byte_buffer(pdu_size, pdcp_sn); auto chain1 = byte_buffer_chain::create(tx_pdcp_pdu1.deep_copy().value()); - ASSERT_FALSE(chain1.is_error()); + ASSERT_TRUE(chain1.has_value()); f1u->handle_sdu(std::move(chain1.value())); byte_buffer tx_pdcp_pdu2 = create_sdu_byte_buffer(pdu_size, pdcp_sn + 1); auto chain2 = byte_buffer_chain::create(tx_pdcp_pdu2.deep_copy().value()); - ASSERT_FALSE(chain2.is_error()); + ASSERT_TRUE(chain2.has_value()); f1u->handle_sdu(std::move(chain2.value())); EXPECT_TRUE(tester->rx_discard_sdu_list.empty()); diff --git a/tests/unittests/fapi/validators/helpers.h b/tests/unittests/fapi/validators/helpers.h index 2ce824dd0e..8d38db1585 100644 --- a/tests/unittests/fapi/validators/helpers.h +++ b/tests/unittests/fapi/validators/helpers.h @@ -133,11 +133,11 @@ class validate_fapi_message : public validate_fapi_field validator) override { fapi_error error = validator(pdu); - if (error.is_error()) { + if (not error.has_value()) { base::report = error.error(); } - return !error.is_error(); + return error.has_value(); }; void check_message_params(bool result, message_type_id msg_type_id) const diff --git a/tests/unittests/gateways/test_helpers.h b/tests/unittests/gateways/test_helpers.h index 6538d2b61a..1ec08575b0 100644 --- a/tests/unittests/gateways/test_helpers.h +++ b/tests/unittests/gateways/test_helpers.h @@ -128,7 +128,7 @@ class dummy_network_gateway_data_notifier : public network_gateway_data_notifier // wait until at least one PDU is received std::unique_lock lock(rx_mutex); if (!rx_cvar.wait_for(lock, timeout_ms, [this]() { return pdu_queue.size() > 0; })) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } byte_buffer pdu = std::move(pdu_queue.front()); pdu_queue.pop(); @@ -172,7 +172,7 @@ class dummy_network_gateway_data_notifier_with_src_addr : public network_gateway // wait until at least one PDU is received std::unique_lock lock(rx_mutex); if (!rx_cvar.wait_for(lock, timeout_ms, [this]() { return pdu_queue.size() > 0; })) { - return default_error_t{}; + return make_unexpected(default_error_t{}); } byte_buffer pdu = std::move(pdu_queue.front()); pdu_queue.pop(); diff --git a/tests/unittests/gtpu/gtpu_tunnel_nru_test.cpp b/tests/unittests/gtpu/gtpu_tunnel_nru_test.cpp index 33a563aff0..0595ceacd9 100644 --- a/tests/unittests/gtpu/gtpu_tunnel_nru_test.cpp +++ b/tests/unittests/gtpu/gtpu_tunnel_nru_test.cpp @@ -247,7 +247,7 @@ TEST_F(gtpu_tunnel_nru_test, tx_rx_nru_dl_msg_with_t_pdu) gtpu_tunnel_nru_tx_lower_layer_interface* tx = gtpu->get_tx_lower_layer_interface(); auto tx_msg_copy = tx_msg.deep_copy(); - ASSERT_FALSE(tx_msg_copy.is_error()); + ASSERT_TRUE(tx_msg_copy.has_value()); tx->handle_sdu(std::move(tx_msg_copy.value())); byte_buffer exp_pdu = @@ -284,7 +284,7 @@ TEST_F(gtpu_tunnel_nru_test, tx_rx_nru_ul_msg) gtpu_tunnel_nru_tx_lower_layer_interface* tx = gtpu->get_tx_lower_layer_interface(); auto tx_msg_copy = tx_msg.deep_copy(); - ASSERT_FALSE(tx_msg_copy.is_error()); + ASSERT_TRUE(tx_msg_copy.has_value()); tx->handle_sdu(std::move(tx_msg_copy.value())); byte_buffer exp_pdu = @@ -322,7 +322,7 @@ TEST_F(gtpu_tunnel_nru_test, tx_rx_nru_ul_msg_with_t_pdu) gtpu_tunnel_nru_tx_lower_layer_interface* tx = gtpu->get_tx_lower_layer_interface(); auto tx_msg_copy = tx_msg.deep_copy(); - ASSERT_FALSE(tx_msg_copy.is_error()); + ASSERT_TRUE(tx_msg_copy.has_value()); tx->handle_sdu(std::move(tx_msg_copy.value())); byte_buffer exp_pdu = diff --git a/tests/unittests/ngap/ngap_handover_test.cpp b/tests/unittests/ngap/ngap_handover_test.cpp index afde9a3594..d310a4460b 100644 --- a/tests/unittests/ngap/ngap_handover_test.cpp +++ b/tests/unittests/ngap/ngap_handover_test.cpp @@ -65,8 +65,11 @@ TEST_F(ngap_test, when_source_gnb_handover_preparation_triggered_then_ho_command auto& ue = test_ues.at(ue_index); ue.rrc_ue_ho_prep_handler.set_ho_preparation_message({}); - ngap_handover_preparation_request request = generate_handover_preparation_request( - ue_index, ue_mng.find_ue(ue_index)->get_up_resource_manager().get_pdu_sessions_map(), {1, 22}, 1); + ngap_handover_preparation_request request = + generate_handover_preparation_request(ue_index, + ue_mng.find_ue(ue_index)->get_up_resource_manager().get_pdu_sessions_map(), + nr_cell_identity::create({1, 22}, 1).value(), + 22); // Action 1: Launch HO preparation procedure test_logger.info("Launch source NGAP handover preparation procedure"); diff --git a/tests/unittests/ngap/ngap_paging_test.cpp b/tests/unittests/ngap/ngap_paging_test.cpp index 7c1b398191..a24258e1e0 100644 --- a/tests/unittests/ngap/ngap_paging_test.cpp +++ b/tests/unittests/ngap/ngap_paging_test.cpp @@ -21,7 +21,6 @@ */ #include "ngap_test_helpers.h" -#include "srsran/ran/nr_cgi_helpers.h" #include using namespace srsran; @@ -53,7 +52,7 @@ class ngap_paging_test : public ngap_test } auto& paging_item = cu_cp_paging_notifier.last_msg.tai_list_for_paging.front(); - if (paging_item.tai.plmn_id != "00f110") { + if (paging_item.tai.plmn_id != plmn_identity::test_value()) { test_logger.error("PLMN mismatch {} != 00f110", paging_item.tai.plmn_id); return false; } @@ -126,11 +125,11 @@ class ngap_paging_test : public ngap_test auto& cell_item = cu_cp_paging_notifier.last_msg.assist_data_for_paging.value() .assist_data_for_recommended_cells.value() .recommended_cells_for_paging.recommended_cell_list.front(); - if (cell_item.ngran_cgi.plmn_hex != "00f110") { - test_logger.error("NR CGI PLMN mismatch {} != 00f110", cell_item.ngran_cgi.plmn_hex); + if (cell_item.ngran_cgi.plmn_id != plmn_identity::test_value()) { + test_logger.error("NR CGI PLMN mismatch {} != 00f110", cell_item.ngran_cgi.plmn_id); return false; } - nr_cell_id_t nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); + nr_cell_identity nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); if (cell_item.ngran_cgi.nci != nci) { test_logger.error("NR CGI NCI mismatch {} != {}", cell_item.ngran_cgi.nci, nci); return false; diff --git a/tests/unittests/ngap/ngap_test_helpers.cpp b/tests/unittests/ngap/ngap_test_helpers.cpp index 12699ffc35..6696d1f2e8 100644 --- a/tests/unittests/ngap/ngap_test_helpers.cpp +++ b/tests/unittests/ngap/ngap_test_helpers.cpp @@ -41,7 +41,7 @@ ngap_test::ngap_test() cfg.gnb_id = {411, 22}; cfg.ran_node_name = "srsgnb01"; - cfg.plmn = "00101"; + cfg.plmn = plmn_identity::test_value(); cfg.tac = 7; s_nssai_t slice_cfg; slice_cfg.sst = 1; @@ -65,10 +65,10 @@ ngap_test::~ngap_test() ue_index_t ngap_test::create_ue(rnti_t rnti) { // Create UE in UE manager - ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); - auto* ue = ue_mng.set_ue_du_context(ue_index, int_to_gnb_du_id(0), MIN_PCI, rnti); - if (ue == nullptr) { - test_logger.error("Failed to create UE with pci={} and rnti={}", MIN_PCI, rnti_t::MIN_CRNTI); + ue_index_t ue_index = ue_mng.add_ue(du_index_t::min, int_to_gnb_du_id(0), MIN_PCI, rnti, du_cell_index_t::min); + if (ue_index == ue_index_t::invalid) { + test_logger.error( + "Failed to create UE with pci={} rnti={} pcell_index={}", MIN_PCI, rnti_t::MIN_CRNTI, du_cell_index_t::min); return ue_index_t::invalid; } @@ -92,10 +92,10 @@ ue_index_t ngap_test::create_ue(rnti_t rnti) ue_index_t ngap_test::create_ue_without_init_ue_message(rnti_t rnti) { // Create UE in UE manager - ue_index_t ue_index = ue_mng.add_ue(uint_to_du_index(0)); - auto* ue = ue_mng.set_ue_du_context(ue_index, int_to_gnb_du_id(0), MIN_PCI, rnti); - if (ue == nullptr) { - test_logger.error("Failed to create UE with pci={} and rnti={}", MIN_PCI, rnti_t::MIN_CRNTI); + ue_index_t ue_index = ue_mng.add_ue(du_index_t::min, int_to_gnb_du_id(0), MIN_PCI, rnti, du_cell_index_t::min); + if (ue_index == ue_index_t::invalid) { + test_logger.error( + "Failed to create UE with pci={} rnti={} pcell_index={}", MIN_PCI, rnti_t::MIN_CRNTI, du_cell_index_t::min); return ue_index_t::invalid; } diff --git a/tests/unittests/ngap/ngap_test_messages.cpp b/tests/unittests/ngap/ngap_test_messages.cpp index 2e0a922729..cdfa53bbc9 100644 --- a/tests/unittests/ngap/ngap_test_messages.cpp +++ b/tests/unittests/ngap/ngap_test_messages.cpp @@ -29,7 +29,6 @@ #include "srsran/ngap/ngap_message.h" #include "srsran/ngap/ngap_types.h" #include "srsran/ran/cu_types.h" -#include "srsran/ran/nr_cgi_helpers.h" using namespace srsran; using namespace srs_cu_cp; @@ -75,7 +74,7 @@ ngap_ng_setup_request srsran::srs_cu_cp::generate_ng_setup_request() { ngap_ng_setup_request request_msg; request_msg.global_ran_node_id.gnb_id = {411, 22}; - request_msg.global_ran_node_id.plmn_id = "00101"; + request_msg.global_ran_node_id.plmn_id = plmn_identity::test_value(); request_msg.ran_node_name = "srsgnb01"; @@ -83,7 +82,7 @@ ngap_ng_setup_request srsran::srs_cu_cp::generate_ng_setup_request() supported_ta_item.tac = 7; ngap_broadcast_plmn_item broadcast_plmn_item; - broadcast_plmn_item.plmn_id = "00101"; + broadcast_plmn_item.plmn_id = plmn_identity::test_value(); slice_support_item_t slice_support_item; slice_support_item.s_nssai.sst = 1; @@ -164,11 +163,11 @@ cu_cp_initial_ue_message srsran::srs_cu_cp::generate_initial_ue_message(ue_index msg.ue_index = ue_index; bool ret = msg.nas_pdu.resize(nas_pdu_len); (void)ret; - msg.establishment_cause = static_cast(rrc_establishment_cause_opts::mo_sig); - msg.user_location_info.nr_cgi.plmn_hex = "00f110"; - msg.user_location_info.nr_cgi.nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); - msg.user_location_info.tai.plmn_id = "00f110"; - msg.user_location_info.tai.tac = 7; + msg.establishment_cause = static_cast(rrc_establishment_cause_opts::mo_sig); + msg.user_location_info.nr_cgi.plmn_id = plmn_identity::test_value(); + msg.user_location_info.nr_cgi.nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); + msg.user_location_info.tai.plmn_id = plmn_identity::test_value(); + msg.user_location_info.tai.tac = 7; return msg; } @@ -200,10 +199,10 @@ cu_cp_ul_nas_transport srsran::srs_cu_cp::generate_ul_nas_transport_message(ue_i ul_nas_transport.ue_index = ue_index; bool ret = ul_nas_transport.nas_pdu.resize(nas_pdu_len); (void)ret; - ul_nas_transport.user_location_info.nr_cgi.plmn_hex = "00f110"; - ul_nas_transport.user_location_info.nr_cgi.nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); - ul_nas_transport.user_location_info.tai.plmn_id = "00f110"; - ul_nas_transport.user_location_info.tai.tac = 7; + ul_nas_transport.user_location_info.nr_cgi.plmn_id = plmn_identity::test_value(); + ul_nas_transport.user_location_info.nr_cgi.nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); + ul_nas_transport.user_location_info.tai.plmn_id = plmn_identity::test_value(); + ul_nas_transport.user_location_info.tai.tac = 7; return ul_nas_transport; } @@ -223,7 +222,7 @@ ngap_message srsran::srs_cu_cp::generate_uplink_nas_transport_message(amf_ue_id_ auto& user_loc_info_nr = ul_nas_transport_msg->user_location_info.set_user_location_info_nr(); user_loc_info_nr.nr_cgi.plmn_id.from_string("00f110"); - user_loc_info_nr.nr_cgi.nr_cell_id.from_number(config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0)); + user_loc_info_nr.nr_cgi.nr_cell_id.from_number(nr_cell_identity::create(gnb_id_t{411, 22}, 0).value().value()); user_loc_info_nr.tai.plmn_id.from_string("00f110"); user_loc_info_nr.tai.tac.from_number(7); @@ -718,8 +717,8 @@ ngap_message srsran::srs_cu_cp::generate_valid_paging_message() asn1::ngap::recommended_cell_item_s recommended_cell_item; auto& nr_cgi = recommended_cell_item.ngran_cgi.set_nr_cgi(); nr_cgi.plmn_id.from_string("00f110"); - nr_cell_id_t nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); - nr_cgi.nr_cell_id.from_number(nci); + nr_cell_identity nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); + nr_cgi.nr_cell_id.from_number(nci.value()); recommended_cell_item.time_stayed_in_cell_present = true; recommended_cell_item.time_stayed_in_cell = 5; @@ -833,14 +832,14 @@ ngap_message srsran::srs_cu_cp::generate_valid_handover_request(amf_ue_id_t amf_ // cgi auto& cgi = transparent_container.target_cell_id.set_nr_cgi(); cgi.plmn_id.from_string("00f110"); - nr_cell_id_t nci = config_helpers::make_nr_cell_identity(gnb_id_t{411, 22}, 0); - cgi.nr_cell_id.from_number(nci); + nr_cell_identity nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); + cgi.nr_cell_id.from_number(nci.value()); // ue history info asn1::ngap::last_visited_cell_item_s cell_item; auto& cell = cell_item.last_visited_cell_info.set_ngran_cell(); auto& ngran_cell = cell.global_cell_id.set_nr_cgi(); ngran_cell.plmn_id.from_string("00f110"); - ngran_cell.nr_cell_id.from_number(nci); + ngran_cell.nr_cell_id.from_number(nci.value()); cell.cell_type.cell_size = asn1::ngap::cell_size_opts::options::small; cell.time_ue_stayed_in_cell = 0; transparent_container.ue_history_info.push_back(cell_item); @@ -909,12 +908,12 @@ ngap_message srsran::srs_cu_cp::generate_valid_handover_command(amf_ue_id_t amf_ ngap_handover_preparation_request srsran::srs_cu_cp::generate_handover_preparation_request( ue_index_t ue_index, const std::map& pdu_sessions, - gnb_id_t gnb_id, - nr_cell_id_t nci) + nr_cell_identity nci, + uint32_t gnb_id_bit_length) { ngap_handover_preparation_request request = {}; request.ue_index = ue_index; - request.gnb_id = gnb_id; + request.gnb_id = nci.gnb_id(gnb_id_bit_length); request.nci = nci; // create a map of all PDU sessions and their associated QoS flows for (const auto& pdu_session : pdu_sessions) { diff --git a/tests/unittests/ngap/ngap_test_messages.h b/tests/unittests/ngap/ngap_test_messages.h index 052e25f4e4..e94fd871f8 100644 --- a/tests/unittests/ngap/ngap_test_messages.h +++ b/tests/unittests/ngap/ngap_test_messages.h @@ -218,8 +218,8 @@ ngap_message generate_valid_handover_command(amf_ue_id_t amf_ue_id, ran_ue_id_t ngap_handover_preparation_request generate_handover_preparation_request(ue_index_t ue_index, const std::map& pdu_sessions, - gnb_id_t gnb_id = {1, 22}, - nr_cell_id_t nci = 1); + nr_cell_identity nci = nr_cell_identity::create({1, 22}, 1).value(), + uint32_t gnb_id_bit_length = 22); } // namespace srs_cu_cp } // namespace srsran diff --git a/tests/unittests/pdcp/pdcp_gen_helper.cpp b/tests/unittests/pdcp/pdcp_gen_helper.cpp index fd0e7070f6..98fc6a05b1 100644 --- a/tests/unittests/pdcp/pdcp_gen_helper.cpp +++ b/tests/unittests/pdcp/pdcp_gen_helper.cpp @@ -44,6 +44,7 @@ class pdcp_tx_gen_frame : public pdcp_tx_lower_notifier, public pdcp_tx_upper_co { public: std::queue pdu_queue = {}; + std::queue retx_queue = {}; uint32_t pdu_counter = 0; /// PDCP TX upper layer control notifier @@ -51,7 +52,15 @@ class pdcp_tx_gen_frame : public pdcp_tx_lower_notifier, public pdcp_tx_upper_co void on_protocol_failure() final {} /// PDCP TX lower layer data notifier - void on_new_pdu(byte_buffer pdu) final { pdu_queue.push(std::move(pdu)); } + void on_new_pdu(byte_buffer pdu, bool is_retx) final + { + if (is_retx) { + retx_queue.push(std::move(pdu)); + } else { + pdu_queue.push(std::move(pdu)); + } + } + void on_discard_pdu(uint32_t pdcp_sn) final {} }; diff --git a/tests/unittests/pdcp/pdcp_tx_empty_pool_test.cpp b/tests/unittests/pdcp/pdcp_tx_empty_pool_test.cpp index 546117d815..3cd2ef2858 100644 --- a/tests/unittests/pdcp/pdcp_tx_empty_pool_test.cpp +++ b/tests/unittests/pdcp/pdcp_tx_empty_pool_test.cpp @@ -69,7 +69,7 @@ TEST_P(pdcp_tx_empty_pool_test, empty_pool) // Write first SDU for (uint32_t i = 0; i < n_sdus; i++) { auto sdu_buf = byte_buffer::create(sdu1); - if (sdu_buf.is_error()) { + if (not sdu_buf.has_value()) { pdcp_tx->handle_sdu({}); break; } diff --git a/tests/unittests/pdcp/pdcp_tx_reestablish_test.cpp b/tests/unittests/pdcp/pdcp_tx_reestablish_test.cpp index d702d1b8a0..6047befcd3 100644 --- a/tests/unittests/pdcp/pdcp_tx_reestablish_test.cpp +++ b/tests/unittests/pdcp/pdcp_tx_reestablish_test.cpp @@ -119,7 +119,8 @@ TEST_P(pdcp_tx_reestablish_test, when_drb_am_reestablish_then_pdus_retx) } pdcp_tx->reestablish(sec_cfg); - ASSERT_EQ(8, test_frame.pdu_queue.size()); // SN=2, 3 and 4 RETXed + ASSERT_EQ(5, test_frame.pdu_queue.size()); // unchanged + ASSERT_EQ(3, test_frame.retx_queue.size()); // SN=2, 3 and 4 RETXed // Check if discard timer was not reset. ASSERT_EQ(3, pdcp_tx->nof_discard_timers()); diff --git a/tests/unittests/pdcp/pdcp_tx_status_report_test.cpp b/tests/unittests/pdcp/pdcp_tx_status_report_test.cpp index f2e2332f52..34c13555e4 100644 --- a/tests/unittests/pdcp/pdcp_tx_status_report_test.cpp +++ b/tests/unittests/pdcp/pdcp_tx_status_report_test.cpp @@ -154,6 +154,7 @@ TEST_P(pdcp_tx_status_report_test, data_recovery) // read the status report { + ASSERT_EQ(test_frame.pdu_queue.size(), 1); byte_buffer pdu = std::move(test_frame.pdu_queue.front()); test_frame.pdu_queue.pop(); byte_buffer exp_pdu = test_frame.compile_status_report(); @@ -162,9 +163,10 @@ TEST_P(pdcp_tx_status_report_test, data_recovery) } // read data PDUs + ASSERT_EQ(test_frame.retx_queue.size(), n_sdus); for (uint32_t count = tx_next; count < tx_next + n_sdus; ++count) { - byte_buffer pdu = std::move(test_frame.pdu_queue.front()); - test_frame.pdu_queue.pop(); + byte_buffer pdu = std::move(test_frame.retx_queue.front()); + test_frame.retx_queue.pop(); byte_buffer exp_pdu = std::move(exp_pdu_list.front()); exp_pdu_list.pop(); ASSERT_EQ(pdu.length(), exp_pdu.length()); @@ -174,6 +176,9 @@ TEST_P(pdcp_tx_status_report_test, data_recovery) while (not test_frame.pdu_queue.empty()) { test_frame.pdu_queue.pop(); } + while (not test_frame.retx_queue.empty()) { + test_frame.retx_queue.pop(); + } }; if (sn_size == pdcp_sn_size::size12bits) { diff --git a/tests/unittests/pdcp/pdcp_tx_test_helpers.h b/tests/unittests/pdcp/pdcp_tx_test_helpers.h index f574414f39..20d4d9cc09 100644 --- a/tests/unittests/pdcp/pdcp_tx_test_helpers.h +++ b/tests/unittests/pdcp/pdcp_tx_test_helpers.h @@ -39,6 +39,7 @@ class pdcp_tx_test_frame : public pdcp_rx_status_provider, { public: std::queue pdu_queue = {}; + std::queue retx_queue = {}; uint32_t nof_max_count_reached = 0; uint32_t nof_protocol_failure = 0; std::queue sdu_discard_queue = {}; @@ -69,7 +70,14 @@ class pdcp_tx_test_frame : public pdcp_rx_status_provider, void on_protocol_failure() final { nof_protocol_failure++; } // PDCP TX lower layer data notifier - void on_new_pdu(byte_buffer pdu) final { pdu_queue.push(std::move(pdu)); } + void on_new_pdu(byte_buffer pdu, bool is_retx) final + { + if (is_retx) { + retx_queue.push(std::move(pdu)); + } else { + pdu_queue.push(std::move(pdu)); + } + } void on_discard_pdu(uint32_t pdcp_sn) final { sdu_discard_queue.push(pdcp_sn); } }; diff --git a/tests/unittests/phy/lower/lower_phy_test_doubles.h b/tests/unittests/phy/lower/lower_phy_test_doubles.h index d24fa5efe7..607d80e807 100644 --- a/tests/unittests/phy/lower/lower_phy_test_doubles.h +++ b/tests/unittests/phy/lower/lower_phy_test_doubles.h @@ -133,7 +133,8 @@ class lower_phy_rx_symbol_notifier_spy : public lower_phy_rx_symbol_notifier explicit lower_phy_rx_symbol_notifier_spy(const std::string& log_level = "warning") : logger(srslog::fetch_basic_logger("Rx Notifier")) { - logger.set_level(srslog::str_to_basic_level(log_level)); + auto value = srslog::str_to_basic_level(log_level); + logger.set_level(value.has_value() ? value.value() : srslog::basic_levels::none); } // See interface for documentation. diff --git a/tests/unittests/phy/lower/modulation/ofdm_demodulator_unittest.cpp b/tests/unittests/phy/lower/modulation/ofdm_demodulator_unittest.cpp index b9e360186b..a4a33cd49f 100644 --- a/tests/unittests/phy/lower/modulation/ofdm_demodulator_unittest.cpp +++ b/tests/unittests/phy/lower/modulation/ofdm_demodulator_unittest.cpp @@ -30,7 +30,7 @@ #include /// Defines the maximum allowed error at the OFDM demodulator output. -static constexpr float ASSERT_MAX_ERROR = 1e-6; +static constexpr float ASSERT_MAX_ERROR = 1.0F / 128.0F; using namespace srsran; diff --git a/tests/unittests/phy/lower/modulation/ofdm_demodulator_vectortest.cpp b/tests/unittests/phy/lower/modulation/ofdm_demodulator_vectortest.cpp index ab2ffe4f17..6fefb3f7ba 100644 --- a/tests/unittests/phy/lower/modulation/ofdm_demodulator_vectortest.cpp +++ b/tests/unittests/phy/lower/modulation/ofdm_demodulator_vectortest.cpp @@ -27,7 +27,7 @@ #include "srsran/support/test_utils.h" /// Defines the maximum allowed error at the OFDM demodulator output. -static constexpr float ASSERT_MAX_ERROR = 1e-4; +static constexpr float ASSERT_MAX_ERROR = 5e-3; using namespace srsran; diff --git a/tests/unittests/phy/lower/modulation/ofdm_modulator_test_data.h b/tests/unittests/phy/lower/modulation/ofdm_modulator_test_data.h index bb2b194470..a7dd57c733 100644 --- a/tests/unittests/phy/lower/modulation/ofdm_modulator_test_data.h +++ b/tests/unittests/phy/lower/modulation/ofdm_modulator_test_data.h @@ -22,7 +22,7 @@ #pragma once -// This file was generated using the following MATLAB class on 14-09-2023 (seed 0): +// This file was generated using the following MATLAB class on 27-06-2024 (seed 0): // + "srsOFDMModulatorUnittest.m" #include "srsran/phy/lower/modulation/ofdm_modulator.h" diff --git a/tests/unittests/phy/lower/modulation/ofdm_modulator_vectortest.cpp b/tests/unittests/phy/lower/modulation/ofdm_modulator_vectortest.cpp index 0907ad3b89..dc3d230764 100644 --- a/tests/unittests/phy/lower/modulation/ofdm_modulator_vectortest.cpp +++ b/tests/unittests/phy/lower/modulation/ofdm_modulator_vectortest.cpp @@ -27,7 +27,7 @@ #include "srsran/support/test_utils.h" /// Defines the maximum allowed error at the OFDM modulator output. -static constexpr float ASSERT_MAX_ERROR = 2e-5; +static constexpr float ASSERT_MAX_ERROR = 5e-5; using namespace srsran; @@ -84,7 +84,7 @@ int main() float error = std::abs(output[i] - modulated[i]) / std::sqrt(static_cast(test_case.test_config.config.dft_size)); TESTASSERT(error < ASSERT_MAX_ERROR, - "Sample index {} error {} exceeds maximum allowed ({}). Expedted symbol {} but got {}.", + "Sample index {} error {} exceeds maximum allowed ({}). Expected symbol {} but got {}.", i, error, ASSERT_MAX_ERROR, diff --git a/tests/unittests/phy/lower/processors/uplink/prach/prach_processor_notifier_test_doubles.h b/tests/unittests/phy/lower/processors/uplink/prach/prach_processor_notifier_test_doubles.h index 2cc76e7856..5c357bdcaa 100644 --- a/tests/unittests/phy/lower/processors/uplink/prach/prach_processor_notifier_test_doubles.h +++ b/tests/unittests/phy/lower/processors/uplink/prach/prach_processor_notifier_test_doubles.h @@ -48,7 +48,8 @@ class prach_processor_notifier_spy : public prach_processor_notifier logger(srslog::fetch_basic_logger("Notifier", false)) { srslog::init(); - logger.set_level(srslog::str_to_basic_level(log_level)); + auto value = srslog::str_to_basic_level(log_level); + logger.set_level(value.has_value() ? value.value() : srslog::basic_levels::none); } void on_prach_request_late(const prach_buffer_context& context) override diff --git a/tests/unittests/phy/support/resource_grid_test_doubles.h b/tests/unittests/phy/support/resource_grid_test_doubles.h index a5c1764e5a..488c8588e9 100644 --- a/tests/unittests/phy/support/resource_grid_test_doubles.h +++ b/tests/unittests/phy/support/resource_grid_test_doubles.h @@ -35,6 +35,7 @@ #include "srsran/support/file_vector.h" #include "srsran/support/srsran_assert.h" #include "srsran/support/srsran_test.h" +#include #include #include #include @@ -69,7 +70,8 @@ class resource_grid_writer_spy : public resource_grid_writer logger(srslog::fetch_basic_logger("unittest/resource_grid_spy", false)) { srslog::init(); - logger.set_level(srslog::str_to_basic_level(log_level)); + auto value = srslog::str_to_basic_level(log_level); + logger.set_level(value.has_value() ? value.value() : srslog::basic_levels::none); } // See interface for documentation. @@ -150,7 +152,7 @@ class resource_grid_writer_spy : public resource_grid_writer entry.symbol, entry.subcarrier); - cf_t value = entries.at(key); + cf_t value = to_cf(entries.at(key)); float err = std::abs(entry.value - value); TESTASSERT(err < ASSERT_MAX_ERROR, "Mismatched value {} but expected {}", value, entry.value); } @@ -159,7 +161,7 @@ class resource_grid_writer_spy : public resource_grid_writer /// \brief Asserts that the mapped resource elements match with a list of expected entries. /// /// This method asserts that mapped resource elements using the put() methods match a list of expected entries - /// without considering any writing order, while using a parametrizable maximkum error threshold. + /// without considering any writing order, while using a parametrizable maximum error threshold. /// /// \param[in] expected_entries Provides a list of golden symbols to assert. /// \param[in] max_error Provides the maximum allowable error when comparing the data in the entries. @@ -178,7 +180,7 @@ class resource_grid_writer_spy : public resource_grid_writer entry.symbol, entry.subcarrier); - cf_t value = entries.at(key); + cf_t value = to_cf(entries.at(key)); float err = std::abs(entry.value - value); TESTASSERT(err < max_error, "Mismatched value {} but expected {}", value, entry.value); } @@ -202,7 +204,7 @@ class resource_grid_writer_spy : public resource_grid_writer static constexpr float ASSERT_MAX_ERROR = 1e-6; /// Stores the resource grid written entries. - std::map entries; + std::map entries; /// Protects concurrent write to entries. std::mutex entries_mutex; @@ -255,7 +257,7 @@ class resource_grid_writer_spy : public resource_grid_writer entries.size() + 1); // Write element. - entries.emplace(key, value); + entries.emplace(key, to_cbf16(value)); } }; @@ -288,7 +290,7 @@ class resource_grid_reader_spy : public resource_grid_reader { ++count; mask.for_each(0, mask.size(), [&](unsigned i_subc) { - symbols.front() = get(static_cast(port), l, k_init + i_subc); + symbols.front() = to_cf(get(static_cast(port), l, k_init + i_subc)); symbols = symbols.last(symbols.size() - 1); }); @@ -317,7 +319,7 @@ class resource_grid_reader_spy : public resource_grid_reader ++count; cf_t* symbol_ptr = symbols.data(); for (unsigned k = k_init, k_end = k_init + stride * symbols.size(); k != k_end; k += stride) { - *(symbol_ptr++) = get(port, l, k); + *(symbol_ptr++) = to_cf(get(port, l, k)); } } @@ -326,7 +328,7 @@ class resource_grid_reader_spy : public resource_grid_reader ++count; cbf16_t* symbol_ptr = symbols.data(); for (unsigned k = k_init, k_end = k_init + symbols.size(); k != k_end; ++k) { - *(symbol_ptr++) = to_cbf16(get(port, l, k)); + *(symbol_ptr++) = get(port, l, k); } } @@ -339,7 +341,7 @@ class resource_grid_reader_spy : public resource_grid_reader std::fill(temp_view.begin(), temp_view.end(), to_cbf16(nan)); // Write the available entries. - for (auto& e : entries) { + for (const auto& e : entries) { if ((std::get<0>(e.first) == port) && (std::get<1>(e.first) == l)) { temp_view[std::get<2>(e.first)] = e.second; } @@ -381,12 +383,12 @@ class resource_grid_reader_spy : public resource_grid_reader using entry_key_t = std::tuple; /// Stores the resource grid written entries. - std::map entries; + std::map entries; /// Temporal storage of the method get_view(). It is overwritten every time get_view() is called. mutable std::vector temp_view; - cf_t get(uint8_t port, uint8_t symbol, uint16_t subcarrier) const + cbf16_t get(uint8_t port, uint8_t symbol, uint16_t subcarrier) const { // Generate key. entry_key_t key{port, symbol, subcarrier}; diff --git a/tests/unittests/phy/upper/channel_processors/pbch_modulator_test.cpp b/tests/unittests/phy/upper/channel_processors/pbch_modulator_test.cpp index faa3cbcf46..90b3452d04 100644 --- a/tests/unittests/phy/upper/channel_processors/pbch_modulator_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pbch_modulator_test.cpp @@ -52,8 +52,10 @@ int main() // Load output golden data const std::vector testvector_symbols = test_case.symbols.read(); + // Tolerance: max BF16 error times sqrt(2), since we are taking the modulus. + constexpr float tolerance = M_SQRT2f32 / 256.0; // Assert resource grid entries. - grid.assert_entries(testvector_symbols); + grid.assert_entries(testvector_symbols, tolerance); } return 0; } diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test.cpp b/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test.cpp index 2a21616ecb..14243a3efc 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test.cpp @@ -62,8 +62,10 @@ int main() // Load output golden data const std::vector testvector_symbols = test_case.symbols.read(); + // Tolerance: max BF16 error times sqrt(2), since we are taking the modulus. + constexpr float tolerance = M_SQRT2f32 / 256.0; // Assert resource grid entries. - grid.assert_entries(testvector_symbols); + grid.assert_entries(testvector_symbols, tolerance); } return 0; diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_processor_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pdcch_processor_vectortest.cpp index f4d5e7bd37..9a9f2dc8c4 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch_processor_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdcch_processor_vectortest.cpp @@ -112,8 +112,10 @@ TEST_P(PdcchProcessorFixture, FromVector) // Load output golden data const std::vector expected = test_case.data.read(); + // Tolerance: max BF16 error times sqrt(2), since we are taking the modulus. + constexpr float tolerance = M_SQRT2f32 / 256.0; // Assert resource grid entries. - grid.assert_entries(expected); + grid.assert_entries(expected, tolerance); } // Creates test suite that combines all possible parameters. diff --git a/tests/unittests/phy/upper/channel_processors/pdsch_encoder_test.cpp b/tests/unittests/phy/upper/channel_processors/pdsch_encoder_test.cpp index 19cd873b24..bf3b71a4f6 100644 --- a/tests/unittests/phy/upper/channel_processors/pdsch_encoder_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdsch_encoder_test.cpp @@ -38,11 +38,11 @@ using namespace srsran::ldpc; static std::string encoder_type = "generic"; #ifdef HWACC_PDSCH_ENABLED -static bool dedicated_queue = true; -static bool cb_mode = false; -static std::string hal_log_level = "ERROR"; -static bool std_out_sink = true; -static std::string eal_arguments = ""; +static bool dedicated_queue = true; +static bool cb_mode = false; +static srslog::basic_levels hal_log_level = srslog::basic_levels::error; +static bool std_out_sink = true; +static std::string eal_arguments = ""; #endif // HWACC_PDSCH_ENABLED static void usage(const char* prog) @@ -111,9 +111,10 @@ static void parse_args(int argc, char** argv) case 'y': std_out_sink = false; break; - case 'z': - hal_log_level = std::string(optarg); - break; + case 'z': { + auto level = srslog::str_to_basic_level(std::string(optarg)); + hal_log_level = level.has_value() ? level.value() : srslog::basic_levels::error; + } break; #endif // HWACC_PDSCH_ENABLED case 'h': default: @@ -152,7 +153,7 @@ static std::shared_ptr create_hw_accelera srslog::set_default_sink(*log_sink); srslog::init(); srslog::basic_logger& logger = srslog::fetch_basic_logger("HAL", false); - logger.set_level(srslog::str_to_basic_level(hal_log_level)); + logger.set_level(hal_log_level); // Pointer to a dpdk-based hardware-accelerator interface. static std::unique_ptr dpdk_interface = nullptr; diff --git a/tests/unittests/phy/upper/channel_processors/pdsch_modulator_test.cpp b/tests/unittests/phy/upper/channel_processors/pdsch_modulator_test.cpp index 8a69941ca7..8a55da043f 100644 --- a/tests/unittests/phy/upper/channel_processors/pdsch_modulator_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdsch_modulator_test.cpp @@ -76,9 +76,11 @@ int main() // Read resource grid data. std::vector rg_entries = test_case.symbols.read(); + // Tolerance: max BF16 error times sqrt(2), since we are taking the modulus. + constexpr float tolerance = M_SQRT2f32 / 256.0; // Assert resource grid entries. - grid.assert_entries(rg_entries); + grid.assert_entries(rg_entries, tolerance); } return 0; -} \ No newline at end of file +} diff --git a/tests/unittests/phy/upper/channel_processors/pdsch_processor_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pdsch_processor_vectortest.cpp index 10425c357c..4ab5b8dd69 100644 --- a/tests/unittests/phy/upper/channel_processors/pdsch_processor_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdsch_processor_vectortest.cpp @@ -86,12 +86,12 @@ class PdschProcessorFixture : public ::testing::TestWithParam create_hw_accelerator_pdsch_enc_factory() { #ifdef HWACC_PDSCH_ENABLED - // Hardcoded stdout and error logging. + // Hardcoded stdout and error logging. srslog::sink* log_sink = srslog::create_stdout_sink(); srslog::set_default_sink(*log_sink); srslog::init(); srslog::basic_logger& logger = srslog::fetch_basic_logger("HAL", false); - logger.set_level(srslog::str_to_basic_level("error")); + logger.set_level(srslog::basic_levels::error); // Pointer to a dpdk-based hardware-accelerator interface. static std::unique_ptr dpdk_interface = nullptr; @@ -334,8 +334,10 @@ TEST_P(PdschProcessorFixture, PdschProcessorVectortest) // Waits for the processor to finish. notifier_spy.wait_for_finished(); + // Tolerance: max BF16 error times sqrt(2), since we are taking the modulus. + constexpr float tolerance = M_SQRT2f32 / 256.0; // Assert results. - grid.assert_entries(test_case.grid_expected.read()); + grid.assert_entries(test_case.grid_expected.read(), tolerance); } // Creates test suite that combines all possible parameters. diff --git a/tests/unittests/phy/upper/channel_processors/pusch/pusch_decoder_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pusch/pusch_decoder_vectortest.cpp index 2d0d94d1b9..ba4ce38609 100644 --- a/tests/unittests/phy/upper/channel_processors/pusch/pusch_decoder_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pusch/pusch_decoder_vectortest.cpp @@ -57,12 +57,12 @@ static unsigned nof_ldpc_iterations = 6; static std::string decoder_type = "generic"; #ifdef HWACC_PUSCH_ENABLED -static bool dedicated_queue = true; -static bool test_harq = true; -static bool ext_softbuffer = true; -static bool std_out_sink = true; -static std::string hal_log_level = "error"; -static std::string eal_arguments = ""; +static bool dedicated_queue = true; +static bool test_harq = true; +static bool ext_softbuffer = true; +static bool std_out_sink = true; +static srslog::basic_levels hal_log_level = srslog::basic_levels::error; +static std::string eal_arguments = ""; #endif // HWACC_PUSCH_ENABLED static void usage(const char* prog) @@ -139,9 +139,10 @@ static void parse_args(int argc, char** argv) case 'y': std_out_sink = false; break; - case 'z': - hal_log_level = std::string(optarg); - break; + case 'z': { + auto level = srslog::str_to_basic_level(std::string(optarg)); + hal_log_level = level.has_value() ? level.value() : srslog::basic_levels::error; + } break; #endif // HWACC_PUSCH_ENABLED case 'h': default: @@ -184,7 +185,7 @@ static std::shared_ptr create_hw_accelera srslog::set_default_sink(*log_sink); srslog::init(); srslog::basic_logger& logger = srslog::fetch_basic_logger("HAL", false); - logger.set_level(srslog::str_to_basic_level(hal_log_level)); + logger.set_level(hal_log_level); // Pointer to a dpdk-based hardware-accelerator interface. static std::unique_ptr dpdk_interface = nullptr; diff --git a/tests/unittests/phy/upper/channel_processors/pusch/pusch_demodulator_test_data.h b/tests/unittests/phy/upper/channel_processors/pusch/pusch_demodulator_test_data.h index eaf317c9ba..e5e65fc4c6 100644 --- a/tests/unittests/phy/upper/channel_processors/pusch/pusch_demodulator_test_data.h +++ b/tests/unittests/phy/upper/channel_processors/pusch/pusch_demodulator_test_data.h @@ -22,7 +22,7 @@ #pragma once -// This file was generated using the following MATLAB class on 29-04-2024 (seed 0): +// This file was generated using the following MATLAB class on 27-06-2024 (seed 0): // + "srsPUSCHDemodulatorUnittest.m" #include "../../../support/resource_grid_test_doubles.h" @@ -49,56 +49,56 @@ struct test_case_t { static const std::vector pusch_demodulator_test_data = { // clang-format off - {{0.0016124, 30.3961, {41442, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, modulation_scheme::PI_2_BPSK, 2, 7, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 821, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols0.dat"}, {"test_data/pusch_demodulator_test_input_estimates0.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq0.dat"}, {"test_data/pusch_demodulator_test_output0.dat"}}, - {{0.0014186, 28.6765, {43699, {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 1, 13, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 1, 995, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols2.dat"}, {"test_data/pusch_demodulator_test_input_estimates2.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq2.dat"}, {"test_data/pusch_demodulator_test_output2.dat"}}, - {{0.00073408, 29.5641, {59376, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 1, 8, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 510, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols4.dat"}, {"test_data/pusch_demodulator_test_input_estimates4.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq4.dat"}, {"test_data/pusch_demodulator_test_output4.dat"}}, - {{0.018395, 18.4468, {14385, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 1, 12, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 684, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols6.dat"}, {"test_data/pusch_demodulator_test_input_estimates6.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq6.dat"}, {"test_data/pusch_demodulator_test_output6.dat"}}, - {{0.0022435, 25.594, {5510, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 0, 8, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 238, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols8.dat"}, {"test_data/pusch_demodulator_test_input_estimates8.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq8.dat"}, {"test_data/pusch_demodulator_test_output8.dat"}}, - {{0.10174, 11.8498, {11247, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 1, 9, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 24, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols10.dat"}, {"test_data/pusch_demodulator_test_input_estimates10.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq10.dat"}, {"test_data/pusch_demodulator_test_output10.dat"}}, - {{0.0014186, 29.2481, {13308, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 1, 8, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 441, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols12.dat"}, {"test_data/pusch_demodulator_test_input_estimates12.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq12.dat"}, {"test_data/pusch_demodulator_test_output12.dat"}}, - {{0.029224, 13.5498, {60495, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, modulation_scheme::QPSK, 1, 8, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 638, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols14.dat"}, {"test_data/pusch_demodulator_test_input_estimates14.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq14.dat"}, {"test_data/pusch_demodulator_test_output14.dat"}}, + {{0.0016124, 30.3963, {41442, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, modulation_scheme::PI_2_BPSK, 2, 7, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 821, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols0.dat"}, {"test_data/pusch_demodulator_test_input_estimates0.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq0.dat"}, {"test_data/pusch_demodulator_test_output0.dat"}}, + {{0.0014186, 28.6761, {43699, {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 1, 13, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 1, 995, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols2.dat"}, {"test_data/pusch_demodulator_test_input_estimates2.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq2.dat"}, {"test_data/pusch_demodulator_test_output2.dat"}}, + {{0.00073408, 29.564, {59376, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 1, 8, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 510, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols4.dat"}, {"test_data/pusch_demodulator_test_input_estimates4.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq4.dat"}, {"test_data/pusch_demodulator_test_output4.dat"}}, + {{0.018395, 18.4471, {14385, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 1, 12, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 684, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols6.dat"}, {"test_data/pusch_demodulator_test_input_estimates6.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq6.dat"}, {"test_data/pusch_demodulator_test_output6.dat"}}, + {{0.0022435, 25.5939, {5510, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 0, 8, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 238, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols8.dat"}, {"test_data/pusch_demodulator_test_input_estimates8.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq8.dat"}, {"test_data/pusch_demodulator_test_output8.dat"}}, + {{0.10174, 11.8499, {11247, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 1, 9, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 24, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols10.dat"}, {"test_data/pusch_demodulator_test_input_estimates10.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq10.dat"}, {"test_data/pusch_demodulator_test_output10.dat"}}, + {{0.0014186, 29.2471, {13308, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 1, 8, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 441, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols12.dat"}, {"test_data/pusch_demodulator_test_input_estimates12.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq12.dat"}, {"test_data/pusch_demodulator_test_output12.dat"}}, + {{0.029224, 13.5496, {60495, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, modulation_scheme::QPSK, 1, 8, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 638, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols14.dat"}, {"test_data/pusch_demodulator_test_input_estimates14.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq14.dat"}, {"test_data/pusch_demodulator_test_output14.dat"}}, {{0.00073234, 32.0545, {8543, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 0, 8, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 431, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols16.dat"}, {"test_data/pusch_demodulator_test_input_estimates16.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq16.dat"}, {"test_data/pusch_demodulator_test_output16.dat"}}, {{0.035557, 14.1995, {36974, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, modulation_scheme::QPSK, 0, 11, {0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 75, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols18.dat"}, {"test_data/pusch_demodulator_test_input_estimates18.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq18.dat"}, {"test_data/pusch_demodulator_test_output18.dat"}}, - {{0.064192, 14.2556, {27364, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 1, 13, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 1, 638, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols20.dat"}, {"test_data/pusch_demodulator_test_input_estimates20.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq20.dat"}, {"test_data/pusch_demodulator_test_output20.dat"}}, - {{0.022483, 16.6993, {43398, {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 1, 12, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 2, 86, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols22.dat"}, {"test_data/pusch_demodulator_test_input_estimates22.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq22.dat"}, {"test_data/pusch_demodulator_test_output22.dat"}}, - {{0.011634, 13.6533, {5149, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 0, 14, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 592, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols24.dat"}, {"test_data/pusch_demodulator_test_input_estimates24.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq24.dat"}, {"test_data/pusch_demodulator_test_output24.dat"}}, - {{0.00058172, 33.3565, {7243, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 1, 13, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 2, 58, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols26.dat"}, {"test_data/pusch_demodulator_test_input_estimates26.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq26.dat"}, {"test_data/pusch_demodulator_test_output26.dat"}}, + {{0.064192, 14.2557, {27364, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 1, 13, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 1, 638, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols20.dat"}, {"test_data/pusch_demodulator_test_input_estimates20.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq20.dat"}, {"test_data/pusch_demodulator_test_output20.dat"}}, + {{0.022483, 16.6985, {43398, {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 1, 12, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 2, 86, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols22.dat"}, {"test_data/pusch_demodulator_test_input_estimates22.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq22.dat"}, {"test_data/pusch_demodulator_test_output22.dat"}}, + {{0.011634, 13.654, {5149, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 0, 14, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 592, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols24.dat"}, {"test_data/pusch_demodulator_test_input_estimates24.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq24.dat"}, {"test_data/pusch_demodulator_test_output24.dat"}}, + {{0.00058172, 33.3567, {7243, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 1, 13, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 2, 58, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols26.dat"}, {"test_data/pusch_demodulator_test_input_estimates26.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq26.dat"}, {"test_data/pusch_demodulator_test_output26.dat"}}, {{0.0056354, 21.8862, {16991, {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 1, 13, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 726, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols28.dat"}, {"test_data/pusch_demodulator_test_input_estimates28.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq28.dat"}, {"test_data/pusch_demodulator_test_output28.dat"}}, - {{0.0064192, 23.9808, {56623, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, modulation_scheme::QAM64, 0, 7, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 642, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols30.dat"}, {"test_data/pusch_demodulator_test_input_estimates30.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq30.dat"}, {"test_data/pusch_demodulator_test_output30.dat"}}, - {{0.0022483, 27.3951, {2220, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 2, 8, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 64, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols32.dat"}, {"test_data/pusch_demodulator_test_input_estimates32.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq32.dat"}, {"test_data/pusch_demodulator_test_output32.dat"}}, - {{0.036791, 10.9537, {14331, {1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 2, 10, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 261, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols34.dat"}, {"test_data/pusch_demodulator_test_input_estimates34.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq34.dat"}, {"test_data/pusch_demodulator_test_output34.dat"}}, - {{0.0011607, 29.9178, {46813, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, modulation_scheme::QAM64, 1, 11, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 556, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols36.dat"}, {"test_data/pusch_demodulator_test_input_estimates36.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq36.dat"}, {"test_data/pusch_demodulator_test_output36.dat"}}, + {{0.0064192, 23.9812, {56623, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, modulation_scheme::QAM64, 0, 7, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 642, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols30.dat"}, {"test_data/pusch_demodulator_test_input_estimates30.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq30.dat"}, {"test_data/pusch_demodulator_test_output30.dat"}}, + {{0.0022483, 27.3943, {2220, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 2, 8, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 64, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols32.dat"}, {"test_data/pusch_demodulator_test_input_estimates32.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq32.dat"}, {"test_data/pusch_demodulator_test_output32.dat"}}, + {{0.036791, 10.9553, {14331, {1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 2, 10, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 261, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols34.dat"}, {"test_data/pusch_demodulator_test_input_estimates34.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq34.dat"}, {"test_data/pusch_demodulator_test_output34.dat"}}, + {{0.0011607, 29.9179, {46813, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, modulation_scheme::QAM64, 1, 11, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 556, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols36.dat"}, {"test_data/pusch_demodulator_test_input_estimates36.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq36.dat"}, {"test_data/pusch_demodulator_test_output36.dat"}}, {{0.011244, 19.0355, {4963, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 0, 12, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 493, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols38.dat"}, {"test_data/pusch_demodulator_test_input_estimates38.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq38.dat"}, {"test_data/pusch_demodulator_test_output38.dat"}}, - {{0.080813, 12.67, {61941, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 1, 7, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 425, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols40.dat"}, {"test_data/pusch_demodulator_test_input_estimates40.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq40.dat"}, {"test_data/pusch_demodulator_test_output40.dat"}}, - {{0.011268, 20.1051, {5690, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 2, 9, {0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 689, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols42.dat"}, {"test_data/pusch_demodulator_test_input_estimates42.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq42.dat"}, {"test_data/pusch_demodulator_test_output42.dat"}}, - {{0.0046318, 20.4929, {7084, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 1, 10, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 727, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols44.dat"}, {"test_data/pusch_demodulator_test_input_estimates44.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq44.dat"}, {"test_data/pusch_demodulator_test_output44.dat"}}, - {{0.0036704, 25.3349, {11215, {1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 1, 10, {0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 446, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols46.dat"}, {"test_data/pusch_demodulator_test_input_estimates46.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq46.dat"}, {"test_data/pusch_demodulator_test_output46.dat"}}, + {{0.080813, 12.6709, {61941, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 1, 7, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 425, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols40.dat"}, {"test_data/pusch_demodulator_test_input_estimates40.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq40.dat"}, {"test_data/pusch_demodulator_test_output40.dat"}}, + {{0.011268, 20.1044, {5690, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 2, 9, {0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 689, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols42.dat"}, {"test_data/pusch_demodulator_test_input_estimates42.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq42.dat"}, {"test_data/pusch_demodulator_test_output42.dat"}}, + {{0.0046318, 20.4928, {7084, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 1, 10, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 727, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols44.dat"}, {"test_data/pusch_demodulator_test_input_estimates44.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq44.dat"}, {"test_data/pusch_demodulator_test_output44.dat"}}, + {{0.0036704, 25.3347, {11215, {1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 1, 10, {0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE1, 1, 446, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols46.dat"}, {"test_data/pusch_demodulator_test_input_estimates46.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq46.dat"}, {"test_data/pusch_demodulator_test_output46.dat"}}, {{0.0044763, 23.2376, {25869, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 1, 8, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE1, 2, 70, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols48.dat"}, {"test_data/pusch_demodulator_test_input_estimates48.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq48.dat"}, {"test_data/pusch_demodulator_test_output48.dat"}}, - {{0.025555, 18.0996, {11969, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, modulation_scheme::PI_2_BPSK, 2, 11, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 3, 604, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols50.dat"}, {"test_data/pusch_demodulator_test_input_estimates50.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq50.dat"}, {"test_data/pusch_demodulator_test_output50.dat"}}, - {{0.044859, 14.4261, {22934, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 0, 12, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 3, 830, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols52.dat"}, {"test_data/pusch_demodulator_test_input_estimates52.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq52.dat"}, {"test_data/pusch_demodulator_test_output52.dat"}}, - {{0.023214, 15.045, {50487, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 1, 7, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 2, 824, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols54.dat"}, {"test_data/pusch_demodulator_test_input_estimates54.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq54.dat"}, {"test_data/pusch_demodulator_test_output54.dat"}}, - {{0.0023159, 27.2752, {22017, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 1, 8, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 2, 239, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols56.dat"}, {"test_data/pusch_demodulator_test_input_estimates56.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq56.dat"}, {"test_data/pusch_demodulator_test_output56.dat"}}, + {{0.025555, 18.0999, {11969, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, modulation_scheme::PI_2_BPSK, 2, 11, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 3, 604, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols50.dat"}, {"test_data/pusch_demodulator_test_input_estimates50.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq50.dat"}, {"test_data/pusch_demodulator_test_output50.dat"}}, + {{0.044859, 14.4257, {22934, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 0, 12, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 3, 830, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols52.dat"}, {"test_data/pusch_demodulator_test_input_estimates52.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq52.dat"}, {"test_data/pusch_demodulator_test_output52.dat"}}, + {{0.023214, 15.0447, {50487, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 1, 7, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 2, 824, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols54.dat"}, {"test_data/pusch_demodulator_test_input_estimates54.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq54.dat"}, {"test_data/pusch_demodulator_test_output54.dat"}}, + {{0.0023159, 27.2753, {22017, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 1, 8, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 2, 239, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols56.dat"}, {"test_data/pusch_demodulator_test_input_estimates56.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq56.dat"}, {"test_data/pusch_demodulator_test_output56.dat"}}, {{0.028244, 15.2944, {48073, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, modulation_scheme::PI_2_BPSK, 1, 9, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE2, 1, 788, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols58.dat"}, {"test_data/pusch_demodulator_test_input_estimates58.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq58.dat"}, {"test_data/pusch_demodulator_test_output58.dat"}}, - {{0.025555, 17.8497, {14840, {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 1, 12, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 1, 960, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols60.dat"}, {"test_data/pusch_demodulator_test_input_estimates60.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq60.dat"}, {"test_data/pusch_demodulator_test_output60.dat"}}, - {{0.0056474, 22.7955, {20601, {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 1, 13, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 2, 146, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols62.dat"}, {"test_data/pusch_demodulator_test_input_estimates62.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq62.dat"}, {"test_data/pusch_demodulator_test_output62.dat"}}, - {{0.014647, 14.336, {25000, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 2, 10, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE2, 3, 499, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols64.dat"}, {"test_data/pusch_demodulator_test_input_estimates64.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq64.dat"}, {"test_data/pusch_demodulator_test_output64.dat"}}, - {{0.0073234, 22.1001, {35076, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 2, 10, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE2, 3, 249, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols66.dat"}, {"test_data/pusch_demodulator_test_input_estimates66.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq66.dat"}, {"test_data/pusch_demodulator_test_output66.dat"}}, - {{0.014155, 17.6082, {4117, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 0, 14, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 1, 248, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols68.dat"}, {"test_data/pusch_demodulator_test_input_estimates68.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq68.dat"}, {"test_data/pusch_demodulator_test_output68.dat"}}, - {{0.016124, 20.2664, {17118, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 2, 7, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 2, 457, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols70.dat"}, {"test_data/pusch_demodulator_test_input_estimates70.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq70.dat"}, {"test_data/pusch_demodulator_test_output70.dat"}}, - {{0.017859, 18.0059, {60097, {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 0, 9, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 3, 477, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols72.dat"}, {"test_data/pusch_demodulator_test_input_estimates72.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq72.dat"}, {"test_data/pusch_demodulator_test_output72.dat"}}, - {{0.0029224, 23.3403, {14064, {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 0, 10, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 2, 514, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols74.dat"}, {"test_data/pusch_demodulator_test_input_estimates74.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq74.dat"}, {"test_data/pusch_demodulator_test_output74.dat"}}, + {{0.025555, 17.8496, {14840, {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 1, 12, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 1, 960, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols60.dat"}, {"test_data/pusch_demodulator_test_input_estimates60.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq60.dat"}, {"test_data/pusch_demodulator_test_output60.dat"}}, + {{0.0056474, 22.7951, {20601, {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 1, 13, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 2, 146, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols62.dat"}, {"test_data/pusch_demodulator_test_input_estimates62.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq62.dat"}, {"test_data/pusch_demodulator_test_output62.dat"}}, + {{0.014647, 14.337, {25000, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 2, 10, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE2, 3, 499, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols64.dat"}, {"test_data/pusch_demodulator_test_input_estimates64.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq64.dat"}, {"test_data/pusch_demodulator_test_output64.dat"}}, + {{0.0073234, 22.1002, {35076, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 2, 10, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE2, 3, 249, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols66.dat"}, {"test_data/pusch_demodulator_test_input_estimates66.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq66.dat"}, {"test_data/pusch_demodulator_test_output66.dat"}}, + {{0.014155, 17.6081, {4117, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QPSK, 0, 14, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 1, 248, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols68.dat"}, {"test_data/pusch_demodulator_test_input_estimates68.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq68.dat"}, {"test_data/pusch_demodulator_test_output68.dat"}}, + {{0.016124, 20.2665, {17118, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 2, 7, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 2, 457, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols70.dat"}, {"test_data/pusch_demodulator_test_input_estimates70.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq70.dat"}, {"test_data/pusch_demodulator_test_output70.dat"}}, + {{0.017859, 18.005, {60097, {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 0, 9, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 3, 477, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols72.dat"}, {"test_data/pusch_demodulator_test_input_estimates72.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq72.dat"}, {"test_data/pusch_demodulator_test_output72.dat"}}, + {{0.0029224, 23.3405, {14064, {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 0, 10, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 2, 514, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols74.dat"}, {"test_data/pusch_demodulator_test_input_estimates74.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq74.dat"}, {"test_data/pusch_demodulator_test_output74.dat"}}, {{0.0058172, 22.7771, {28692, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, modulation_scheme::QAM16, 0, 8, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 3, 207, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols76.dat"}, {"test_data/pusch_demodulator_test_input_estimates76.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq76.dat"}, {"test_data/pusch_demodulator_test_output76.dat"}}, {{0.0011244, 28.9833, {24074, {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM16, 1, 13, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 1, 538, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols78.dat"}, {"test_data/pusch_demodulator_test_input_estimates78.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq78.dat"}, {"test_data/pusch_demodulator_test_output78.dat"}}, - {{0.0020299, 29.1924, {15934, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 1, 10, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE2, 1, 986, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols80.dat"}, {"test_data/pusch_demodulator_test_input_estimates80.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq80.dat"}, {"test_data/pusch_demodulator_test_output80.dat"}}, - {{0.00056474, 33.0376, {46564, {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 1, 12, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 1, 564, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols82.dat"}, {"test_data/pusch_demodulator_test_input_estimates82.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq82.dat"}, {"test_data/pusch_demodulator_test_output82.dat"}}, - {{0.029224, 9.0996, {35572, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 2, 12, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 2, 156, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols84.dat"}, {"test_data/pusch_demodulator_test_input_estimates84.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq84.dat"}, {"test_data/pusch_demodulator_test_output84.dat"}}, - {{0.0073234, 21.9992, {749, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 0, 12, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE2, 1, 13, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols86.dat"}, {"test_data/pusch_demodulator_test_input_estimates86.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq86.dat"}, {"test_data/pusch_demodulator_test_output86.dat"}}, - {{0.00089315, 30.3234, {21309, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 2, 11, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 3, 39, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols88.dat"}, {"test_data/pusch_demodulator_test_input_estimates88.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq88.dat"}, {"test_data/pusch_demodulator_test_output88.dat"}}, - {{0.005099, 25.0121, {47542, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 0, 10, {0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE2, 1, 760, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols90.dat"}, {"test_data/pusch_demodulator_test_input_estimates90.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq90.dat"}, {"test_data/pusch_demodulator_test_output90.dat"}}, - {{0.00089506, 31.294, {59296, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 0, 12, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 1, 898, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols92.dat"}, {"test_data/pusch_demodulator_test_input_estimates92.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq92.dat"}, {"test_data/pusch_demodulator_test_output92.dat"}}, - {{0.018439, 12.0356, {44327, {1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 0, 14, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 1, 635, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols94.dat"}, {"test_data/pusch_demodulator_test_input_estimates94.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq94.dat"}, {"test_data/pusch_demodulator_test_output94.dat"}}, - {{0.0011607, 30.1038, {5270, {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 1, 13, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 1, 470, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols96.dat"}, {"test_data/pusch_demodulator_test_input_estimates96.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq96.dat"}, {"test_data/pusch_demodulator_test_output96.dat"}}, - {{0.017821, 16.262, {2792, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 0, 8, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 2, 380, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols98.dat"}, {"test_data/pusch_demodulator_test_input_estimates98.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq98.dat"}, {"test_data/pusch_demodulator_test_output98.dat"}}, + {{0.0020299, 29.1925, {15934, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 1, 10, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE2, 1, 986, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols80.dat"}, {"test_data/pusch_demodulator_test_input_estimates80.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq80.dat"}, {"test_data/pusch_demodulator_test_output80.dat"}}, + {{0.00056474, 33.0377, {46564, {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 1, 12, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 1, 564, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols82.dat"}, {"test_data/pusch_demodulator_test_input_estimates82.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq82.dat"}, {"test_data/pusch_demodulator_test_output82.dat"}}, + {{0.029224, 9.1004, {35572, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 2, 12, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 2, 156, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols84.dat"}, {"test_data/pusch_demodulator_test_input_estimates84.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq84.dat"}, {"test_data/pusch_demodulator_test_output84.dat"}}, + {{0.0073234, 21.9987, {749, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 0, 12, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE2, 1, 13, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols86.dat"}, {"test_data/pusch_demodulator_test_input_estimates86.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq86.dat"}, {"test_data/pusch_demodulator_test_output86.dat"}}, + {{0.00089315, 30.3226, {21309, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM64, 2, 11, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 3, 39, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols88.dat"}, {"test_data/pusch_demodulator_test_input_estimates88.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq88.dat"}, {"test_data/pusch_demodulator_test_output88.dat"}}, + {{0.005099, 25.0125, {47542, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 0, 10, {0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0}, dmrs_type::TYPE2, 1, 760, 1, {0}}}, {"test_data/pusch_demodulator_test_input_symbols90.dat"}, {"test_data/pusch_demodulator_test_input_estimates90.dat", {300, 14, 1, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq90.dat"}, {"test_data/pusch_demodulator_test_output90.dat"}}, + {{0.00089506, 31.2937, {59296, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 0, 12, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 1, 898, 1, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols92.dat"}, {"test_data/pusch_demodulator_test_input_estimates92.dat", {300, 14, 2, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq92.dat"}, {"test_data/pusch_demodulator_test_output92.dat"}}, + {{0.018439, 12.0363, {44327, {1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 0, 14, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 1, 635, 2, {0, 1}}}, {"test_data/pusch_demodulator_test_input_symbols94.dat"}, {"test_data/pusch_demodulator_test_input_estimates94.dat", {300, 14, 2, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq94.dat"}, {"test_data/pusch_demodulator_test_output94.dat"}}, + {{0.0011607, 30.1039, {5270, {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 1, 13, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, dmrs_type::TYPE2, 1, 470, 1, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols96.dat"}, {"test_data/pusch_demodulator_test_input_estimates96.dat", {300, 14, 4, 1}}, {"test_data/pusch_demodulator_test_output_scrambling_seq96.dat"}, {"test_data/pusch_demodulator_test_output96.dat"}}, + {{0.017821, 16.2618, {2792, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, modulation_scheme::QAM256, 0, 8, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, dmrs_type::TYPE2, 2, 380, 2, {0, 1, 2, 3}}}, {"test_data/pusch_demodulator_test_input_symbols98.dat"}, {"test_data/pusch_demodulator_test_input_estimates98.dat", {300, 14, 4, 2}}, {"test_data/pusch_demodulator_test_output_scrambling_seq98.dat"}, {"test_data/pusch_demodulator_test_output98.dat"}}, // clang-format on }; diff --git a/tests/unittests/phy/upper/channel_processors/pusch/pusch_demodulator_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pusch/pusch_demodulator_vectortest.cpp index 0311c367d3..614c0a04b1 100644 --- a/tests/unittests/phy/upper/channel_processors/pusch/pusch_demodulator_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pusch/pusch_demodulator_vectortest.cpp @@ -32,7 +32,7 @@ namespace srsran { // Maximum allowed error. -constexpr log_likelihood_ratio::value_type LLR_MAX_ERROR = 12; +constexpr log_likelihood_ratio::value_type LLR_MAX_ERROR = 1; std::ostream& operator<<(std::ostream& os, const test_case_t& test_case) { diff --git a/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_vectortest.cpp index f2df6c5bff..b5879a3d97 100644 --- a/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_vectortest.cpp @@ -100,12 +100,12 @@ class PuschProcessorFixture : public ::testing::TestWithParam create_hw_accelerator_pusch_dec_factory() { #ifdef HWACC_PUSCH_ENABLED - // Hardcoded stdout and error logging. + // Hardcoded stdout and error logging. srslog::sink* log_sink = srslog::create_stdout_sink(); srslog::set_default_sink(*log_sink); srslog::init(); srslog::basic_logger& logger = srslog::fetch_basic_logger("HAL", false); - logger.set_level(srslog::str_to_basic_level("error")); + logger.set_level(srslog::basic_levels::error); // Pointer to a dpdk-based hardware-accelerator interface. static std::unique_ptr dpdk_interface = nullptr; diff --git a/tests/unittests/phy/upper/channel_processors/ssb_processor_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/ssb_processor_vectortest.cpp index cc4e407bb6..41a916a245 100644 --- a/tests/unittests/phy/upper/channel_processors/ssb_processor_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/ssb_processor_vectortest.cpp @@ -85,8 +85,10 @@ int main() // Load output golden data const std::vector testvector_symbols = test_case.symbols.read(); + // Tolerance: max BF16 error times sqrt(2), since we are taking the modulus. + constexpr float tolerance = M_SQRT2f32 / 256.0; // Assert resource grid entries. - grid.assert_entries(testvector_symbols); + grid.assert_entries(testvector_symbols, tolerance); } return 0; diff --git a/tests/unittests/phy/upper/equalization/channel_equalizer_test.cpp b/tests/unittests/phy/upper/equalization/channel_equalizer_test.cpp index acabebc2c6..3ef251068f 100644 --- a/tests/unittests/phy/upper/equalization/channel_equalizer_test.cpp +++ b/tests/unittests/phy/upper/equalization/channel_equalizer_test.cpp @@ -28,8 +28,8 @@ using namespace srsran; -static constexpr float max_abs_eq_symbol_error = 0.2F; -static constexpr float max_abs_eq_nvar_error = 0.1F; +static constexpr float max_abs_eq_symbol_error = 1e-2; +static constexpr float max_abs_eq_nvar_error = 1e-2; namespace srsran { diff --git a/tests/unittests/phy/upper/equalization/channel_equalizer_test_data.h b/tests/unittests/phy/upper/equalization/channel_equalizer_test_data.h index 93aaa102c5..be602f6c5a 100644 --- a/tests/unittests/phy/upper/equalization/channel_equalizer_test_data.h +++ b/tests/unittests/phy/upper/equalization/channel_equalizer_test_data.h @@ -22,7 +22,7 @@ #pragma once -// This file was generated using the following MATLAB class on 18-04-2024 (seed 0): +// This file was generated using the following MATLAB class on 27-06-2024 (seed 0): // + "srsChEqualizerUnittest.m" #include "srsran/adt/complex.h" diff --git a/tests/unittests/phy/upper/signal_processors/dmrs_pbch_processor_test.cpp b/tests/unittests/phy/upper/signal_processors/dmrs_pbch_processor_test.cpp index 11bd4b19fb..3f85e83f78 100644 --- a/tests/unittests/phy/upper/signal_processors/dmrs_pbch_processor_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/dmrs_pbch_processor_test.cpp @@ -47,8 +47,10 @@ int main() // Load output golden data const std::vector testvector_symbols = test_case.symbols.read(); + // Tolerance: max BF16 error times sqrt(2), since we are taking the modulus. + constexpr float tolerance = M_SQRT2f32 / 256.0; // Assert resource grid entries. - grid.assert_entries(testvector_symbols); + grid.assert_entries(testvector_symbols, tolerance); } return 0; diff --git a/tests/unittests/phy/upper/signal_processors/dmrs_pdcch_processor_test.cpp b/tests/unittests/phy/upper/signal_processors/dmrs_pdcch_processor_test.cpp index 4660b68154..beeb42d94e 100644 --- a/tests/unittests/phy/upper/signal_processors/dmrs_pdcch_processor_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/dmrs_pdcch_processor_test.cpp @@ -56,8 +56,10 @@ int main() // Load output golden data. const std::vector testvector_symbols = test_case.symbols.read(); + // Tolerance: max BF16 error times sqrt(2), since we are taking the modulus. + constexpr float tolerance = M_SQRT2f32 / 256.0; // Assert resource grid entries. - grid.assert_entries(testvector_symbols); + grid.assert_entries(testvector_symbols, tolerance); } return 0; diff --git a/tests/unittests/phy/upper/signal_processors/dmrs_pdsch_processor_test.cpp b/tests/unittests/phy/upper/signal_processors/dmrs_pdsch_processor_test.cpp index b82d9aa4a1..9c3f99cfd0 100644 --- a/tests/unittests/phy/upper/signal_processors/dmrs_pdsch_processor_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/dmrs_pdsch_processor_test.cpp @@ -57,8 +57,10 @@ int main() // Load output golden data. const std::vector testvector_symbols = test_case.symbols.read(); + // Tolerance: max BF16 error times sqrt(2), since we are taking the modulus. + constexpr float tolerance = M_SQRT2f32 / 256.0; // Assert resource grid entries. - grid.assert_entries(testvector_symbols); + grid.assert_entries(testvector_symbols, tolerance); } return 0; diff --git a/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test.cpp b/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test.cpp index 1b1a6ecfee..dd46b32790 100644 --- a/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test.cpp @@ -215,7 +215,7 @@ static bool are_estimates_ok(span tolerance) { + if (std::abs(to_cf(computed_symbol[i_sc]) - value) > tolerance * std::abs(value)) { return false; } } diff --git a/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test_data.h b/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test_data.h index 1f145dc7a7..99345c5e80 100644 --- a/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test_data.h +++ b/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test_data.h @@ -22,7 +22,7 @@ #pragma once -// This file was generated using the following MATLAB class on 07-05-2024 (seed 0): +// This file was generated using the following MATLAB class on 27-06-2024 (seed 0): // + "srsPUSCHdmrsUnittest.m" #include "../../support/resource_grid_test_doubles.h" @@ -45,53 +45,53 @@ struct test_case_t { static const std::vector dmrs_pusch_estimator_test_data = { // clang-format off {test_label::dmrs_creation, {{0, 0, 0, 0}, dmrs_type::TYPE1, 608, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output0.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates0.dat"}}, - {test_label::ch_estimation, {{0, 0, 3, 0}, dmrs_type::TYPE1, 451, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.76926, 2.3038, {"test_data/dmrs_pusch_estimator_test_output1.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates1.dat"}}, + {test_label::ch_estimation, {{0, 0, 3, 0}, dmrs_type::TYPE1, 451, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.76911, 2.3038, {"test_data/dmrs_pusch_estimator_test_output1.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates1.dat"}}, {test_label::dmrs_creation, {{0, 0, 1, 0}, dmrs_type::TYPE2, 122, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output2.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates2.dat"}}, - {test_label::ch_estimation, {{0, 0, 6, 0}, dmrs_type::TYPE2, 447, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.67686, 1.6064, {"test_data/dmrs_pusch_estimator_test_output3.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates3.dat"}}, + {test_label::ch_estimation, {{0, 0, 6, 0}, dmrs_type::TYPE2, 447, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.67663, 1.6049, {"test_data/dmrs_pusch_estimator_test_output3.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates3.dat"}}, {test_label::dmrs_creation, {{0, 0, 1, 0}, dmrs_type::TYPE1, 871, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output4.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates4.dat"}}, - {test_label::ch_estimation, {{0, 0, 6, 0}, dmrs_type::TYPE1, 771, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.635, 0.13555, {"test_data/dmrs_pusch_estimator_test_output5.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates5.dat"}}, + {test_label::ch_estimation, {{0, 0, 6, 0}, dmrs_type::TYPE1, 771, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.6349, 0.13555, {"test_data/dmrs_pusch_estimator_test_output5.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates5.dat"}}, {test_label::dmrs_creation, {{0, 0, 5, 0}, dmrs_type::TYPE2, 98, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output6.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates6.dat"}}, - {test_label::ch_estimation, {{0, 0, 9, 0}, dmrs_type::TYPE2, 745, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.183, 0.15909, {"test_data/dmrs_pusch_estimator_test_output7.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates7.dat"}}, + {test_label::ch_estimation, {{0, 0, 9, 0}, dmrs_type::TYPE2, 745, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.1826, 0.15905, {"test_data/dmrs_pusch_estimator_test_output7.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates7.dat"}}, {test_label::dmrs_creation, {{0, 0, 9, 0}, dmrs_type::TYPE1, 173, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output8.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates8.dat"}}, - {test_label::ch_estimation, {{0, 0, 8, 0}, dmrs_type::TYPE1, 332, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.92276, 3.3482, {"test_data/dmrs_pusch_estimator_test_output9.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates9.dat"}}, + {test_label::ch_estimation, {{0, 0, 8, 0}, dmrs_type::TYPE1, 332, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.92309, 3.3483, {"test_data/dmrs_pusch_estimator_test_output9.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates9.dat"}}, {test_label::dmrs_creation, {{0, 0, 0, 0}, dmrs_type::TYPE2, 634, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output10.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates10.dat"}}, - {test_label::ch_estimation, {{0, 0, 6, 0}, dmrs_type::TYPE2, 555, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.7599, 0.17853, {"test_data/dmrs_pusch_estimator_test_output11.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates11.dat"}}, + {test_label::ch_estimation, {{0, 0, 6, 0}, dmrs_type::TYPE2, 555, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.7598, 0.17851, {"test_data/dmrs_pusch_estimator_test_output11.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates11.dat"}}, {test_label::dmrs_creation, {{0, 0, 4, 0}, dmrs_type::TYPE1, 970, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output12.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates12.dat"}}, - {test_label::ch_estimation, {{0, 0, 3, 0}, dmrs_type::TYPE1, 395, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.97636, 3.3271, {"test_data/dmrs_pusch_estimator_test_output13.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates13.dat"}}, + {test_label::ch_estimation, {{0, 0, 3, 0}, dmrs_type::TYPE1, 395, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.97644, 3.3272, {"test_data/dmrs_pusch_estimator_test_output13.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates13.dat"}}, {test_label::dmrs_creation, {{0, 0, 1, 0}, dmrs_type::TYPE2, 931, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output14.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates14.dat"}}, - {test_label::ch_estimation, {{0, 0, 7, 0}, dmrs_type::TYPE2, 698, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 4.2177, 1.9908, {"test_data/dmrs_pusch_estimator_test_output15.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates15.dat"}}, + {test_label::ch_estimation, {{0, 0, 7, 0}, dmrs_type::TYPE2, 698, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 4.2175, 1.9907, {"test_data/dmrs_pusch_estimator_test_output15.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates15.dat"}}, {test_label::dmrs_creation, {{0, 0, 6, 0}, dmrs_type::TYPE1, 293, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output16.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates16.dat"}}, - {test_label::ch_estimation, {{0, 0, 1, 0}, dmrs_type::TYPE1, 243, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.0159, 0.064941, {"test_data/dmrs_pusch_estimator_test_output17.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates17.dat"}}, + {test_label::ch_estimation, {{0, 0, 1, 0}, dmrs_type::TYPE1, 243, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.0158, 0.064939, {"test_data/dmrs_pusch_estimator_test_output17.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates17.dat"}}, {test_label::dmrs_creation, {{0, 0, 4, 0}, dmrs_type::TYPE2, 280, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output18.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates18.dat"}}, - {test_label::ch_estimation, {{0, 0, 9, 0}, dmrs_type::TYPE2, 691, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.4504, 1.1227, {"test_data/dmrs_pusch_estimator_test_output19.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates19.dat"}}, + {test_label::ch_estimation, {{0, 0, 9, 0}, dmrs_type::TYPE2, 691, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.4503, 1.1225, {"test_data/dmrs_pusch_estimator_test_output19.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates19.dat"}}, {test_label::dmrs_creation, {{0, 0, 8, 0}, dmrs_type::TYPE1, 31, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output20.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates20.dat"}}, - {test_label::ch_estimation, {{0, 0, 9, 0}, dmrs_type::TYPE1, 642, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.8526, 0.046879, {"test_data/dmrs_pusch_estimator_test_output21.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates21.dat"}}, + {test_label::ch_estimation, {{0, 0, 9, 0}, dmrs_type::TYPE1, 642, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.8527, 0.046855, {"test_data/dmrs_pusch_estimator_test_output21.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates21.dat"}}, {test_label::dmrs_creation, {{0, 0, 6, 0}, dmrs_type::TYPE2, 360, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output22.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates22.dat"}}, - {test_label::ch_estimation, {{0, 0, 3, 0}, dmrs_type::TYPE2, 407, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.0562, 0.19082, {"test_data/dmrs_pusch_estimator_test_output23.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates23.dat"}}, + {test_label::ch_estimation, {{0, 0, 3, 0}, dmrs_type::TYPE2, 407, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.0562, 0.19079, {"test_data/dmrs_pusch_estimator_test_output23.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates23.dat"}}, {test_label::dmrs_creation, {{0, 0, 0, 0}, dmrs_type::TYPE1, 39, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output24.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates24.dat"}}, - {test_label::ch_estimation, {{0, 0, 0, 0}, dmrs_type::TYPE1, 21, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.1733, 0.17352, {"test_data/dmrs_pusch_estimator_test_output25.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates25.dat"}}, + {test_label::ch_estimation, {{0, 0, 0, 0}, dmrs_type::TYPE1, 21, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.1731, 0.1735, {"test_data/dmrs_pusch_estimator_test_output25.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates25.dat"}}, {test_label::dmrs_creation, {{0, 0, 7, 0}, dmrs_type::TYPE2, 459, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output26.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates26.dat"}}, - {test_label::ch_estimation, {{0, 0, 5, 0}, dmrs_type::TYPE2, 959, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.86049, 0.33325, {"test_data/dmrs_pusch_estimator_test_output27.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates27.dat"}}, + {test_label::ch_estimation, {{0, 0, 5, 0}, dmrs_type::TYPE2, 959, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.86057, 0.33335, {"test_data/dmrs_pusch_estimator_test_output27.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates27.dat"}}, {test_label::dmrs_creation, {{0, 0, 6, 0}, dmrs_type::TYPE1, 859, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output28.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates28.dat"}}, - {test_label::ch_estimation, {{0, 0, 9, 0}, dmrs_type::TYPE1, 260, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.9802, 0.081571, {"test_data/dmrs_pusch_estimator_test_output29.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates29.dat"}}, + {test_label::ch_estimation, {{0, 0, 9, 0}, dmrs_type::TYPE1, 260, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.98032, 0.081573, {"test_data/dmrs_pusch_estimator_test_output29.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates29.dat"}}, {test_label::dmrs_creation, {{0, 0, 2, 0}, dmrs_type::TYPE2, 302, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output30.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates30.dat"}}, - {test_label::ch_estimation, {{0, 0, 5, 0}, dmrs_type::TYPE2, 652, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.0496, 0.24206, {"test_data/dmrs_pusch_estimator_test_output31.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates31.dat"}}, + {test_label::ch_estimation, {{0, 0, 5, 0}, dmrs_type::TYPE2, 652, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.0497, 0.24208, {"test_data/dmrs_pusch_estimator_test_output31.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates31.dat"}}, {test_label::dmrs_creation, {{0, 0, 4, 0}, dmrs_type::TYPE1, 174, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output32.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates32.dat"}}, - {test_label::ch_estimation, {{0, 0, 2, 0}, dmrs_type::TYPE1, 544, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 3.5259, 0.098264, {"test_data/dmrs_pusch_estimator_test_output33.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates33.dat"}}, + {test_label::ch_estimation, {{0, 0, 2, 0}, dmrs_type::TYPE1, 544, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 3.5262, 0.098301, {"test_data/dmrs_pusch_estimator_test_output33.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates33.dat"}}, {test_label::dmrs_creation, {{0, 0, 2, 0}, dmrs_type::TYPE2, 33, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output34.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates34.dat"}}, - {test_label::ch_estimation, {{0, 0, 1, 0}, dmrs_type::TYPE2, 757, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.6037, 0.16354, {"test_data/dmrs_pusch_estimator_test_output35.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates35.dat"}}, + {test_label::ch_estimation, {{0, 0, 1, 0}, dmrs_type::TYPE2, 757, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.6045, 0.16356, {"test_data/dmrs_pusch_estimator_test_output35.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates35.dat"}}, {test_label::dmrs_creation, {{0, 0, 2, 0}, dmrs_type::TYPE1, 772, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output36.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates36.dat"}}, - {test_label::ch_estimation, {{0, 0, 5, 0}, dmrs_type::TYPE1, 645, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 6.2962, 0.057194, {"test_data/dmrs_pusch_estimator_test_output37.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates37.dat"}}, + {test_label::ch_estimation, {{0, 0, 5, 0}, dmrs_type::TYPE1, 645, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 6.2959, 0.057159, {"test_data/dmrs_pusch_estimator_test_output37.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates37.dat"}}, {test_label::dmrs_creation, {{0, 0, 3, 0}, dmrs_type::TYPE2, 151, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output38.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates38.dat"}}, - {test_label::ch_estimation, {{0, 0, 6, 0}, dmrs_type::TYPE2, 795, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.2931, 0.078522, {"test_data/dmrs_pusch_estimator_test_output39.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates39.dat"}}, + {test_label::ch_estimation, {{0, 0, 6, 0}, dmrs_type::TYPE2, 795, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.293, 0.078485, {"test_data/dmrs_pusch_estimator_test_output39.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates39.dat"}}, {test_label::dmrs_creation, {{0, 0, 8, 0}, dmrs_type::TYPE1, 677, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output40.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates40.dat"}}, - {test_label::ch_estimation, {{0, 0, 2, 0}, dmrs_type::TYPE1, 342, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 4.2209, 0.065567, {"test_data/dmrs_pusch_estimator_test_output41.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates41.dat"}}, + {test_label::ch_estimation, {{0, 0, 2, 0}, dmrs_type::TYPE1, 342, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 4.2206, 0.065534, {"test_data/dmrs_pusch_estimator_test_output41.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates41.dat"}}, {test_label::dmrs_creation, {{0, 0, 9, 0}, dmrs_type::TYPE2, 743, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output42.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates42.dat"}}, - {test_label::ch_estimation, {{0, 0, 1, 0}, dmrs_type::TYPE2, 91, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.7136, 0.1015, {"test_data/dmrs_pusch_estimator_test_output43.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates43.dat"}}, + {test_label::ch_estimation, {{0, 0, 1, 0}, dmrs_type::TYPE2, 91, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.7139, 0.10157, {"test_data/dmrs_pusch_estimator_test_output43.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates43.dat"}}, {test_label::dmrs_creation, {{0, 0, 3, 0}, dmrs_type::TYPE1, 704, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output44.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates44.dat"}}, - {test_label::ch_estimation, {{0, 0, 9, 0}, dmrs_type::TYPE1, 599, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 12.642, 0.045474, {"test_data/dmrs_pusch_estimator_test_output45.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates45.dat"}}, + {test_label::ch_estimation, {{0, 0, 9, 0}, dmrs_type::TYPE1, 599, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 12.6425, 0.045464, {"test_data/dmrs_pusch_estimator_test_output45.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates45.dat"}}, {test_label::dmrs_creation, {{0, 0, 1, 0}, dmrs_type::TYPE2, 722, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output46.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates46.dat"}}, - {test_label::ch_estimation, {{0, 0, 7, 0}, dmrs_type::TYPE2, 864, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.97317, 0.40704, {"test_data/dmrs_pusch_estimator_test_output47.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates47.dat"}}, + {test_label::ch_estimation, {{0, 0, 7, 0}, dmrs_type::TYPE2, 864, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.9731, 0.40699, {"test_data/dmrs_pusch_estimator_test_output47.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates47.dat"}}, {test_label::dmrs_creation, {{0, 0, 8, 0}, dmrs_type::TYPE1, 300, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 2, {{0, 1}}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output48.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates48.dat"}}, {test_label::dmrs_creation, {{0, 0, 1, 0}, dmrs_type::TYPE2, 169, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 2, {{0, 1}}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output49.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates49.dat"}}, {test_label::dmrs_creation, {{0, 0, 2, 0}, dmrs_type::TYPE1, 998, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 2, {{0, 1}}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output50.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates50.dat"}}, @@ -141,53 +141,53 @@ static const std::vector dmrs_pusch_estimator_test_data = { {test_label::dmrs_creation, {{0, 0, 7, 0}, dmrs_type::TYPE1, 719, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 4, {{0, 1, 2, 3}}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output94.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates94.dat"}}, {test_label::dmrs_creation, {{0, 0, 5, 0}, dmrs_type::TYPE2, 833, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 4, {{0, 1, 2, 3}}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output95.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates95.dat"}}, {test_label::dmrs_creation, {{1, 0, 9, 1}, dmrs_type::TYPE1, 751, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output96.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates96.dat"}}, - {test_label::ch_estimation, {{1, 0, 9, 0}, dmrs_type::TYPE1, 731, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.0689, 0.18748, {"test_data/dmrs_pusch_estimator_test_output97.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates97.dat"}}, + {test_label::ch_estimation, {{1, 0, 9, 0}, dmrs_type::TYPE1, 731, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.0692, 0.18748, {"test_data/dmrs_pusch_estimator_test_output97.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates97.dat"}}, {test_label::dmrs_creation, {{1, 0, 1, 1}, dmrs_type::TYPE2, 788, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output98.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates98.dat"}}, - {test_label::ch_estimation, {{1, 0, 8, 0}, dmrs_type::TYPE2, 839, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.80322, 0.50728, {"test_data/dmrs_pusch_estimator_test_output99.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates99.dat"}}, + {test_label::ch_estimation, {{1, 0, 8, 0}, dmrs_type::TYPE2, 839, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.80329, 0.50725, {"test_data/dmrs_pusch_estimator_test_output99.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates99.dat"}}, {test_label::dmrs_creation, {{1, 0, 2, 0}, dmrs_type::TYPE1, 418, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output100.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates100.dat"}}, - {test_label::ch_estimation, {{1, 0, 3, 0}, dmrs_type::TYPE1, 672, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.4286, 0.092828, {"test_data/dmrs_pusch_estimator_test_output101.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates101.dat"}}, + {test_label::ch_estimation, {{1, 0, 3, 0}, dmrs_type::TYPE1, 672, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.4286, 0.092791, {"test_data/dmrs_pusch_estimator_test_output101.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates101.dat"}}, {test_label::dmrs_creation, {{1, 0, 9, 1}, dmrs_type::TYPE2, 517, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output102.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates102.dat"}}, - {test_label::ch_estimation, {{1, 0, 2, 0}, dmrs_type::TYPE2, 943, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.498, 0.1539, {"test_data/dmrs_pusch_estimator_test_output103.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates103.dat"}}, + {test_label::ch_estimation, {{1, 0, 2, 0}, dmrs_type::TYPE2, 943, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.4976, 0.15389, {"test_data/dmrs_pusch_estimator_test_output103.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates103.dat"}}, {test_label::dmrs_creation, {{1, 0, 1, 1}, dmrs_type::TYPE1, 335, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output104.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates104.dat"}}, - {test_label::ch_estimation, {{1, 0, 0, 0}, dmrs_type::TYPE1, 120, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.91718, 2.8256, {"test_data/dmrs_pusch_estimator_test_output105.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates105.dat"}}, + {test_label::ch_estimation, {{1, 0, 0, 0}, dmrs_type::TYPE1, 120, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.91711, 2.8254, {"test_data/dmrs_pusch_estimator_test_output105.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates105.dat"}}, {test_label::dmrs_creation, {{1, 0, 7, 0}, dmrs_type::TYPE2, 270, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output106.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates106.dat"}}, {test_label::ch_estimation, {{1, 0, 1, 0}, dmrs_type::TYPE2, 389, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.1917, 1.0793, {"test_data/dmrs_pusch_estimator_test_output107.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates107.dat"}}, {test_label::dmrs_creation, {{1, 0, 1, 1}, dmrs_type::TYPE1, 876, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output108.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates108.dat"}}, - {test_label::ch_estimation, {{1, 0, 8, 0}, dmrs_type::TYPE1, 766, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 8.3402, 0.050143, {"test_data/dmrs_pusch_estimator_test_output109.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates109.dat"}}, + {test_label::ch_estimation, {{1, 0, 8, 0}, dmrs_type::TYPE1, 766, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 8.3399, 0.050134, {"test_data/dmrs_pusch_estimator_test_output109.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates109.dat"}}, {test_label::dmrs_creation, {{1, 0, 7, 1}, dmrs_type::TYPE2, 518, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output110.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates110.dat"}}, - {test_label::ch_estimation, {{1, 0, 0, 1}, dmrs_type::TYPE2, 850, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.8542, 0.77589, {"test_data/dmrs_pusch_estimator_test_output111.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates111.dat"}}, + {test_label::ch_estimation, {{1, 0, 0, 1}, dmrs_type::TYPE2, 850, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.8542, 0.77624, {"test_data/dmrs_pusch_estimator_test_output111.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates111.dat"}}, {test_label::dmrs_creation, {{1, 0, 0, 0}, dmrs_type::TYPE1, 803, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output112.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates112.dat"}}, - {test_label::ch_estimation, {{1, 0, 0, 0}, dmrs_type::TYPE1, 227, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.658, 0.098159, {"test_data/dmrs_pusch_estimator_test_output113.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates113.dat"}}, + {test_label::ch_estimation, {{1, 0, 0, 0}, dmrs_type::TYPE1, 227, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.6579, 0.098135, {"test_data/dmrs_pusch_estimator_test_output113.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates113.dat"}}, {test_label::dmrs_creation, {{1, 0, 9, 0}, dmrs_type::TYPE2, 159, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output114.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates114.dat"}}, - {test_label::ch_estimation, {{1, 0, 6, 0}, dmrs_type::TYPE2, 592, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.149, 0.10751, {"test_data/dmrs_pusch_estimator_test_output115.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates115.dat"}}, + {test_label::ch_estimation, {{1, 0, 6, 0}, dmrs_type::TYPE2, 592, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.1488, 0.10751, {"test_data/dmrs_pusch_estimator_test_output115.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates115.dat"}}, {test_label::dmrs_creation, {{1, 0, 9, 0}, dmrs_type::TYPE1, 840, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output116.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates116.dat"}}, - {test_label::ch_estimation, {{1, 0, 4, 0}, dmrs_type::TYPE1, 235, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.8551, 0.070339, {"test_data/dmrs_pusch_estimator_test_output117.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates117.dat"}}, + {test_label::ch_estimation, {{1, 0, 4, 0}, dmrs_type::TYPE1, 235, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.8549, 0.070351, {"test_data/dmrs_pusch_estimator_test_output117.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates117.dat"}}, {test_label::dmrs_creation, {{1, 0, 8, 0}, dmrs_type::TYPE2, 804, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output118.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates118.dat"}}, - {test_label::ch_estimation, {{1, 0, 9, 1}, dmrs_type::TYPE2, 709, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.7139, 0.092382, {"test_data/dmrs_pusch_estimator_test_output119.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates119.dat"}}, + {test_label::ch_estimation, {{1, 0, 9, 1}, dmrs_type::TYPE2, 709, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 2.7142, 0.092385, {"test_data/dmrs_pusch_estimator_test_output119.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates119.dat"}}, {test_label::dmrs_creation, {{1, 0, 3, 1}, dmrs_type::TYPE1, 54, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output120.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates120.dat"}}, - {test_label::ch_estimation, {{1, 0, 8, 1}, dmrs_type::TYPE1, 505, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 4.634, 0.19385, {"test_data/dmrs_pusch_estimator_test_output121.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates121.dat"}}, + {test_label::ch_estimation, {{1, 0, 8, 1}, dmrs_type::TYPE1, 505, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 4.6345, 0.19408, {"test_data/dmrs_pusch_estimator_test_output121.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates121.dat"}}, {test_label::dmrs_creation, {{1, 0, 0, 0}, dmrs_type::TYPE2, 487, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output122.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates122.dat"}}, - {test_label::ch_estimation, {{1, 0, 3, 1}, dmrs_type::TYPE2, 962, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.678, 0.98794, {"test_data/dmrs_pusch_estimator_test_output123.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates123.dat"}}, + {test_label::ch_estimation, {{1, 0, 3, 1}, dmrs_type::TYPE2, 962, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.67799, 0.98827, {"test_data/dmrs_pusch_estimator_test_output123.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates123.dat"}}, {test_label::dmrs_creation, {{1, 0, 0, 1}, dmrs_type::TYPE1, 375, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output124.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates124.dat"}}, - {test_label::ch_estimation, {{1, 0, 1, 1}, dmrs_type::TYPE1, 635, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.94147, 0.092888, {"test_data/dmrs_pusch_estimator_test_output125.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates125.dat"}}, + {test_label::ch_estimation, {{1, 0, 1, 1}, dmrs_type::TYPE1, 635, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.94173, 0.092907, {"test_data/dmrs_pusch_estimator_test_output125.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates125.dat"}}, {test_label::dmrs_creation, {{1, 0, 9, 0}, dmrs_type::TYPE2, 648, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output126.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates126.dat"}}, - {test_label::ch_estimation, {{1, 0, 1, 1}, dmrs_type::TYPE2, 147, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.4732, 0.16003, {"test_data/dmrs_pusch_estimator_test_output127.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates127.dat"}}, + {test_label::ch_estimation, {{1, 0, 1, 1}, dmrs_type::TYPE2, 147, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.4733, 0.16004, {"test_data/dmrs_pusch_estimator_test_output127.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates127.dat"}}, {test_label::dmrs_creation, {{1, 0, 3, 0}, dmrs_type::TYPE1, 660, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output128.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates128.dat"}}, - {test_label::ch_estimation, {{1, 0, 3, 1}, dmrs_type::TYPE1, 930, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 6.6925, 0.11871, {"test_data/dmrs_pusch_estimator_test_output129.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates129.dat"}}, + {test_label::ch_estimation, {{1, 0, 3, 1}, dmrs_type::TYPE1, 930, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 6.6929, 0.11868, {"test_data/dmrs_pusch_estimator_test_output129.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates129.dat"}}, {test_label::dmrs_creation, {{1, 0, 6, 0}, dmrs_type::TYPE2, 868, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output130.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates130.dat"}}, - {test_label::ch_estimation, {{1, 0, 5, 0}, dmrs_type::TYPE2, 878, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.4073, 0.53696, {"test_data/dmrs_pusch_estimator_test_output131.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates131.dat"}}, + {test_label::ch_estimation, {{1, 0, 5, 0}, dmrs_type::TYPE2, 878, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.4074, 0.53698, {"test_data/dmrs_pusch_estimator_test_output131.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates131.dat"}}, {test_label::dmrs_creation, {{1, 0, 8, 1}, dmrs_type::TYPE1, 2, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output132.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates132.dat"}}, - {test_label::ch_estimation, {{1, 0, 3, 0}, dmrs_type::TYPE1, 789, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 11.5317, 0.12058, {"test_data/dmrs_pusch_estimator_test_output133.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates133.dat"}}, + {test_label::ch_estimation, {{1, 0, 3, 0}, dmrs_type::TYPE1, 789, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 11.5322, 0.12059, {"test_data/dmrs_pusch_estimator_test_output133.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates133.dat"}}, {test_label::dmrs_creation, {{1, 0, 2, 1}, dmrs_type::TYPE2, 357, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output134.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates134.dat"}}, - {test_label::ch_estimation, {{1, 0, 5, 0}, dmrs_type::TYPE2, 95, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.96288, 0.075347, {"test_data/dmrs_pusch_estimator_test_output135.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates135.dat"}}, + {test_label::ch_estimation, {{1, 0, 5, 0}, dmrs_type::TYPE2, 95, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0.96259, 0.075334, {"test_data/dmrs_pusch_estimator_test_output135.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates135.dat"}}, {test_label::dmrs_creation, {{1, 0, 0, 0}, dmrs_type::TYPE1, 869, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output136.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates136.dat"}}, - {test_label::ch_estimation, {{1, 0, 2, 0}, dmrs_type::TYPE1, 165, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 8.6283, 0.069851, {"test_data/dmrs_pusch_estimator_test_output137.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates137.dat"}}, + {test_label::ch_estimation, {{1, 0, 2, 0}, dmrs_type::TYPE1, 165, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 8.6284, 0.069862, {"test_data/dmrs_pusch_estimator_test_output137.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates137.dat"}}, {test_label::dmrs_creation, {{1, 0, 7, 1}, dmrs_type::TYPE2, 801, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output138.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates138.dat"}}, - {test_label::ch_estimation, {{1, 0, 9, 1}, dmrs_type::TYPE2, 319, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.2291, 0.38696, {"test_data/dmrs_pusch_estimator_test_output139.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates139.dat"}}, + {test_label::ch_estimation, {{1, 0, 9, 1}, dmrs_type::TYPE2, 319, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.2288, 0.38694, {"test_data/dmrs_pusch_estimator_test_output139.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates139.dat"}}, {test_label::dmrs_creation, {{1, 0, 8, 1}, dmrs_type::TYPE1, 690, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output140.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates140.dat"}}, - {test_label::ch_estimation, {{1, 0, 9, 1}, dmrs_type::TYPE1, 738, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.3294, 0.045961, {"test_data/dmrs_pusch_estimator_test_output141.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates141.dat"}}, + {test_label::ch_estimation, {{1, 0, 9, 1}, dmrs_type::TYPE1, 738, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.3296, 0.045955, {"test_data/dmrs_pusch_estimator_test_output141.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates141.dat"}}, {test_label::dmrs_creation, {{1, 0, 1, 0}, dmrs_type::TYPE2, 879, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output142.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates142.dat"}}, - {test_label::ch_estimation, {{1, 0, 9, 0}, dmrs_type::TYPE2, 135, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.0064, 0.32712, {"test_data/dmrs_pusch_estimator_test_output143.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates143.dat"}}, + {test_label::ch_estimation, {{1, 0, 9, 0}, dmrs_type::TYPE2, 135, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 1, {0}}, 1.0064, 0.32709, {"test_data/dmrs_pusch_estimator_test_output143.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates143.dat"}}, {test_label::dmrs_creation, {{1, 0, 6, 1}, dmrs_type::TYPE1, 593, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 2, {{0, 1}}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output144.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates144.dat"}}, {test_label::dmrs_creation, {{1, 0, 9, 1}, dmrs_type::TYPE2, 268, 1, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 2, {{0, 1}}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output145.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates145.dat"}}, {test_label::dmrs_creation, {{1, 0, 3, 1}, dmrs_type::TYPE1, 67, 0, 1.4142, cyclic_prefix::NORMAL, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1, 13, 2, {{0, 1}}}, 0, 0, {"test_data/dmrs_pusch_estimator_test_output146.dat"}, {"test_data/dmrs_pusch_estimator_ch_estimates146.dat"}}, diff --git a/tests/unittests/phy/upper/signal_processors/nzp_csi_rs_generator_test_data.h b/tests/unittests/phy/upper/signal_processors/nzp_csi_rs_generator_test_data.h index ce248e2d3d..da3a8391f2 100644 --- a/tests/unittests/phy/upper/signal_processors/nzp_csi_rs_generator_test_data.h +++ b/tests/unittests/phy/upper/signal_processors/nzp_csi_rs_generator_test_data.h @@ -22,7 +22,7 @@ #pragma once -// This file was generated using the following MATLAB class on 14-09-2023 (seed 0): +// This file was generated using the following MATLAB class on 27-06-2024 (seed 0): // + "srsNZPCSIRSGeneratorUnittest.m" #include "../../support/resource_grid_test_doubles.h" diff --git a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp index 963162db61..eb82f77540 100644 --- a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp @@ -96,7 +96,7 @@ std::shared_ptr ChannelEstFixture::ch_est_factor bool are_estimates_ok(span expected, const channel_estimate& computed) { - constexpr float tolerance = 1e-2; + constexpr float tolerance = 0.008; unsigned old_symbol = 15; span computed_symbol; @@ -110,7 +110,8 @@ bool are_estimates_ok(span exp computed_symbol = computed.get_symbol_ch_estimate(i_symbol, 0, 0); } - if (std::abs(to_cf(computed_symbol[i_sc]) - value) > tolerance) { + cf_t ref = to_cf(computed_symbol[i_sc]) - value; + if ((std::abs(std::real(ref)) > tolerance) || (std::abs(std::imag(ref)) > tolerance)) { return false; } } @@ -172,14 +173,14 @@ TEST_P(ChannelEstFixture, test) ASSERT_TRUE(are_estimates_ok(expected_estimates, estimates)); ASSERT_NEAR(estimates.get_rsrp(0, 0), test_params.rsrp, 5e-4); ASSERT_NEAR(estimates.get_epre(0, 0), test_params.epre, 5e-4); - ASSERT_NEAR(estimates.get_noise_variance(0, 0), test_params.noise_var_est, 0.004); - ASSERT_NEAR(estimates.get_snr_dB(0, 0), test_params.snr_est, 0.02 * std::abs(test_params.snr_est)); + ASSERT_NEAR(estimates.get_noise_variance(0, 0), test_params.noise_var_est, 5e-4); + ASSERT_NEAR(estimates.get_snr_dB(0, 0), test_params.snr_est, 0.002 * std::abs(test_params.snr_est)); ASSERT_NEAR(estimates.get_time_alignment(0, 0).to_seconds() * 1e6, test_params.ta_us, tolerance_ta_us); if (test_params.cfo_est_Hz.has_value()) { ASSERT_TRUE(estimates.get_cfo_Hz(0, 0).has_value()) << "CFO estimation was expected, none obtained."; ASSERT_NEAR(estimates.get_cfo_Hz(0, 0).value(), test_params.cfo_est_Hz.value(), - 0.05 * std::abs(test_params.cfo_est_Hz.value())); + 0.001 * std::abs(test_params.cfo_est_Hz.value())); } else { ASSERT_FALSE(estimates.get_cfo_Hz(0, 0).has_value()) << "No CFO estimation was expected."; } diff --git a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test_data.h b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test_data.h index 9dfba6e39c..652c819e0d 100644 --- a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test_data.h +++ b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test_data.h @@ -22,7 +22,7 @@ #pragma once -// This file was generated using the following MATLAB class on 22-05-2024 (seed 0): +// This file was generated using the following MATLAB class on 26-06-2024 (seed 0): // + "srsChEstimatorUnittest.m" #include "../../support/resource_grid_test_doubles.h" @@ -53,118 +53,118 @@ struct test_case_t { static const std::vector port_channel_estimator_test_data = { // clang-format off - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0141, 2.0226, 20, 20.3378, 0.009339, 0, 0, -4.8912, {"test_data/port_channel_estimator_test_input_rg0.dat"}, {"test_data/port_channel_estimator_test_pilots0.dat"}, {"test_data/port_channel_estimator_test_output_ch_est0.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0636, 2.0308, 20, 20.6911, 0.0088209, 0.84635, 105, 116.3191, {"test_data/port_channel_estimator_test_input_rg1.dat"}, {"test_data/port_channel_estimator_test_pilots1.dat"}, {"test_data/port_channel_estimator_test_output_ch_est1.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0777, 2.0487, 20, 19.9442, 0.010548, 1.0742, -195, -205.0632, {"test_data/port_channel_estimator_test_input_rg2.dat"}, {"test_data/port_channel_estimator_test_pilots2.dat"}, {"test_data/port_channel_estimator_test_output_ch_est2.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0241, 1.9915, 20, 20.2498, 0.0095778, 0.86263, 405, 398.0863, {"test_data/port_channel_estimator_test_input_rg3.dat"}, {"test_data/port_channel_estimator_test_pilots3.dat"}, {"test_data/port_channel_estimator_test_output_ch_est3.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0154, 2.0235, 20, 20.0118, 0.010074, 0.097656, 0, -9.0296, {"test_data/port_channel_estimator_test_input_rg4.dat"}, {"test_data/port_channel_estimator_test_pilots4.dat"}, {"test_data/port_channel_estimator_test_output_ch_est4.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0565, 2.0237, 20, 20.3579, 0.0094917, 0.84635, 105, 105.4642, {"test_data/port_channel_estimator_test_input_rg5.dat"}, {"test_data/port_channel_estimator_test_pilots5.dat"}, {"test_data/port_channel_estimator_test_output_ch_est5.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0214, 2.0288, 20, 20.9245, 0.0081885, 0.10579, -195, -187.8516, {"test_data/port_channel_estimator_test_input_rg6.dat"}, {"test_data/port_channel_estimator_test_pilots6.dat"}, {"test_data/port_channel_estimator_test_output_ch_est6.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0201, 1.9893, 20, 19.5528, 0.011223, 0.96842, 405, 406.9891, {"test_data/port_channel_estimator_test_input_rg7.dat"}, {"test_data/port_channel_estimator_test_pilots7.dat"}, {"test_data/port_channel_estimator_test_output_ch_est7.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9704, 1.9827, 20, 18.5329, 0.013844, 0.10579, 0, -29.9404, {"test_data/port_channel_estimator_test_input_rg8.dat"}, {"test_data/port_channel_estimator_test_pilots8.dat"}, {"test_data/port_channel_estimator_test_output_ch_est8.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0093, 2.0225, 20, 19.261, 0.011939, 0.056966, 210, 199.4011, {"test_data/port_channel_estimator_test_input_rg9.dat"}, {"test_data/port_channel_estimator_test_pilots9.dat"}, {"test_data/port_channel_estimator_test_output_ch_est9.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9812, 1.9851, 20, 19.2316, 0.011851, 0.15462, -390, -404.0844, {"test_data/port_channel_estimator_test_input_rg10.dat"}, {"test_data/port_channel_estimator_test_pilots10.dat"}, {"test_data/port_channel_estimator_test_output_ch_est10.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9975, 2.0085, 20, 19.7651, 0.010568, 0.048828, 810, 809.7605, {"test_data/port_channel_estimator_test_input_rg11.dat"}, {"test_data/port_channel_estimator_test_pilots11.dat"}, {"test_data/port_channel_estimator_test_output_ch_est11.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0101, 1.9915, 20, 19.7326, 0.010714, 0.31738, 0, 2.8507, {"test_data/port_channel_estimator_test_input_rg12.dat"}, {"test_data/port_channel_estimator_test_pilots12.dat"}, {"test_data/port_channel_estimator_test_output_ch_est12.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.015, 1.9867, 20, 19.7099, 0.010797, 0.42318, 210, 211.3146, {"test_data/port_channel_estimator_test_input_rg13.dat"}, {"test_data/port_channel_estimator_test_pilots13.dat"}, {"test_data/port_channel_estimator_test_output_ch_est13.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9996, 2.0067, 20, 20.1638, 0.0096508, 0.052897, -390, -395.4395, {"test_data/port_channel_estimator_test_input_rg14.dat"}, {"test_data/port_channel_estimator_test_pilots14.dat"}, {"test_data/port_channel_estimator_test_output_ch_est14.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0149, 2.0093, 20, 21.1135, 0.0078144, 0.20752, 810, 812.086, {"test_data/port_channel_estimator_test_input_rg15.dat"}, {"test_data/port_channel_estimator_test_pilots15.dat"}, {"test_data/port_channel_estimator_test_output_ch_est15.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.049, 2.0177, 20, 19.9565, 0.010373, 1.0742, 0, -1.5672, {"test_data/port_channel_estimator_test_input_rg16.dat"}, {"test_data/port_channel_estimator_test_pilots16.dat"}, {"test_data/port_channel_estimator_test_output_ch_est16.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9885, 1.9969, 20, 20.2867, 0.0093294, 0.11393, 105, 108.8475, {"test_data/port_channel_estimator_test_input_rg17.dat"}, {"test_data/port_channel_estimator_test_pilots17.dat"}, {"test_data/port_channel_estimator_test_output_ch_est17.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0121, 2.0177, 20, 20.396, 0.0092054, 0.21159, -195, -195.2145, {"test_data/port_channel_estimator_test_input_rg18.dat"}, {"test_data/port_channel_estimator_test_pilots18.dat"}, {"test_data/port_channel_estimator_test_output_ch_est18.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2, 2.0014, 20, 20.2914, 0.0093731, 0.32552, 405, 398.5025, {"test_data/port_channel_estimator_test_input_rg19.dat"}, {"test_data/port_channel_estimator_test_pilots19.dat"}, {"test_data/port_channel_estimator_test_output_ch_est19.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9942, 2.0024, 20, 20.6589, 0.008588, 0.11393, 0, -4.1958, {"test_data/port_channel_estimator_test_input_rg20.dat"}, {"test_data/port_channel_estimator_test_pilots20.dat"}, {"test_data/port_channel_estimator_test_output_ch_est20.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0122, 2.02, 20, 20.6322, 0.0087188, 0.11393, 105, 101.7756, {"test_data/port_channel_estimator_test_input_rg21.dat"}, {"test_data/port_channel_estimator_test_pilots21.dat"}, {"test_data/port_channel_estimator_test_output_ch_est21.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0015, 2.0078, 20, 20.2532, 0.0094633, 0.21159, -195, -191.3337, {"test_data/port_channel_estimator_test_input_rg22.dat"}, {"test_data/port_channel_estimator_test_pilots22.dat"}, {"test_data/port_channel_estimator_test_output_ch_est22.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9903, 1.9953, 20, 20.7383, 0.0084155, 0.21159, 405, 407.3301, {"test_data/port_channel_estimator_test_input_rg23.dat"}, {"test_data/port_channel_estimator_test_pilots23.dat"}, {"test_data/port_channel_estimator_test_output_ch_est23.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0177, 2.0129, 20, 20.5633, 0.0088823, 0.21159, 0, -5.311, {"test_data/port_channel_estimator_test_input_rg24.dat"}, {"test_data/port_channel_estimator_test_pilots24.dat"}, {"test_data/port_channel_estimator_test_output_ch_est24.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9927, 2.002, 20, 20.4702, 0.0089623, 0, 210, 208.6105, {"test_data/port_channel_estimator_test_input_rg25.dat"}, {"test_data/port_channel_estimator_test_pilots25.dat"}, {"test_data/port_channel_estimator_test_output_ch_est25.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0053, 2.0069, 20, 20.0006, 0.010049, 0.16276, -390, -398.8503, {"test_data/port_channel_estimator_test_input_rg26.dat"}, {"test_data/port_channel_estimator_test_pilots26.dat"}, {"test_data/port_channel_estimator_test_output_ch_est26.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9905, 1.9963, 20, 20.3596, 0.0091835, 0.10579, 810, 815.9859, {"test_data/port_channel_estimator_test_input_rg27.dat"}, {"test_data/port_channel_estimator_test_pilots27.dat"}, {"test_data/port_channel_estimator_test_output_ch_est27.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.006, 1.9944, 20, 20.449, 0.0090662, 0.26855, 0, -4.5514, {"test_data/port_channel_estimator_test_input_rg28.dat"}, {"test_data/port_channel_estimator_test_pilots28.dat"}, {"test_data/port_channel_estimator_test_output_ch_est28.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9992, 2.0077, 20, 20.2124, 0.0095415, 0.056966, 210, 217.3404, {"test_data/port_channel_estimator_test_input_rg29.dat"}, {"test_data/port_channel_estimator_test_pilots29.dat"}, {"test_data/port_channel_estimator_test_output_ch_est29.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0236, 1.9912, 20, 20.3977, 0.0092546, 0.48014, -390, -385.5684, {"test_data/port_channel_estimator_test_input_rg30.dat"}, {"test_data/port_channel_estimator_test_pilots30.dat"}, {"test_data/port_channel_estimator_test_output_ch_est30.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0368, 2.0038, 20, 20.3858, 0.0093404, 0.53711, 810, 804.2222, {"test_data/port_channel_estimator_test_input_rg31.dat"}, {"test_data/port_channel_estimator_test_pilots31.dat"}, {"test_data/port_channel_estimator_test_output_ch_est31.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0436, 2.012, 20, 20.0617, 0.010098, 1.0742, 0, 1.5958, {"test_data/port_channel_estimator_test_input_rg32.dat"}, {"test_data/port_channel_estimator_test_pilots32.dat"}, {"test_data/port_channel_estimator_test_output_ch_est32.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0379, 2.0083, 20, 20.3755, 0.0093678, 0.86263, 105, 108.9964, {"test_data/port_channel_estimator_test_input_rg33.dat"}, {"test_data/port_channel_estimator_test_pilots33.dat"}, {"test_data/port_channel_estimator_test_output_ch_est33.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9941, 2.0035, 20, 20.2809, 0.0093682, 0, -195, -197.4308, {"test_data/port_channel_estimator_test_input_rg34.dat"}, {"test_data/port_channel_estimator_test_pilots34.dat"}, {"test_data/port_channel_estimator_test_output_ch_est34.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0325, 2.0006, 20, 20.0632, 0.01004, 0.96029, 405, 403.5354, {"test_data/port_channel_estimator_test_input_rg35.dat"}, {"test_data/port_channel_estimator_test_pilots35.dat"}, {"test_data/port_channel_estimator_test_output_ch_est35.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0002, 2.0082, 20, 20.4173, 0.0091062, 0.056966, 0, 2.7656, {"test_data/port_channel_estimator_test_input_rg36.dat"}, {"test_data/port_channel_estimator_test_pilots36.dat"}, {"test_data/port_channel_estimator_test_output_ch_est36.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0413, 2.0092, 20, 20.2648, 0.0096258, 0.48014, 210, 204.2069, {"test_data/port_channel_estimator_test_input_rg37.dat"}, {"test_data/port_channel_estimator_test_pilots37.dat"}, {"test_data/port_channel_estimator_test_output_ch_est37.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0185, 2.0137, 20, 20.4252, 0.009173, 0.21159, -390, -391.2415, {"test_data/port_channel_estimator_test_input_rg38.dat"}, {"test_data/port_channel_estimator_test_pilots38.dat"}, {"test_data/port_channel_estimator_test_output_ch_est38.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.019, 2.0145, 20, 20.2333, 0.0095898, 0.21159, 810, 809.2049, {"test_data/port_channel_estimator_test_input_rg39.dat"}, {"test_data/port_channel_estimator_test_pilots39.dat"}, {"test_data/port_channel_estimator_test_output_ch_est39.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.92773, 1.0091, 20, 10.3852, 0.084898, 0, 0, 18.0125, {"test_data/port_channel_estimator_test_input_rg40.dat"}, {"test_data/port_channel_estimator_test_pilots40.dat"}, {"test_data/port_channel_estimator_test_output_ch_est40.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.98185, 0.99445, 20, 18.7319, 0.013148, 0, 105, 100.1507, {"test_data/port_channel_estimator_test_input_rg41.dat"}, {"test_data/port_channel_estimator_test_pilots41.dat"}, {"test_data/port_channel_estimator_test_output_ch_est41.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.98922, 1.018, 20, 15.1704, 0.030078, 0, -195, -249.9309, {"test_data/port_channel_estimator_test_input_rg42.dat"}, {"test_data/port_channel_estimator_test_pilots42.dat"}, {"test_data/port_channel_estimator_test_output_ch_est42.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.83738, 0.99507, 20, 7.0664, 0.16454, 0, 405, 458.7814, {"test_data/port_channel_estimator_test_input_rg43.dat"}, {"test_data/port_channel_estimator_test_pilots43.dat"}, {"test_data/port_channel_estimator_test_output_ch_est43.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.92724, 0.97133, 20, 13.043, 0.046014, 0, 0, {}, {"test_data/port_channel_estimator_test_input_rg44.dat"}, {"test_data/port_channel_estimator_test_pilots44.dat"}, {"test_data/port_channel_estimator_test_output_ch_est44.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.96501, 0.97211, 20, 21.1534, 0.0073994, 0, 105, {}, {"test_data/port_channel_estimator_test_input_rg45.dat"}, {"test_data/port_channel_estimator_test_pilots45.dat"}, {"test_data/port_channel_estimator_test_output_ch_est45.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.98704, 1.0391, 20, 12.5941, 0.054316, 0, -195, {}, {"test_data/port_channel_estimator_test_input_rg46.dat"}, {"test_data/port_channel_estimator_test_pilots46.dat"}, {"test_data/port_channel_estimator_test_output_ch_est46.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 1.0028, 1.014, 20, 19.352, 0.011642, 0, 405, {}, {"test_data/port_channel_estimator_test_input_rg47.dat"}, {"test_data/port_channel_estimator_test_pilots47.dat"}, {"test_data/port_channel_estimator_test_output_ch_est47.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.937, 1.0081, 20, 11.0159, 0.074157, 0, 0, 141.2287, {"test_data/port_channel_estimator_test_input_rg48.dat"}, {"test_data/port_channel_estimator_test_pilots48.dat"}, {"test_data/port_channel_estimator_test_output_ch_est48.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.90798, 1.0276, 20, 8.6166, 0.12486, 0, 210, 163.5811, {"test_data/port_channel_estimator_test_input_rg49.dat"}, {"test_data/port_channel_estimator_test_pilots49.dat"}, {"test_data/port_channel_estimator_test_output_ch_est49.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.99767, 1.0126, 20, 18.0714, 0.015554, 0, -390, -371.5474, {"test_data/port_channel_estimator_test_input_rg50.dat"}, {"test_data/port_channel_estimator_test_pilots50.dat"}, {"test_data/port_channel_estimator_test_output_ch_est50.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.91733, 1.0053, 20, 9.9957, 0.091823, 0, 810, 785.2378, {"test_data/port_channel_estimator_test_input_rg51.dat"}, {"test_data/port_channel_estimator_test_pilots51.dat"}, {"test_data/port_channel_estimator_test_output_ch_est51.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.9428, 1.0385, 20, 9.7519, 0.099822, 0, 0, {}, {"test_data/port_channel_estimator_test_input_rg52.dat"}, {"test_data/port_channel_estimator_test_pilots52.dat"}, {"test_data/port_channel_estimator_test_output_ch_est52.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.93922, 0.99057, 20, 12.4371, 0.053586, 0, 210, {}, {"test_data/port_channel_estimator_test_input_rg53.dat"}, {"test_data/port_channel_estimator_test_pilots53.dat"}, {"test_data/port_channel_estimator_test_output_ch_est53.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.90884, 1.0183, 20, 9.0072, 0.11423, 0, -390, {}, {"test_data/port_channel_estimator_test_input_rg54.dat"}, {"test_data/port_channel_estimator_test_pilots54.dat"}, {"test_data/port_channel_estimator_test_output_ch_est54.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 1.0537, 1.0639, 20, 19.9479, 0.010664, 0, 810, {}, {"test_data/port_channel_estimator_test_input_rg55.dat"}, {"test_data/port_channel_estimator_test_pilots55.dat"}, {"test_data/port_channel_estimator_test_output_ch_est55.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.96176, 1.0108, 20, 12.8732, 0.049631, 0, 0, 60.6817, {"test_data/port_channel_estimator_test_input_rg56.dat"}, {"test_data/port_channel_estimator_test_pilots56.dat"}, {"test_data/port_channel_estimator_test_output_ch_est56.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.92807, 0.97217, 20, 13.1793, 0.044633, 0, 105, 133.7294, {"test_data/port_channel_estimator_test_input_rg57.dat"}, {"test_data/port_channel_estimator_test_pilots57.dat"}, {"test_data/port_channel_estimator_test_output_ch_est57.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.84816, 0.97482, 20, 8.2062, 0.12819, 0, -195, -157.4014, {"test_data/port_channel_estimator_test_input_rg58.dat"}, {"test_data/port_channel_estimator_test_pilots58.dat"}, {"test_data/port_channel_estimator_test_output_ch_est58.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.55248, 1.0007, 20, 0.85639, 0.45361, 0, 405, 423.2168, {"test_data/port_channel_estimator_test_input_rg59.dat"}, {"test_data/port_channel_estimator_test_pilots59.dat"}, {"test_data/port_channel_estimator_test_output_ch_est59.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.99375, 1.0023, 20, 20.5873, 0.0086807, 0, 0, -1.2908, {"test_data/port_channel_estimator_test_input_rg60.dat"}, {"test_data/port_channel_estimator_test_pilots60.dat"}, {"test_data/port_channel_estimator_test_output_ch_est60.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.97766, 1.001, 20, 16.1781, 0.023571, 0, 105, 116.222, {"test_data/port_channel_estimator_test_input_rg61.dat"}, {"test_data/port_channel_estimator_test_pilots61.dat"}, {"test_data/port_channel_estimator_test_output_ch_est61.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.90902, 1.0061, 20, 9.6642, 0.098209, 0, -195, -195.4093, {"test_data/port_channel_estimator_test_input_rg62.dat"}, {"test_data/port_channel_estimator_test_pilots62.dat"}, {"test_data/port_channel_estimator_test_output_ch_est62.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.79254, 1.0332, 20, 5.1235, 0.24359, 0, 405, 436.5061, {"test_data/port_channel_estimator_test_input_rg63.dat"}, {"test_data/port_channel_estimator_test_pilots63.dat"}, {"test_data/port_channel_estimator_test_output_ch_est63.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.98631, 1.0073, 20, 16.6661, 0.021252, 0, 0, 40.0278, {"test_data/port_channel_estimator_test_input_rg64.dat"}, {"test_data/port_channel_estimator_test_pilots64.dat"}, {"test_data/port_channel_estimator_test_output_ch_est64.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.92546, 1.0132, 20, 10.1797, 0.088795, 0, 210, 261.8733, {"test_data/port_channel_estimator_test_input_rg65.dat"}, {"test_data/port_channel_estimator_test_pilots65.dat"}, {"test_data/port_channel_estimator_test_output_ch_est65.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.86154, 1.0099, 20, 7.587, 0.15017, 0, -390, -338.3241, {"test_data/port_channel_estimator_test_input_rg66.dat"}, {"test_data/port_channel_estimator_test_pilots66.dat"}, {"test_data/port_channel_estimator_test_output_ch_est66.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.51471, 1.0102, 20, 0.11348, 0.50144, 0, 810, 778.8338, {"test_data/port_channel_estimator_test_input_rg67.dat"}, {"test_data/port_channel_estimator_test_pilots67.dat"}, {"test_data/port_channel_estimator_test_output_ch_est67.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.96791, 1.0222, 20, 12.4597, 0.054937, 0, 0, 35.6927, {"test_data/port_channel_estimator_test_input_rg68.dat"}, {"test_data/port_channel_estimator_test_pilots68.dat"}, {"test_data/port_channel_estimator_test_output_ch_est68.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.97479, 1.0009, 20, 15.6715, 0.02641, 0, 210, 200.884, {"test_data/port_channel_estimator_test_input_rg69.dat"}, {"test_data/port_channel_estimator_test_pilots69.dat"}, {"test_data/port_channel_estimator_test_output_ch_est69.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.95264, 1.005, 20, 12.5443, 0.053028, 0, -390, -472.3701, {"test_data/port_channel_estimator_test_input_rg70.dat"}, {"test_data/port_channel_estimator_test_pilots70.dat"}, {"test_data/port_channel_estimator_test_output_ch_est70.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.7833, 1.0073, 20, 5.3841, 0.22674, 0, 810, 740.312, {"test_data/port_channel_estimator_test_input_rg71.dat"}, {"test_data/port_channel_estimator_test_pilots71.dat"}, {"test_data/port_channel_estimator_test_output_ch_est71.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.9075, 0.95987, 20, 23.3963, 0.0041516, 1.0905, 0, -77.1756, {"test_data/port_channel_estimator_test_input_rg72.dat"}, {"test_data/port_channel_estimator_test_pilots72.dat"}, {"test_data/port_channel_estimator_test_output_ch_est72.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.99232, 1.0286, 20, 18.6898, 0.013417, 0.87891, 105, -21.4809, {"test_data/port_channel_estimator_test_input_rg73.dat"}, {"test_data/port_channel_estimator_test_pilots73.dat"}, {"test_data/port_channel_estimator_test_output_ch_est73.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.99415, 1.0463, 20, 21.8182, 0.0065409, 1.0091, -195, -188.0035, {"test_data/port_channel_estimator_test_input_rg74.dat"}, {"test_data/port_channel_estimator_test_pilots74.dat"}, {"test_data/port_channel_estimator_test_output_ch_est74.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.91907, 0.9199, 20, 20.3384, 0.0085018, 0.11393, 405, 261.0421, {"test_data/port_channel_estimator_test_input_rg75.dat"}, {"test_data/port_channel_estimator_test_pilots75.dat"}, {"test_data/port_channel_estimator_test_output_ch_est75.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.89632, 0.94912, 20, 21.6845, 0.0060815, 1.001, 0, {}, {"test_data/port_channel_estimator_test_input_rg76.dat"}, {"test_data/port_channel_estimator_test_pilots76.dat"}, {"test_data/port_channel_estimator_test_output_ch_est76.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.002, 1.0111, 20, 22.7804, 0.0052824, 0.08138, 105, {}, {"test_data/port_channel_estimator_test_input_rg77.dat"}, {"test_data/port_channel_estimator_test_pilots77.dat"}, {"test_data/port_channel_estimator_test_output_ch_est77.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0084, 1.0127, 20, 25.1825, 0.0030575, 0.04069, -195, {}, {"test_data/port_channel_estimator_test_input_rg78.dat"}, {"test_data/port_channel_estimator_test_pilots78.dat"}, {"test_data/port_channel_estimator_test_output_ch_est78.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.99912, 1.0526, 20, 21.2414, 0.0075072, 0.93587, 405, {}, {"test_data/port_channel_estimator_test_input_rg79.dat"}, {"test_data/port_channel_estimator_test_pilots79.dat"}, {"test_data/port_channel_estimator_test_output_ch_est79.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.94639, 0.98645, 20, 18.0641, 0.014779, 0.39062, 0, 327.2693, {"test_data/port_channel_estimator_test_input_rg80.dat"}, {"test_data/port_channel_estimator_test_pilots80.dat"}, {"test_data/port_channel_estimator_test_output_ch_est80.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0411, 1.0419, 20, 21.1142, 0.0080554, 0.048828, 210, 164.3707, {"test_data/port_channel_estimator_test_input_rg81.dat"}, {"test_data/port_channel_estimator_test_pilots81.dat"}, {"test_data/port_channel_estimator_test_output_ch_est81.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.94088, 0.9712, 20, 17.1849, 0.01799, 0.21973, -390, -386.7762, {"test_data/port_channel_estimator_test_input_rg82.dat"}, {"test_data/port_channel_estimator_test_pilots82.dat"}, {"test_data/port_channel_estimator_test_output_ch_est82.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.99301, 1.0198, 20, 19.0676, 0.012308, 0.19531, 810, 1325.2174, {"test_data/port_channel_estimator_test_input_rg83.dat"}, {"test_data/port_channel_estimator_test_pilots83.dat"}, {"test_data/port_channel_estimator_test_output_ch_est83.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0457, 1.0987, 20, 22.5621, 0.005797, 0.49235, 0, {}, {"test_data/port_channel_estimator_test_input_rg84.dat"}, {"test_data/port_channel_estimator_test_pilots84.dat"}, {"test_data/port_channel_estimator_test_output_ch_est84.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.95303, 0.9643, 20, 20.4602, 0.0085721, -0.012207, 210, {}, {"test_data/port_channel_estimator_test_input_rg85.dat"}, {"test_data/port_channel_estimator_test_pilots85.dat"}, {"test_data/port_channel_estimator_test_output_ch_est85.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.9809, 0.97849, 20, 23.2489, 0.0046424, 0.073242, -390, {}, {"test_data/port_channel_estimator_test_input_rg86.dat"}, {"test_data/port_channel_estimator_test_pilots86.dat"}, {"test_data/port_channel_estimator_test_output_ch_est86.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.92919, 0.93499, 20, 22.2185, 0.0055751, 0.24007, 810, {}, {"test_data/port_channel_estimator_test_input_rg87.dat"}, {"test_data/port_channel_estimator_test_pilots87.dat"}, {"test_data/port_channel_estimator_test_output_ch_est87.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.1118, 1.0516, 20, 22.1075, 0.0068433, 0.65104, 0, {}, {"test_data/port_channel_estimator_test_input_rg88.dat"}, {"test_data/port_channel_estimator_test_pilots88.dat"}, {"test_data/port_channel_estimator_test_output_ch_est88.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0806, 0.97182, 20, 20.2396, 0.010226, 0.96029, 105, {}, {"test_data/port_channel_estimator_test_input_rg89.dat"}, {"test_data/port_channel_estimator_test_pilots89.dat"}, {"test_data/port_channel_estimator_test_output_ch_est89.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.1017, 1.0768, 20, 23.5362, 0.0048803, 0.42318, -195, {}, {"test_data/port_channel_estimator_test_input_rg90.dat"}, {"test_data/port_channel_estimator_test_pilots90.dat"}, {"test_data/port_channel_estimator_test_output_ch_est90.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.98631, 0.99012, 20, 23.1843, 0.0047378, 0.11393, 405, {}, {"test_data/port_channel_estimator_test_input_rg91.dat"}, {"test_data/port_channel_estimator_test_pilots91.dat"}, {"test_data/port_channel_estimator_test_output_ch_est91.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0087, 0.99711, 20, 22.5401, 0.0056201, 0.15462, 0, {}, {"test_data/port_channel_estimator_test_input_rg92.dat"}, {"test_data/port_channel_estimator_test_pilots92.dat"}, {"test_data/port_channel_estimator_test_output_ch_est92.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0108, 1.0179, 20, 20.5885, 0.0088274, 0, 210, {}, {"test_data/port_channel_estimator_test_input_rg93.dat"}, {"test_data/port_channel_estimator_test_pilots93.dat"}, {"test_data/port_channel_estimator_test_output_ch_est93.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0374, 1.0326, 20, 22.4065, 0.0059609, 0.10579, -390, {}, {"test_data/port_channel_estimator_test_input_rg94.dat"}, {"test_data/port_channel_estimator_test_pilots94.dat"}, {"test_data/port_channel_estimator_test_output_ch_est94.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0596, 0.98504, 20, 20.4232, 0.009612, 0.37435, 810, {}, {"test_data/port_channel_estimator_test_input_rg95.dat"}, {"test_data/port_channel_estimator_test_pilots95.dat"}, {"test_data/port_channel_estimator_test_output_ch_est95.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0152, 1.016, 20, 20.4654, 0.0091206, 0.21159, 0, 36.7511, {"test_data/port_channel_estimator_test_input_rg96.dat"}, {"test_data/port_channel_estimator_test_pilots96.dat"}, {"test_data/port_channel_estimator_test_output_ch_est96.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.014, 1.0211, 20, 20.2223, 0.0096339, 0.11393, 105, 113.6789, {"test_data/port_channel_estimator_test_input_rg97.dat"}, {"test_data/port_channel_estimator_test_pilots97.dat"}, {"test_data/port_channel_estimator_test_output_ch_est97.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.1158, 1.0201, 20, 20.1525, 0.010773, 0.86263, -195, -146.8209, {"test_data/port_channel_estimator_test_input_rg98.dat"}, {"test_data/port_channel_estimator_test_pilots98.dat"}, {"test_data/port_channel_estimator_test_output_ch_est98.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0156, 1.0218, 20, 20.8396, 0.0083711, 0.11393, 405, 448.1142, {"test_data/port_channel_estimator_test_input_rg99.dat"}, {"test_data/port_channel_estimator_test_pilots99.dat"}, {"test_data/port_channel_estimator_test_output_ch_est99.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0444, 0.98766, 20, 21.9406, 0.0066803, 0.6429, 0, {}, {"test_data/port_channel_estimator_test_input_rg100.dat"}, {"test_data/port_channel_estimator_test_pilots100.dat"}, {"test_data/port_channel_estimator_test_output_ch_est100.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0604, 1.005, 20, 21.3093, 0.0078438, 0.63477, 105, {}, {"test_data/port_channel_estimator_test_input_rg101.dat"}, {"test_data/port_channel_estimator_test_pilots101.dat"}, {"test_data/port_channel_estimator_test_output_ch_est101.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0562, 1.0002, 20, 21.4501, 0.0075634, 0.6429, -195, {}, {"test_data/port_channel_estimator_test_input_rg102.dat"}, {"test_data/port_channel_estimator_test_pilots102.dat"}, {"test_data/port_channel_estimator_test_output_ch_est102.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0668, 1.0102, 20, 21.8527, 0.0069631, 0.63477, 405, {}, {"test_data/port_channel_estimator_test_input_rg103.dat"}, {"test_data/port_channel_estimator_test_pilots103.dat"}, {"test_data/port_channel_estimator_test_output_ch_est103.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0761, 1.0026, 20, 20.9551, 0.0086369, 0.37435, 0, -36.0239, {"test_data/port_channel_estimator_test_input_rg104.dat"}, {"test_data/port_channel_estimator_test_pilots104.dat"}, {"test_data/port_channel_estimator_test_output_ch_est104.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.1039, 1.0098, 20, 20.0297, 0.010964, 0.43132, 210, 78.0337, {"test_data/port_channel_estimator_test_input_rg105.dat"}, {"test_data/port_channel_estimator_test_pilots105.dat"}, {"test_data/port_channel_estimator_test_output_ch_est105.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.98981, 0.99909, 20, 20.2926, 0.0092532, 0, -390, -494.0946, {"test_data/port_channel_estimator_test_input_rg106.dat"}, {"test_data/port_channel_estimator_test_pilots106.dat"}, {"test_data/port_channel_estimator_test_output_ch_est106.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0075, 1.0135, 20, 21.1536, 0.0077246, 0.056966, 810, 742.5242, {"test_data/port_channel_estimator_test_input_rg107.dat"}, {"test_data/port_channel_estimator_test_pilots107.dat"}, {"test_data/port_channel_estimator_test_output_ch_est107.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.99267, 0.99449, 20, 20.9615, 0.0079552, 0.10579, 0, {}, {"test_data/port_channel_estimator_test_input_rg108.dat"}, {"test_data/port_channel_estimator_test_pilots108.dat"}, {"test_data/port_channel_estimator_test_output_ch_est108.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0423, 1.0039, 20, 21.3743, 0.0075956, 0.26855, 210, {}, {"test_data/port_channel_estimator_test_input_rg109.dat"}, {"test_data/port_channel_estimator_test_pilots109.dat"}, {"test_data/port_channel_estimator_test_output_ch_est109.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.98004, 0.98486, 20, 22.2825, 0.0057942, 0.052897, -390, {}, {"test_data/port_channel_estimator_test_input_rg110.dat"}, {"test_data/port_channel_estimator_test_pilots110.dat"}, {"test_data/port_channel_estimator_test_output_ch_est110.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0464, 1.0075, 20, 22.0018, 0.0065999, 0.26855, 810, {}, {"test_data/port_channel_estimator_test_input_rg111.dat"}, {"test_data/port_channel_estimator_test_pilots111.dat"}, {"test_data/port_channel_estimator_test_output_ch_est111.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0143, 2.0227, 20, 20.3467, 0.0093207, 0, 0, -4.4087, {"test_data/port_channel_estimator_test_input_rg0.dat"}, {"test_data/port_channel_estimator_test_pilots0.dat"}, {"test_data/port_channel_estimator_test_output_ch_est0.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0635, 2.0306, 20, 20.6993, 0.0088038, 0.84635, 105, 116.1222, {"test_data/port_channel_estimator_test_input_rg1.dat"}, {"test_data/port_channel_estimator_test_pilots1.dat"}, {"test_data/port_channel_estimator_test_output_ch_est1.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0791, 2.0503, 20, 19.9146, 0.010627, 1.0742, -195, -204.9691, {"test_data/port_channel_estimator_test_input_rg2.dat"}, {"test_data/port_channel_estimator_test_pilots2.dat"}, {"test_data/port_channel_estimator_test_output_ch_est2.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.024, 1.9914, 20, 20.2424, 0.0095935, 0.86263, 405, 398.004, {"test_data/port_channel_estimator_test_input_rg3.dat"}, {"test_data/port_channel_estimator_test_pilots3.dat"}, {"test_data/port_channel_estimator_test_output_ch_est3.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0157, 2.0239, 20, 20.0101, 0.010079, 0.097656, 0, -8.9659, {"test_data/port_channel_estimator_test_input_rg4.dat"}, {"test_data/port_channel_estimator_test_pilots4.dat"}, {"test_data/port_channel_estimator_test_output_ch_est4.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0578, 2.0249, 20, 20.3531, 0.0095082, 0.84635, 105, 105.5152, {"test_data/port_channel_estimator_test_input_rg5.dat"}, {"test_data/port_channel_estimator_test_pilots5.dat"}, {"test_data/port_channel_estimator_test_output_ch_est5.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0202, 2.0275, 20, 20.9718, 0.0080948, 0.10579, -195, -188.1324, {"test_data/port_channel_estimator_test_input_rg6.dat"}, {"test_data/port_channel_estimator_test_pilots6.dat"}, {"test_data/port_channel_estimator_test_output_ch_est6.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0204, 1.9896, 20, 19.556, 0.011216, 0.96842, 405, 407.0491, {"test_data/port_channel_estimator_test_input_rg7.dat"}, {"test_data/port_channel_estimator_test_pilots7.dat"}, {"test_data/port_channel_estimator_test_output_ch_est7.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9704, 1.9827, 20, 18.5265, 0.013865, 0.10579, 0, -30.0356, {"test_data/port_channel_estimator_test_input_rg8.dat"}, {"test_data/port_channel_estimator_test_pilots8.dat"}, {"test_data/port_channel_estimator_test_output_ch_est8.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0094, 2.0225, 20, 19.267, 0.011922, 0.056966, 210, 198.7048, {"test_data/port_channel_estimator_test_input_rg9.dat"}, {"test_data/port_channel_estimator_test_pilots9.dat"}, {"test_data/port_channel_estimator_test_output_ch_est9.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9805, 1.9845, 20, 19.2363, 0.011834, 0.15462, -390, -403.9435, {"test_data/port_channel_estimator_test_input_rg10.dat"}, {"test_data/port_channel_estimator_test_pilots10.dat"}, {"test_data/port_channel_estimator_test_output_ch_est10.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9975, 2.0086, 20, 19.7488, 0.010607, 0.048828, 810, 809.7705, {"test_data/port_channel_estimator_test_input_rg11.dat"}, {"test_data/port_channel_estimator_test_pilots11.dat"}, {"test_data/port_channel_estimator_test_output_ch_est11.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0093, 1.9907, 20, 19.7619, 0.010638, 0.31738, 0, 2.851, {"test_data/port_channel_estimator_test_input_rg12.dat"}, {"test_data/port_channel_estimator_test_pilots12.dat"}, {"test_data/port_channel_estimator_test_output_ch_est12.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0158, 1.9875, 20, 19.7113, 0.010797, 0.42318, 210, 211.648, {"test_data/port_channel_estimator_test_input_rg13.dat"}, {"test_data/port_channel_estimator_test_pilots13.dat"}, {"test_data/port_channel_estimator_test_output_ch_est13.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.999, 2.0061, 20, 20.1501, 0.0096786, 0.052897, -390, -395.3968, {"test_data/port_channel_estimator_test_input_rg14.dat"}, {"test_data/port_channel_estimator_test_pilots14.dat"}, {"test_data/port_channel_estimator_test_output_ch_est14.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0153, 2.0098, 20, 21.0956, 0.0078485, 0.20752, 810, 812.19, {"test_data/port_channel_estimator_test_input_rg15.dat"}, {"test_data/port_channel_estimator_test_pilots15.dat"}, {"test_data/port_channel_estimator_test_output_ch_est15.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0487, 2.0175, 20, 19.9447, 0.0104, 1.0742, 0, -1.6279, {"test_data/port_channel_estimator_test_input_rg16.dat"}, {"test_data/port_channel_estimator_test_pilots16.dat"}, {"test_data/port_channel_estimator_test_output_ch_est16.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9887, 1.997, 20, 20.2945, 0.0093135, 0.11393, 105, 108.8448, {"test_data/port_channel_estimator_test_input_rg17.dat"}, {"test_data/port_channel_estimator_test_pilots17.dat"}, {"test_data/port_channel_estimator_test_output_ch_est17.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0118, 2.0174, 20, 20.4065, 0.0091819, 0.21159, -195, -195.2135, {"test_data/port_channel_estimator_test_input_rg18.dat"}, {"test_data/port_channel_estimator_test_pilots18.dat"}, {"test_data/port_channel_estimator_test_output_ch_est18.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9995, 2.0009, 20, 20.2794, 0.009397, 0.32552, 405, 398.3487, {"test_data/port_channel_estimator_test_input_rg19.dat"}, {"test_data/port_channel_estimator_test_pilots19.dat"}, {"test_data/port_channel_estimator_test_output_ch_est19.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9938, 2.002, 20, 20.6591, 0.0085859, 0.11393, 0, -4.2054, {"test_data/port_channel_estimator_test_input_rg20.dat"}, {"test_data/port_channel_estimator_test_pilots20.dat"}, {"test_data/port_channel_estimator_test_output_ch_est20.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0121, 2.0199, 20, 20.6436, 0.0086956, 0.11393, 105, 101.7418, {"test_data/port_channel_estimator_test_input_rg21.dat"}, {"test_data/port_channel_estimator_test_pilots21.dat"}, {"test_data/port_channel_estimator_test_output_ch_est21.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0016, 2.0079, 20, 20.2485, 0.0094738, 0.21159, -195, -191.3504, {"test_data/port_channel_estimator_test_input_rg22.dat"}, {"test_data/port_channel_estimator_test_pilots22.dat"}, {"test_data/port_channel_estimator_test_output_ch_est22.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9904, 1.9954, 20, 20.7301, 0.0084319, 0.21159, 405, 407.3559, {"test_data/port_channel_estimator_test_input_rg23.dat"}, {"test_data/port_channel_estimator_test_pilots23.dat"}, {"test_data/port_channel_estimator_test_output_ch_est23.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0173, 2.0124, 20, 20.5619, 0.0088834, 0.21159, 0, -5.2553, {"test_data/port_channel_estimator_test_input_rg24.dat"}, {"test_data/port_channel_estimator_test_pilots24.dat"}, {"test_data/port_channel_estimator_test_output_ch_est24.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9931, 2.0024, 20, 20.4725, 0.0089595, 0, 210, 208.5308, {"test_data/port_channel_estimator_test_input_rg25.dat"}, {"test_data/port_channel_estimator_test_pilots25.dat"}, {"test_data/port_channel_estimator_test_output_ch_est25.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0053, 2.0069, 20, 19.996, 0.01006, 0.16276, -390, -398.8123, {"test_data/port_channel_estimator_test_input_rg26.dat"}, {"test_data/port_channel_estimator_test_pilots26.dat"}, {"test_data/port_channel_estimator_test_output_ch_est26.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9906, 1.9964, 20, 20.3591, 0.0091848, 0.10579, 810, 815.9771, {"test_data/port_channel_estimator_test_input_rg27.dat"}, {"test_data/port_channel_estimator_test_pilots27.dat"}, {"test_data/port_channel_estimator_test_output_ch_est27.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0057, 1.9941, 20, 20.4467, 0.0090697, 0.26855, 0, -4.5018, {"test_data/port_channel_estimator_test_input_rg28.dat"}, {"test_data/port_channel_estimator_test_pilots28.dat"}, {"test_data/port_channel_estimator_test_output_ch_est28.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9988, 2.0073, 20, 20.2114, 0.0095419, 0.056966, 210, 217.2379, {"test_data/port_channel_estimator_test_input_rg29.dat"}, {"test_data/port_channel_estimator_test_pilots29.dat"}, {"test_data/port_channel_estimator_test_output_ch_est29.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0232, 1.9908, 20, 20.3972, 0.009254, 0.48014, -390, -385.5204, {"test_data/port_channel_estimator_test_input_rg30.dat"}, {"test_data/port_channel_estimator_test_pilots30.dat"}, {"test_data/port_channel_estimator_test_output_ch_est30.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0372, 2.0042, 20, 20.3757, 0.0093638, 0.53711, 810, 804.2148, {"test_data/port_channel_estimator_test_input_rg31.dat"}, {"test_data/port_channel_estimator_test_pilots31.dat"}, {"test_data/port_channel_estimator_test_output_ch_est31.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0435, 2.0119, 20, 20.0575, 0.010107, 1.0742, 0, 1.5958, {"test_data/port_channel_estimator_test_input_rg32.dat"}, {"test_data/port_channel_estimator_test_pilots32.dat"}, {"test_data/port_channel_estimator_test_output_ch_est32.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0382, 2.0085, 20, 20.3778, 0.0093638, 0.86263, 105, 109.0055, {"test_data/port_channel_estimator_test_input_rg33.dat"}, {"test_data/port_channel_estimator_test_pilots33.dat"}, {"test_data/port_channel_estimator_test_output_ch_est33.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.9939, 2.0033, 20, 20.2794, 0.0093706, 0, -195, -197.4148, {"test_data/port_channel_estimator_test_input_rg34.dat"}, {"test_data/port_channel_estimator_test_pilots34.dat"}, {"test_data/port_channel_estimator_test_output_ch_est34.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0325, 2.0007, 20, 20.0591, 0.010049, 0.96029, 405, 403.5268, {"test_data/port_channel_estimator_test_input_rg35.dat"}, {"test_data/port_channel_estimator_test_pilots35.dat"}, {"test_data/port_channel_estimator_test_output_ch_est35.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0004, 2.0085, 20, 20.4074, 0.009128, 0.056966, 0, 2.6347, {"test_data/port_channel_estimator_test_input_rg36.dat"}, {"test_data/port_channel_estimator_test_pilots36.dat"}, {"test_data/port_channel_estimator_test_output_ch_est36.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0414, 2.0092, 20, 20.2677, 0.0096194, 0.48014, 210, 204.1927, {"test_data/port_channel_estimator_test_input_rg37.dat"}, {"test_data/port_channel_estimator_test_pilots37.dat"}, {"test_data/port_channel_estimator_test_output_ch_est37.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0187, 2.0139, 20, 20.4222, 0.00918, 0.21159, -390, -391.2151, {"test_data/port_channel_estimator_test_input_rg38.dat"}, {"test_data/port_channel_estimator_test_pilots38.dat"}, {"test_data/port_channel_estimator_test_output_ch_est38.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 2.0187, 2.0142, 20, 20.2277, 0.0096007, 0.21159, 810, 809.1345, {"test_data/port_channel_estimator_test_input_rg39.dat"}, {"test_data/port_channel_estimator_test_pilots39.dat"}, {"test_data/port_channel_estimator_test_output_ch_est39.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.92667, 1.0079, 20, 10.3881, 0.084744, 0, 0, 17.8793, {"test_data/port_channel_estimator_test_input_rg40.dat"}, {"test_data/port_channel_estimator_test_pilots40.dat"}, {"test_data/port_channel_estimator_test_output_ch_est40.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.98288, 0.99546, 20, 18.7455, 0.013121, 0, 105, 100.1957, {"test_data/port_channel_estimator_test_input_rg41.dat"}, {"test_data/port_channel_estimator_test_pilots41.dat"}, {"test_data/port_channel_estimator_test_output_ch_est41.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.98872, 1.0176, 20, 15.163, 0.030114, 0, -195, -249.8832, {"test_data/port_channel_estimator_test_input_rg42.dat"}, {"test_data/port_channel_estimator_test_pilots42.dat"}, {"test_data/port_channel_estimator_test_output_ch_est42.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.8379, 0.99569, 20, 7.0663, 0.16465, 0, 405, 458.8566, {"test_data/port_channel_estimator_test_input_rg43.dat"}, {"test_data/port_channel_estimator_test_pilots43.dat"}, {"test_data/port_channel_estimator_test_output_ch_est43.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.92702, 0.97098, 20, 13.0561, 0.045865, 0, 0, {}, {"test_data/port_channel_estimator_test_input_rg44.dat"}, {"test_data/port_channel_estimator_test_pilots44.dat"}, {"test_data/port_channel_estimator_test_output_ch_est44.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.96481, 0.97192, 20, 21.1381, 0.0074239, 0, 105, {}, {"test_data/port_channel_estimator_test_input_rg45.dat"}, {"test_data/port_channel_estimator_test_pilots45.dat"}, {"test_data/port_channel_estimator_test_output_ch_est45.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.98636, 1.0385, 20, 12.5825, 0.054424, 0, -195, {}, {"test_data/port_channel_estimator_test_input_rg46.dat"}, {"test_data/port_channel_estimator_test_pilots46.dat"}, {"test_data/port_channel_estimator_test_output_ch_est46.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 1.0025, 1.0136, 20, 19.3678, 0.011596, 0, 405, {}, {"test_data/port_channel_estimator_test_input_rg47.dat"}, {"test_data/port_channel_estimator_test_pilots47.dat"}, {"test_data/port_channel_estimator_test_output_ch_est47.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.9379, 1.009, 20, 11.0155, 0.074234, 0, 0, 141.6522, {"test_data/port_channel_estimator_test_input_rg48.dat"}, {"test_data/port_channel_estimator_test_pilots48.dat"}, {"test_data/port_channel_estimator_test_output_ch_est48.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.90768, 1.0274, 20, 8.614, 0.12489, 0, 210, 162.3948, {"test_data/port_channel_estimator_test_input_rg49.dat"}, {"test_data/port_channel_estimator_test_pilots49.dat"}, {"test_data/port_channel_estimator_test_output_ch_est49.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.99797, 1.0129, 20, 18.0514, 0.015631, 0, -390, -371.8697, {"test_data/port_channel_estimator_test_input_rg50.dat"}, {"test_data/port_channel_estimator_test_pilots50.dat"}, {"test_data/port_channel_estimator_test_output_ch_est50.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.9157, 1.0035, 20, 9.9976, 0.091621, 0, 810, 785.3555, {"test_data/port_channel_estimator_test_input_rg51.dat"}, {"test_data/port_channel_estimator_test_pilots51.dat"}, {"test_data/port_channel_estimator_test_output_ch_est51.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.94304, 1.0386, 20, 9.7579, 0.09971, 0, 0, {}, {"test_data/port_channel_estimator_test_input_rg52.dat"}, {"test_data/port_channel_estimator_test_pilots52.dat"}, {"test_data/port_channel_estimator_test_output_ch_est52.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.9393, 0.99071, 20, 12.4326, 0.053647, 0, 210, {}, {"test_data/port_channel_estimator_test_input_rg53.dat"}, {"test_data/port_channel_estimator_test_pilots53.dat"}, {"test_data/port_channel_estimator_test_output_ch_est53.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.90854, 1.0178, 20, 9.0127, 0.11404, 0, -390, {}, {"test_data/port_channel_estimator_test_input_rg54.dat"}, {"test_data/port_channel_estimator_test_pilots54.dat"}, {"test_data/port_channel_estimator_test_output_ch_est54.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 1.0539, 1.0641, 20, 19.957, 0.010643, 0, 810, {}, {"test_data/port_channel_estimator_test_input_rg55.dat"}, {"test_data/port_channel_estimator_test_pilots55.dat"}, {"test_data/port_channel_estimator_test_output_ch_est55.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.96191, 1.0109, 20, 12.8743, 0.049626, 0, 0, 60.4937, {"test_data/port_channel_estimator_test_input_rg56.dat"}, {"test_data/port_channel_estimator_test_pilots56.dat"}, {"test_data/port_channel_estimator_test_output_ch_est56.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.92806, 0.97217, 20, 13.1785, 0.04464, 0, 105, 133.8908, {"test_data/port_channel_estimator_test_input_rg57.dat"}, {"test_data/port_channel_estimator_test_pilots57.dat"}, {"test_data/port_channel_estimator_test_output_ch_est57.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.8481, 0.97469, 20, 8.2085, 0.12811, 0, -195, -157.735, {"test_data/port_channel_estimator_test_input_rg58.dat"}, {"test_data/port_channel_estimator_test_pilots58.dat"}, {"test_data/port_channel_estimator_test_output_ch_est58.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.5525, 1.0007, 20, 0.85624, 0.45364, 0, 405, 423.366, {"test_data/port_channel_estimator_test_input_rg59.dat"}, {"test_data/port_channel_estimator_test_pilots59.dat"}, {"test_data/port_channel_estimator_test_output_ch_est59.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.99336, 1.0019, 20, 20.587, 0.0086778, 0, 0, -0.86513, {"test_data/port_channel_estimator_test_input_rg60.dat"}, {"test_data/port_channel_estimator_test_pilots60.dat"}, {"test_data/port_channel_estimator_test_output_ch_est60.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.97749, 1.0007, 20, 16.1878, 0.023515, 0, 105, 116.191, {"test_data/port_channel_estimator_test_input_rg61.dat"}, {"test_data/port_channel_estimator_test_pilots61.dat"}, {"test_data/port_channel_estimator_test_output_ch_est61.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.90884, 1.0059, 20, 9.6636, 0.098204, 0, -195, -195.3017, {"test_data/port_channel_estimator_test_input_rg62.dat"}, {"test_data/port_channel_estimator_test_pilots62.dat"}, {"test_data/port_channel_estimator_test_output_ch_est62.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.79189, 1.0323, 20, 5.1249, 0.24332, 0, 405, 436.0944, {"test_data/port_channel_estimator_test_input_rg63.dat"}, {"test_data/port_channel_estimator_test_pilots63.dat"}, {"test_data/port_channel_estimator_test_output_ch_est63.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.98671, 1.0077, 20, 16.6618, 0.021282, 0, 0, 38.8966, {"test_data/port_channel_estimator_test_input_rg64.dat"}, {"test_data/port_channel_estimator_test_pilots64.dat"}, {"test_data/port_channel_estimator_test_output_ch_est64.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.92538, 1.0131, 20, 10.1789, 0.088803, 0, 210, 261.8987, {"test_data/port_channel_estimator_test_input_rg65.dat"}, {"test_data/port_channel_estimator_test_pilots65.dat"}, {"test_data/port_channel_estimator_test_output_ch_est65.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.86183, 1.0104, 20, 7.583, 0.15036, 0, -390, -338.1823, {"test_data/port_channel_estimator_test_input_rg66.dat"}, {"test_data/port_channel_estimator_test_pilots66.dat"}, {"test_data/port_channel_estimator_test_output_ch_est66.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.51508, 1.011, 20, 0.11269, 0.50189, 0, 810, 778.57, {"test_data/port_channel_estimator_test_input_rg67.dat"}, {"test_data/port_channel_estimator_test_pilots67.dat"}, {"test_data/port_channel_estimator_test_output_ch_est67.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.9677, 1.022, 20, 12.4575, 0.054953, 0, 0, 36.887, {"test_data/port_channel_estimator_test_input_rg68.dat"}, {"test_data/port_channel_estimator_test_pilots68.dat"}, {"test_data/port_channel_estimator_test_output_ch_est68.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.97489, 1.001, 20, 15.6744, 0.026394, 0, 210, 201.1862, {"test_data/port_channel_estimator_test_input_rg69.dat"}, {"test_data/port_channel_estimator_test_pilots69.dat"}, {"test_data/port_channel_estimator_test_output_ch_est69.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.95291, 1.0053, 20, 12.5452, 0.053031, 0, -390, -471.9865, {"test_data/port_channel_estimator_test_input_rg70.dat"}, {"test_data/port_channel_estimator_test_pilots70.dat"}, {"test_data/port_channel_estimator_test_output_ch_est70.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, 0, 52, 0.78307, 1.0071, 20, 5.3828, 0.22673, 0, 810, 739.4834, {"test_data/port_channel_estimator_test_input_rg71.dat"}, {"test_data/port_channel_estimator_test_pilots71.dat"}, {"test_data/port_channel_estimator_test_output_ch_est71.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.90818, 0.96077, 20, 23.4291, 0.0041235, 1.0905, 0, -77.8652, {"test_data/port_channel_estimator_test_input_rg72.dat"}, {"test_data/port_channel_estimator_test_pilots72.dat"}, {"test_data/port_channel_estimator_test_output_ch_est72.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.9917, 1.0279, 20, 18.6853, 0.013423, 0.87891, 105, -21.1131, {"test_data/port_channel_estimator_test_input_rg73.dat"}, {"test_data/port_channel_estimator_test_pilots73.dat"}, {"test_data/port_channel_estimator_test_output_ch_est73.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.99378, 1.0461, 20, 21.8448, 0.0064984, 1.0091, -195, -185.0026, {"test_data/port_channel_estimator_test_input_rg74.dat"}, {"test_data/port_channel_estimator_test_pilots74.dat"}, {"test_data/port_channel_estimator_test_output_ch_est74.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.91883, 0.91961, 20, 20.3598, 0.0084578, 0.11393, 405, 261.1756, {"test_data/port_channel_estimator_test_input_rg75.dat"}, {"test_data/port_channel_estimator_test_pilots75.dat"}, {"test_data/port_channel_estimator_test_output_ch_est75.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.89601, 0.94902, 20, 21.6617, 0.0061114, 1.001, 0, {}, {"test_data/port_channel_estimator_test_input_rg76.dat"}, {"test_data/port_channel_estimator_test_pilots76.dat"}, {"test_data/port_channel_estimator_test_output_ch_est76.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0014, 1.0106, 20, 22.8022, 0.0052526, 0.08138, 105, {}, {"test_data/port_channel_estimator_test_input_rg77.dat"}, {"test_data/port_channel_estimator_test_pilots77.dat"}, {"test_data/port_channel_estimator_test_output_ch_est77.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0065, 1.0107, 20, 25.2981, 0.0029716, 0.04069, -195, {}, {"test_data/port_channel_estimator_test_input_rg78.dat"}, {"test_data/port_channel_estimator_test_pilots78.dat"}, {"test_data/port_channel_estimator_test_output_ch_est78.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.9985, 1.052, 20, 21.1924, 0.0075877, 0.93587, 405, {}, {"test_data/port_channel_estimator_test_input_rg79.dat"}, {"test_data/port_channel_estimator_test_pilots79.dat"}, {"test_data/port_channel_estimator_test_output_ch_est79.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.94751, 0.98757, 20, 18.0578, 0.014818, 0.39062, 0, 328.1227, {"test_data/port_channel_estimator_test_input_rg80.dat"}, {"test_data/port_channel_estimator_test_pilots80.dat"}, {"test_data/port_channel_estimator_test_output_ch_est80.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0419, 1.0425, 20, 21.1019, 0.0080841, 0.048828, 210, 161.7025, {"test_data/port_channel_estimator_test_input_rg81.dat"}, {"test_data/port_channel_estimator_test_pilots81.dat"}, {"test_data/port_channel_estimator_test_output_ch_est81.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.94052, 0.9704, 20, 17.2206, 0.017836, 0.21973, -390, -386.9386, {"test_data/port_channel_estimator_test_input_rg82.dat"}, {"test_data/port_channel_estimator_test_pilots82.dat"}, {"test_data/port_channel_estimator_test_output_ch_est82.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.99352, 1.0204, 20, 19.0758, 0.012291, 0.19531, 810, 1327.7275, {"test_data/port_channel_estimator_test_input_rg83.dat"}, {"test_data/port_channel_estimator_test_pilots83.dat"}, {"test_data/port_channel_estimator_test_output_ch_est83.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0474, 1.1004, 20, 22.5891, 0.0057701, 0.49235, 0, {}, {"test_data/port_channel_estimator_test_input_rg84.dat"}, {"test_data/port_channel_estimator_test_pilots84.dat"}, {"test_data/port_channel_estimator_test_output_ch_est84.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.955, 0.96624, 20, 20.4317, 0.0086464, -0.012207, 210, {}, {"test_data/port_channel_estimator_test_input_rg85.dat"}, {"test_data/port_channel_estimator_test_pilots85.dat"}, {"test_data/port_channel_estimator_test_output_ch_est85.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.98178, 0.97926, 20, 23.2012, 0.0046978, 0.073242, -390, {}, {"test_data/port_channel_estimator_test_input_rg86.dat"}, {"test_data/port_channel_estimator_test_pilots86.dat"}, {"test_data/port_channel_estimator_test_output_ch_est86.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.9291, 0.935, 20, 22.2528, 0.0055307, 0.24007, 810, {}, {"test_data/port_channel_estimator_test_input_rg87.dat"}, {"test_data/port_channel_estimator_test_pilots87.dat"}, {"test_data/port_channel_estimator_test_output_ch_est87.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.1114, 1.0514, 20, 22.0965, 0.0068587, 0.65104, 0, {}, {"test_data/port_channel_estimator_test_input_rg88.dat"}, {"test_data/port_channel_estimator_test_pilots88.dat"}, {"test_data/port_channel_estimator_test_output_ch_est88.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0805, 0.97166, 20, 20.2555, 0.010187, 0.96029, 105, {}, {"test_data/port_channel_estimator_test_input_rg89.dat"}, {"test_data/port_channel_estimator_test_pilots89.dat"}, {"test_data/port_channel_estimator_test_output_ch_est89.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.1009, 1.076, 20, 23.5785, 0.0048292, 0.42318, -195, {}, {"test_data/port_channel_estimator_test_input_rg90.dat"}, {"test_data/port_channel_estimator_test_pilots90.dat"}, {"test_data/port_channel_estimator_test_output_ch_est90.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.98669, 0.9906, 20, 23.144, 0.0047839, 0.11393, 405, {}, {"test_data/port_channel_estimator_test_input_rg91.dat"}, {"test_data/port_channel_estimator_test_pilots91.dat"}, {"test_data/port_channel_estimator_test_output_ch_est91.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0078, 0.99632, 20, 22.5326, 0.005625, 0.15462, 0, {}, {"test_data/port_channel_estimator_test_input_rg92.dat"}, {"test_data/port_channel_estimator_test_pilots92.dat"}, {"test_data/port_channel_estimator_test_output_ch_est92.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0113, 1.0185, 20, 20.553, 0.008904, 0, 210, {}, {"test_data/port_channel_estimator_test_input_rg93.dat"}, {"test_data/port_channel_estimator_test_pilots93.dat"}, {"test_data/port_channel_estimator_test_output_ch_est93.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0376, 1.0327, 20, 22.4415, 0.005914, 0.10579, -390, {}, {"test_data/port_channel_estimator_test_input_rg94.dat"}, {"test_data/port_channel_estimator_test_pilots94.dat"}, {"test_data/port_channel_estimator_test_output_ch_est94.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0591, 0.98466, 20, 20.4074, 0.009643, 0.37435, 810, {}, {"test_data/port_channel_estimator_test_input_rg95.dat"}, {"test_data/port_channel_estimator_test_pilots95.dat"}, {"test_data/port_channel_estimator_test_output_ch_est95.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0153, 1.0161, 20, 20.4588, 0.0091356, 0.21159, 0, 37.2051, {"test_data/port_channel_estimator_test_input_rg96.dat"}, {"test_data/port_channel_estimator_test_pilots96.dat"}, {"test_data/port_channel_estimator_test_output_ch_est96.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0143, 1.0215, 20, 20.2096, 0.0096654, 0.11393, 105, 114.1783, {"test_data/port_channel_estimator_test_input_rg97.dat"}, {"test_data/port_channel_estimator_test_pilots97.dat"}, {"test_data/port_channel_estimator_test_output_ch_est97.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.1163, 1.0205, 20, 20.1495, 0.010785, 0.86263, -195, -146.8239, {"test_data/port_channel_estimator_test_input_rg98.dat"}, {"test_data/port_channel_estimator_test_pilots98.dat"}, {"test_data/port_channel_estimator_test_output_ch_est98.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0155, 1.0217, 20, 20.8441, 0.0083614, 0.11393, 405, 448.619, {"test_data/port_channel_estimator_test_input_rg99.dat"}, {"test_data/port_channel_estimator_test_pilots99.dat"}, {"test_data/port_channel_estimator_test_output_ch_est99.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0445, 0.98772, 20, 21.9494, 0.0066673, 0.6429, 0, {}, {"test_data/port_channel_estimator_test_input_rg100.dat"}, {"test_data/port_channel_estimator_test_pilots100.dat"}, {"test_data/port_channel_estimator_test_output_ch_est100.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0608, 1.0055, 20, 21.2982, 0.0078669, 0.63477, 105, {}, {"test_data/port_channel_estimator_test_input_rg101.dat"}, {"test_data/port_channel_estimator_test_pilots101.dat"}, {"test_data/port_channel_estimator_test_output_ch_est101.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0566, 1.0007, 20, 21.4424, 0.0075803, 0.6429, -195, {}, {"test_data/port_channel_estimator_test_input_rg102.dat"}, {"test_data/port_channel_estimator_test_pilots102.dat"}, {"test_data/port_channel_estimator_test_output_ch_est102.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0666, 1.01, 20, 21.8603, 0.0069497, 0.63477, 405, {}, {"test_data/port_channel_estimator_test_input_rg103.dat"}, {"test_data/port_channel_estimator_test_pilots103.dat"}, {"test_data/port_channel_estimator_test_output_ch_est103.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0762, 1.0027, 20, 20.9423, 0.0086632, 0.37435, 0, -36.5569, {"test_data/port_channel_estimator_test_input_rg104.dat"}, {"test_data/port_channel_estimator_test_pilots104.dat"}, {"test_data/port_channel_estimator_test_output_ch_est104.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.104, 1.0099, 20, 20.0255, 0.010975, 0.43132, 210, 78.3938, {"test_data/port_channel_estimator_test_input_rg105.dat"}, {"test_data/port_channel_estimator_test_pilots105.dat"}, {"test_data/port_channel_estimator_test_output_ch_est105.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.98938, 0.99864, 20, 20.3087, 0.0092149, 0, -390, -494.4192, {"test_data/port_channel_estimator_test_input_rg106.dat"}, {"test_data/port_channel_estimator_test_pilots106.dat"}, {"test_data/port_channel_estimator_test_output_ch_est106.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0075, 1.0135, 20, 21.1577, 0.0077172, 0.056966, 810, 743.568, {"test_data/port_channel_estimator_test_input_rg107.dat"}, {"test_data/port_channel_estimator_test_pilots107.dat"}, {"test_data/port_channel_estimator_test_output_ch_est107.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.99234, 0.9942, 20, 20.9477, 0.007978, 0.10579, 0, {}, {"test_data/port_channel_estimator_test_input_rg108.dat"}, {"test_data/port_channel_estimator_test_pilots108.dat"}, {"test_data/port_channel_estimator_test_output_ch_est108.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0422, 1.0038, 20, 21.3842, 0.0075773, 0.26855, 210, {}, {"test_data/port_channel_estimator_test_input_rg109.dat"}, {"test_data/port_channel_estimator_test_pilots109.dat"}, {"test_data/port_channel_estimator_test_output_ch_est109.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 0.97966, 0.98446, 20, 22.2941, 0.0057765, 0.052897, -390, {}, {"test_data/port_channel_estimator_test_input_rg110.dat"}, {"test_data/port_channel_estimator_test_pilots110.dat"}, {"test_data/port_channel_estimator_test_output_ch_est110.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, 1, 52, 1.0467, 1.0077, 20, 22.0156, 0.0065804, 0.26855, 810, {}, {"test_data/port_channel_estimator_test_input_rg111.dat"}, {"test_data/port_channel_estimator_test_pilots111.dat"}, {"test_data/port_channel_estimator_test_output_ch_est111.dat"}}, // clang-format on }; diff --git a/tests/unittests/radio/zmq/radio_zmq_loopback_test.cpp b/tests/unittests/radio/zmq/radio_zmq_loopback_test.cpp index efccbd2f2b..27ddf5688a 100644 --- a/tests/unittests/radio/zmq/radio_zmq_loopback_test.cpp +++ b/tests/unittests/radio/zmq/radio_zmq_loopback_test.cpp @@ -42,7 +42,7 @@ using namespace srsran; using radio_zmq_e2e_test_parameters = std::tuple; /// Indicates the test logging level. -static const std::string log_level = "warning"; +static const srslog::basic_levels log_level = srslog::basic_levels::warning; class RadioZmqE2EFixture : public ::testing::TestWithParam { @@ -100,7 +100,7 @@ class RadioZmqE2EFixture : public ::testing::TestWithParam get_config; @@ -241,12 +241,6 @@ const std::vector radio_zmq_validator_test_data = { return config; }, "Only default OTW format is currently supported.\n"}, - {[] { - radio_configuration::radio config = radio_base_config; - config.log_level = "some invalid log level"; - return config; - }, - "Log level some invalid log level does not correspond to an actual logger level.\n"}, {[] { radio_configuration::radio config = radio_base_config; config.tx_mode = radio_configuration::transmission_mode::discontinuous; diff --git a/tests/unittests/ran/CMakeLists.txt b/tests/unittests/ran/CMakeLists.txt index b88a79621a..1f607c272d 100644 --- a/tests/unittests/ran/CMakeLists.txt +++ b/tests/unittests/ran/CMakeLists.txt @@ -130,10 +130,6 @@ add_executable(srs_bandwidth_configuration_test srs/srs_bandwidth_configuration_ target_link_libraries(srs_bandwidth_configuration_test srsran_ran srslog gtest gtest_main) add_test(srs_bandwidth_configuration_test srs_bandwidth_configuration_test) -add_executable(bcd_helpers_test bcd_helpers_test.cpp) -target_link_libraries(bcd_helpers_test srsran_support gtest gtest_main) -gtest_discover_tests(bcd_helpers_test) - -add_executable(nr_cgi_helpers_test nr_cgi_helpers_test.cpp) -target_link_libraries(nr_cgi_helpers_test srsran_support gtest gtest_main) -gtest_discover_tests(nr_cgi_helpers_test) +add_executable(cell_identity_test cell_identity_test.cpp) +target_link_libraries(cell_identity_test srsran_support gtest gtest_main) +add_test(cell_identity_test cell_identity_test) \ No newline at end of file diff --git a/tests/unittests/ran/band_helper_test.cpp b/tests/unittests/ran/band_helper_test.cpp index 155bbab740..c4ac858455 100644 --- a/tests/unittests/ran/band_helper_test.cpp +++ b/tests/unittests/ran/band_helper_test.cpp @@ -635,14 +635,15 @@ TEST(test_get_min_channel_bw, invalid_cases) TEST(test_is_valid_ssb_arfcn, mixed_bands) { // ARFCN 427970 is a valid SSB ARFCN for n1, expect no error. - ASSERT_FALSE(band_helper::is_ssb_arfcn_valid_given_band(427970U, nr_band::n1, subcarrier_spacing::kHz15).is_error()); + ASSERT_TRUE(band_helper::is_ssb_arfcn_valid_given_band(427970U, nr_band::n1, subcarrier_spacing::kHz15).has_value()); // ARFCN 433970 is NOT a valid SSB ARFCN for n1, expect an error. - ASSERT_TRUE(band_helper::is_ssb_arfcn_valid_given_band(433970U, nr_band::n1, subcarrier_spacing::kHz15).is_error()); + ASSERT_FALSE(band_helper::is_ssb_arfcn_valid_given_band(433970U, nr_band::n1, subcarrier_spacing::kHz15).has_value()); // ARFCN 427970 is a valid SSB ARFCN for n1, expect no error. - ASSERT_FALSE(band_helper::is_ssb_arfcn_valid_given_band(755712U, nr_band::n46, subcarrier_spacing::kHz30).is_error()); + ASSERT_TRUE(band_helper::is_ssb_arfcn_valid_given_band(755712U, nr_band::n46, subcarrier_spacing::kHz30).has_value()); // ARFCN 433970 is NOT a valid SSB ARFCN for n1, expect an error. - ASSERT_TRUE(band_helper::is_ssb_arfcn_valid_given_band(785856U, nr_band::n46, subcarrier_spacing::kHz30).is_error()); + ASSERT_FALSE( + band_helper::is_ssb_arfcn_valid_given_band(785856U, nr_band::n46, subcarrier_spacing::kHz30).has_value()); } TEST(test_get_ssb_l_max, test_different_ssb_cases) diff --git a/tests/unittests/ran/cell_identity_test.cpp b/tests/unittests/ran/cell_identity_test.cpp new file mode 100644 index 0000000000..b60c6479d8 --- /dev/null +++ b/tests/unittests/ran/cell_identity_test.cpp @@ -0,0 +1,460 @@ +/* + * + * 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/ran/nr_cell_identity.h" +#include "srsran/ran/plmn_identity.h" +#include + +using namespace srsran; + +class bcd_helper_test : public ::testing::Test +{}; + +TEST_F(bcd_helper_test, is_valid_mcc_checks_bcd_integer) +{ + ASSERT_FALSE(bcd_helper::is_valid_mcc(0x0000)); + ASSERT_FALSE(bcd_helper::is_valid_mcc(0xfa00)); + ASSERT_FALSE(bcd_helper::is_valid_mcc(0xff00)); + ASSERT_TRUE(bcd_helper::is_valid_mcc(0xf000)); + ASSERT_TRUE(bcd_helper::is_valid_mcc(0xf001)); + ASSERT_TRUE(bcd_helper::is_valid_mcc(0xf100)); +} + +TEST_F(bcd_helper_test, is_valid_mcc_checks_string) +{ + ASSERT_FALSE(bcd_helper::is_valid_mcc("1234")); + ASSERT_FALSE(bcd_helper::is_valid_mcc("abc")); + ASSERT_FALSE(bcd_helper::is_valid_mcc("12a")); + ASSERT_FALSE(bcd_helper::is_valid_mcc("12")); + ASSERT_TRUE(bcd_helper::is_valid_mcc("123")); +} + +TEST_F(bcd_helper_test, is_valid_mcc_checks_bytes) +{ + ASSERT_FALSE(bcd_helper::is_valid_mcc(std::array{10, 0, 1})); + ASSERT_TRUE(bcd_helper::is_valid_mcc(std::array{1, 2, 9})); +} + +TEST_F(bcd_helper_test, is_valid_mnc_checks_bcd_integer) +{ + ASSERT_FALSE(bcd_helper::is_valid_mnc(0x0000)); + ASSERT_FALSE(bcd_helper::is_valid_mcc(0xfa00)); + ASSERT_FALSE(bcd_helper::is_valid_mcc(0xfff0)); + ASSERT_TRUE(bcd_helper::is_valid_mnc(0xf000)); + ASSERT_TRUE(bcd_helper::is_valid_mnc(0xff00)); + ASSERT_TRUE(bcd_helper::is_valid_mnc(0xff10)); +} + +TEST_F(bcd_helper_test, is_valid_mnc_checks_string) +{ + ASSERT_FALSE(bcd_helper::is_valid_mnc("1234")); + ASSERT_FALSE(bcd_helper::is_valid_mnc("abc")); + ASSERT_FALSE(bcd_helper::is_valid_mnc("ab")); + ASSERT_FALSE(bcd_helper::is_valid_mnc("12a")); + ASSERT_TRUE(bcd_helper::is_valid_mnc("12")); + ASSERT_TRUE(bcd_helper::is_valid_mnc("123")); +} + +TEST_F(bcd_helper_test, is_valid_mnc_checks_bytes) +{ + static_vector bytes = {10, 1}; + ASSERT_FALSE(bcd_helper::is_valid_mnc(bytes)); + bytes = {10, 0, 1}; + ASSERT_FALSE(bcd_helper::is_valid_mnc(bytes)); + bytes = {1, 2}; + ASSERT_TRUE(bcd_helper::is_valid_mnc(bytes)); + bytes = {1, 2, 3}; + ASSERT_TRUE(bcd_helper::is_valid_mnc(bytes)); +} + +TEST_F(bcd_helper_test, conversion_string_to_mcc) +{ + uint16_t mcc; + std::string mcc_str; + + // invalid conversions. + ASSERT_FALSE(bcd_helper::string_to_mcc("12a", &mcc)); + ASSERT_FALSE(bcd_helper::string_to_mcc("12", &mcc)); + ASSERT_FALSE(bcd_helper::string_to_mcc("1234", &mcc)); + ASSERT_FALSE(bcd_helper::mcc_to_string(0x1234, &mcc_str)); + + // valid conversions. + ASSERT_TRUE(bcd_helper::string_to_mcc("123", &mcc)); + ASSERT_EQ(mcc, 0xf123U); + ASSERT_TRUE(bcd_helper::mcc_to_string(mcc, &mcc_str)); + ASSERT_EQ(mcc_str, "123"); +} + +TEST_F(bcd_helper_test, conversion_string_to_mnc) +{ + uint16_t mnc; + std::string mnc_str; + + // invalid conversions. + ASSERT_FALSE(bcd_helper::string_to_mnc("12a", &mnc)); + ASSERT_FALSE(bcd_helper::string_to_mnc("1", &mnc)); + ASSERT_FALSE(bcd_helper::string_to_mnc("1234", &mnc)); + ASSERT_FALSE(bcd_helper::mnc_to_string(0x1234, &mnc_str)); + + // valid 2-digit conversion. + ASSERT_TRUE(bcd_helper::string_to_mnc("12", &mnc)); + ASSERT_EQ(mnc, 0xff12U); + ASSERT_TRUE(bcd_helper::mnc_to_string(mnc, &mnc_str)); + ASSERT_EQ(mnc_str, "12"); + + // valid 3-digit conversion. + ASSERT_TRUE(bcd_helper::string_to_mnc("123", &mnc)); + ASSERT_EQ(mnc, 0xf123U); + ASSERT_TRUE(bcd_helper::mnc_to_string(mnc, &mnc_str)); + ASSERT_EQ(mnc_str, "123"); +} + +TEST_F(bcd_helper_test, conversion_string_to_plmn) +{ + // invalid conversions. + ASSERT_EQ(bcd_helper::plmn_string_to_bcd("1234"), 0); + ASSERT_EQ(bcd_helper::plmn_string_to_bcd("1234567"), 0); + ASSERT_EQ(bcd_helper::plmn_string_to_bcd("12345a"), 0); + ASSERT_TRUE(bcd_helper::plmn_bcd_to_string(0x1234567).empty()); + + // valid conversions. + ASSERT_EQ(bcd_helper::plmn_string_to_bcd("12345"), 0x21f354); + ASSERT_EQ(bcd_helper::plmn_string_to_bcd("123456"), 0x214365); + ASSERT_EQ(bcd_helper::plmn_string_to_bcd("00101"), 0xf110); + ASSERT_EQ(bcd_helper::plmn_string_to_bcd("20899"), 0x2f899); + ASSERT_EQ(bcd_helper::plmn_bcd_to_string(bcd_helper::plmn_string_to_bcd("12345")), "12345"); + ASSERT_EQ(bcd_helper::plmn_bcd_to_string(bcd_helper::plmn_string_to_bcd("123456")), "123456"); + ASSERT_EQ(bcd_helper::plmn_bcd_to_string(0xf110), "00101"); + ASSERT_EQ(bcd_helper::plmn_bcd_to_string(0x2f899), "20899"); +} + +TEST_F(bcd_helper_test, conversion_plmn_to_mccmnc) +{ + uint16_t mcc, mnc; + + // invalid conversions. + ASSERT_FALSE(bcd_helper::ngap_plmn_to_mccmnc(0x1234567, &mcc, &mnc)); + ASSERT_EQ(bcd_helper::bcd_plmn_to_mcc(0x1234567), 0); + ASSERT_EQ(bcd_helper::bcd_plmn_to_mnc(0x1234567), 0); + + // valid conversions. + ASSERT_EQ(bcd_helper::bcd_plmn_to_mcc(0x21f354), 0xf123U); + ASSERT_EQ(bcd_helper::bcd_plmn_to_mnc(0x21f354), 0xff45U); + ASSERT_EQ(bcd_helper::bcd_plmn_to_mcc(0xf110), 0xf001U); + ASSERT_EQ(bcd_helper::bcd_plmn_to_mnc(0xf110), 0xff01U); + ASSERT_EQ(bcd_helper::bcd_plmn_to_mcc(0x2f899), 0xf208U); + ASSERT_EQ(bcd_helper::bcd_plmn_to_mnc(0x2f899), 0xff99U); + ASSERT_TRUE(bcd_helper::ngap_plmn_to_mccmnc(0x21f354, &mcc, &mnc)); + ASSERT_EQ(mcc, 0xf123); + ASSERT_EQ(mnc, 0xff45); +} + +class mcc_test : public ::testing::Test +{}; + +TEST_F(mcc_test, mcc_creation_from_invalid_bcd_integer_fails) +{ + auto ret = mobile_country_code::from_bcd(0x0000); + ASSERT_FALSE(ret.has_value()); +} + +TEST_F(mcc_test, mcc_creation_from_valid_bcd_integer_succeeds) +{ + auto ret = mobile_country_code::from_bcd(0xf001); + ASSERT_TRUE(ret.has_value()); + ASSERT_EQ(ret.value().to_string(), "001"); + ASSERT_EQ(ret.value().to_bcd(), 0xf001); +} + +TEST_F(mcc_test, mcc_creation_from_invalid_string_fails) +{ + auto ret = mobile_country_code::from_string("1234"); + ASSERT_FALSE(ret.has_value()); +} + +TEST_F(mcc_test, mcc_creation_from_valid_string_succeeds) +{ + auto ret = mobile_country_code::from_string("001"); + ASSERT_TRUE(ret.has_value()); + ASSERT_EQ(ret.value().to_string(), "001"); + ASSERT_EQ(ret.value().to_bcd(), 0xf001); +} + +TEST_F(mcc_test, mcc_creation_from_invalid_bytes_fails) +{ + std::array bytes = {20, 21, 1}; + auto ret = mobile_country_code::from_bytes(bytes); + ASSERT_FALSE(ret.has_value()); +} + +TEST_F(mcc_test, mcc_creation_from_valid_bytes_succeeds) +{ + std::array bytes = {0, 0, 1}; + auto ret = mobile_country_code::from_bytes(bytes); + ASSERT_TRUE(ret.has_value()); + mobile_country_code mcc = ret.value(); + ASSERT_EQ(mcc.to_string(), "001"); + ASSERT_EQ(mcc.to_bcd(), 0xf001); + ASSERT_EQ(mcc.to_bytes(), bytes); +} + +class mnc_test : public ::testing::Test +{}; + +TEST_F(mnc_test, mnc_creation_from_invalid_bcd_integer_fails) +{ + auto ret = mobile_network_code::from_bcd(0x0000); + ASSERT_FALSE(ret.has_value()); +} + +TEST_F(mnc_test, mnc_creation_from_valid_2_digit_bcd_integer_succeeds) +{ + auto ret = mobile_network_code::from_bcd(0xff01U); + ASSERT_TRUE(ret.has_value()); + mobile_network_code mnc = ret.value(); + ASSERT_EQ(mnc.to_string(), "01"); + ASSERT_EQ(mnc.to_bcd(), 0xff01U); + ASSERT_EQ(mnc.nof_digits(), 2); +} + +TEST_F(mnc_test, mnc_creation_from_valid_3_digit_bcd_integer_succeeds) +{ + auto ret = mobile_network_code::from_bcd(0xf001U); + ASSERT_TRUE(ret.has_value()); + mobile_network_code mnc = ret.value(); + ASSERT_EQ(mnc.to_string(), "001"); + ASSERT_EQ(mnc.to_bcd(), 0xf001U); + ASSERT_EQ(mnc.nof_digits(), 3); +} + +TEST_F(mnc_test, mnc_creation_from_invalid_string_fails) +{ + auto ret = mobile_network_code::from_string("1234"); + ASSERT_FALSE(ret.has_value()); +} + +TEST_F(mnc_test, mnc_creation_from_valid_2_digit_string_succeeds) +{ + auto ret = mobile_network_code::from_string("01"); + ASSERT_TRUE(ret.has_value()); + mobile_network_code mnc = ret.value(); + ASSERT_EQ(mnc.to_string(), "01"); + ASSERT_EQ(mnc.to_bcd(), 0xff01U); + ASSERT_EQ(mnc.nof_digits(), 2); +} + +TEST_F(mnc_test, mnc_creation_from_valid_3_digit_string_succeeds) +{ + auto ret = mobile_network_code::from_string("001"); + ASSERT_TRUE(ret.has_value()); + mobile_network_code mnc = ret.value(); + ASSERT_EQ(mnc.to_string(), "001"); + ASSERT_EQ(mnc.to_bcd(), 0xf001U); + ASSERT_EQ(mnc.nof_digits(), 3); +} + +TEST_F(mnc_test, mnc_creation_from_invalid_bytes_fails) +{ + std::array bytes = {21, 1}; + auto ret = mobile_network_code::from_bytes(bytes); + ASSERT_FALSE(ret.has_value()); +} + +TEST_F(mnc_test, mnc_creation_from_valid_2_digit_bytes_succeeds) +{ + static_vector bytes = {0, 1}; + auto ret = mobile_network_code::from_bytes(bytes); + ASSERT_TRUE(ret.has_value()); + mobile_network_code mnc = ret.value(); + ASSERT_EQ(mnc.to_string(), "01"); + ASSERT_EQ(mnc.to_bcd(), 0xff01U); + ASSERT_EQ(mnc.to_bytes(), bytes); +} + +TEST_F(mnc_test, mnc_creation_from_valid_3_digit_bytes_succeeds) +{ + static_vector bytes = {0, 0, 1}; + auto ret = mobile_network_code::from_bytes(bytes); + ASSERT_TRUE(ret.has_value()); + mobile_network_code mnc = ret.value(); + ASSERT_EQ(mnc.to_string(), "001"); + ASSERT_EQ(mnc.to_bcd(), 0xf001U); + ASSERT_EQ(mnc.to_bytes(), bytes); +} + +class plmn_id_test : public ::testing::Test +{}; + +TEST_F(plmn_id_test, plmn_id_creation_from_invalid_string_fails) +{ + auto ret = plmn_identity::parse("1234567"); + ASSERT_FALSE(ret.has_value()); +} + +TEST_F(plmn_id_test, plmn_id_creation_from_valid_string_succeeds) +{ + auto ret = plmn_identity::parse("00101"); + ASSERT_TRUE(ret.has_value()); + plmn_identity plmn = ret.value(); + ASSERT_EQ(plmn.to_bcd(), 0xf110U); + ASSERT_EQ(plmn.to_string(), "00101"); + ASSERT_EQ(plmn.mcc().to_string(), "001"); + ASSERT_EQ(plmn.mnc().to_string(), "01"); +} + +TEST_F(plmn_id_test, plmn_id_creation_from_mnc_mcc_succeeds) +{ + plmn_identity plmn{mobile_country_code::from_string("001").value(), mobile_network_code::from_string("01").value()}; + ASSERT_EQ(plmn.to_bcd(), 0xf110U); + ASSERT_EQ(plmn.to_string(), "00101"); + ASSERT_EQ(plmn.mcc().to_string(), "001"); + ASSERT_EQ(plmn.mnc().to_string(), "01"); +} + +TEST_F(plmn_id_test, plmn_id_creation_from_invalid_bytes_fails) +{ + auto ret = plmn_identity::from_bytes({0x00, 0x1f, 0x0a}); + ASSERT_FALSE(ret.has_value()); + ret = plmn_identity::from_bytes({0x00, 0xdf, 0x01}); +} + +TEST_F(plmn_id_test, plmn_id_creation_from_valid_bytes_succeeds) +{ + plmn_identity plmn = plmn_identity::from_bytes({0x00, 0xf1, 0x10}).value(); + ASSERT_EQ(plmn.to_string(), "00101"); + ASSERT_EQ(plmn.to_bcd(), 0xf110); + plmn = plmn_identity::from_bytes({0x00, 0x01, 0x10}).value(); + ASSERT_EQ(plmn.to_string(), "001001"); + ASSERT_EQ(plmn.to_bcd(), 0x0110); + plmn = plmn_identity::from_bytes({0x21, 0x43, 0x65}).value(); + ASSERT_EQ(plmn.to_string(), "123456"); + ASSERT_EQ(plmn.to_bcd(), 0x214365); +} + +TEST_F(plmn_id_test, plmn_id_conversion_to_bytes) +{ + plmn_identity plmn = plmn_identity::parse("00101").value(); + std::array bytes = {0x00, 0xf1, 0x10}; + ASSERT_EQ(plmn.to_bytes(), bytes); + plmn = plmn_identity::parse("001001").value(); + bytes = {0x00, 0x01, 0x10}; + ASSERT_EQ(plmn.to_bytes(), bytes); + plmn = plmn_identity::parse("123456").value(); + bytes = {0x21, 0x43, 0x65}; + ASSERT_EQ(plmn.to_bytes(), bytes); + ASSERT_EQ(plmn.to_string(), "123456"); +} + +TEST_F(plmn_id_test, plmn_id_test_value_is_mcc_mnc_test_value) +{ + ASSERT_EQ(mobile_country_code::test_value().to_string(), "001"); + ASSERT_EQ(mobile_network_code::test_value().to_string(), "01"); + ASSERT_EQ(plmn_identity::test_value().mcc(), mobile_country_code::test_value()); + ASSERT_EQ(plmn_identity::test_value().mnc(), mobile_network_code::test_value()); +} + +class nci_test : public ::testing::Test +{}; + +TEST_F(nci_test, nci_creation_from_invalid_number_fails) +{ + auto ret = nr_cell_identity::create(0x123456789a); + ASSERT_FALSE(ret.has_value()); +} + +TEST_F(nci_test, nci_creation_from_valid_number_succeeds) +{ + auto ret = nr_cell_identity::create(0x19b01); + ASSERT_TRUE(ret.has_value()); + nr_cell_identity nci = ret.value(); + ASSERT_EQ(nci.value(), 0x19b01); + + ret = nr_cell_identity::create(0x123456789); + ASSERT_TRUE(ret.has_value()); + nci = ret.value(); + ASSERT_EQ(nci.value(), 0x123456789); +} + +TEST_F(nci_test, nci_creation_from_invalid_string_fails) +{ + auto ret = nr_cell_identity::parse_hex("123456789a"); + ASSERT_FALSE(ret.has_value()); + ret = nr_cell_identity::parse_hex("12345678x"); + ASSERT_FALSE(ret.has_value()); +} + +TEST_F(nci_test, nci_creation_from_valid_string_succeeds) +{ + auto ret = nr_cell_identity::parse_hex("19B01"); + ASSERT_TRUE(ret.has_value()); + nr_cell_identity nci = ret.value(); + ASSERT_EQ(nci.value(), 0x19b01); + ASSERT_EQ(fmt::format("{:x}", nci), "19b01"); + ASSERT_EQ(fmt::format("{:#x}", nci), "0x19b01"); + + ret = nr_cell_identity::parse_hex("123456789"); + ASSERT_TRUE(ret.has_value()); + nci = ret.value(); + ASSERT_EQ(nci.value(), 0x123456789); + ASSERT_EQ(fmt::format("{:x}", nci), "123456789"); +} + +TEST_F(nci_test, nci_to_local_cell_id) +{ + auto ret = nr_cell_identity::create(0x19b01); + ASSERT_TRUE(ret.has_value()); + nr_cell_identity nci = ret.value(); + ASSERT_EQ(nci.local_cell_id(4), 0x01); + ASSERT_EQ(nci.local_cell_id(8), 0x01); + ASSERT_EQ(nci.local_cell_id(12), 0xb01); + ASSERT_EQ(nci.local_cell_id(14), 0x1b01); +} + +TEST_F(nci_test, nci_to_gnb_id) +{ + auto ret = nr_cell_identity::create(0x19b01); + ASSERT_TRUE(ret.has_value()); + nr_cell_identity nci = ret.value(); + gnb_id_t gnb_id{0x19, 24}; + ASSERT_EQ(nci.gnb_id(24), gnb_id); + gnb_id = {0x19b, 28}; + ASSERT_EQ(nci.gnb_id(28), gnb_id); + gnb_id = {0x19b0, 32}; + ASSERT_EQ(nci.gnb_id(32), gnb_id); +} + +TEST_F(nci_test, invalid_gnb_id_and_local_cell_id_to_nci_fails) +{ + gnb_id_t gnb_id{0x19, 24}; + auto ret = nr_cell_identity::create(gnb_id, 0x1b01); + ASSERT_FALSE(ret.has_value()); +} + +TEST_F(nci_test, valid_gnb_id_and_local_cell_id_to_nci_succeeds) +{ + gnb_id_t gnb_id{0x19, 24}; + auto ret = nr_cell_identity::create(gnb_id, 0xb01); + ASSERT_TRUE(ret.has_value()); + nr_cell_identity nci = ret.value(); + ASSERT_EQ(nci.gnb_id(24), gnb_id); + ASSERT_EQ(nci.local_cell_id(12), 0xb01); +} diff --git a/tests/unittests/ran/nr_cgi_helpers_test.cpp b/tests/unittests/ran/nr_cgi_helpers_test.cpp deleted file mode 100644 index ac264e3df6..0000000000 --- a/tests/unittests/ran/nr_cgi_helpers_test.cpp +++ /dev/null @@ -1,60 +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/ran/nr_cgi_helpers.h" -#include - -using namespace srsran; - -TEST(nr_cgi_helpers_test, nr_cell_identity_validity) -{ - nr_cell_global_id_t valid_cgi; - valid_cgi.mcc = 1; - valid_cgi.mnc = 1; - valid_cgi.plmn = "00101"; - valid_cgi.plmn_hex = "00f110"; - - ASSERT_TRUE(srsran::config_helpers::is_valid(valid_cgi)); - - nr_cell_global_id_t invalid_cgi; - invalid_cgi.mcc = 0; - invalid_cgi.mnc = 0; - invalid_cgi.plmn = "00000"; - invalid_cgi.plmn_hex = "00f000"; - - ASSERT_FALSE(srsran::config_helpers::is_valid(invalid_cgi)); -} - -TEST(nr_cgi_helpers_test, make_nr_cell_identity_test) -{ - ASSERT_EQ(srsran::config_helpers::make_nr_cell_identity({411, 32}, 0), 6576U); - ASSERT_EQ(srsran::config_helpers::make_nr_cell_identity({411, 22}, 0), 6733824U); -} - -TEST(nr_cgi_helpers_test, get_gnb_id_test) -{ - ASSERT_EQ(srsran::config_helpers::get_gnb_id(6576, 32).id, 411U); - ASSERT_EQ(srsran::config_helpers::get_gnb_id(6576, 32).bit_length, 32U); - - ASSERT_EQ(srsran::config_helpers::get_gnb_id(6733824, 22).id, 411U); - ASSERT_EQ(srsran::config_helpers::get_gnb_id(6733824, 22).bit_length, 22U); -} diff --git a/tests/unittests/ran/ssb_gscn_test.cpp b/tests/unittests/ran/ssb_gscn_test.cpp index ae165e6b72..14ce5e7eeb 100644 --- a/tests/unittests/ran/ssb_gscn_test.cpp +++ b/tests/unittests/ran/ssb_gscn_test.cpp @@ -94,72 +94,72 @@ TEST(test_get_gscn_from_ss_ref, mixed_bands) TEST(test_is_gscn_valid_given_band, mixed_bands) { - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(5300U, nr_band::n1, subcarrier_spacing::kHz15).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(5500U, nr_band::n1, subcarrier_spacing::kHz15).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(4518U, nr_band::n3, subcarrier_spacing::kHz15).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(4501U, nr_band::n3, subcarrier_spacing::kHz15).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(2177U, nr_band::n5, subcarrier_spacing::kHz15).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(2224U, nr_band::n5, subcarrier_spacing::kHz30).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6618U, nr_band::n7, subcarrier_spacing::kHz15).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6818U, nr_band::n3, subcarrier_spacing::kHz15).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6618U, nr_band::n7, subcarrier_spacing::kHz15).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6818U, nr_band::n3, subcarrier_spacing::kHz15).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(3892U, nr_band::n24, subcarrier_spacing::kHz15).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(3886U, nr_band::n24, subcarrier_spacing::kHz30).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(5043U, nr_band::n34, subcarrier_spacing::kHz15).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(5045U, nr_band::n34, subcarrier_spacing::kHz15).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(5045U, nr_band::n34, subcarrier_spacing::kHz30).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(5051U, nr_band::n34, subcarrier_spacing::kHz30).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6479U, nr_band::n38, subcarrier_spacing::kHz15).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6494U, nr_band::n38, subcarrier_spacing::kHz15).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6494U, nr_band::n38, subcarrier_spacing::kHz30).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6433U, nr_band::n38, subcarrier_spacing::kHz30).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(4747U, nr_band::n39, subcarrier_spacing::kHz15).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(4771U, nr_band::n39, subcarrier_spacing::kHz15).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6696U, nr_band::n41, subcarrier_spacing::kHz15).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6695U, nr_band::n41, subcarrier_spacing::kHz15).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6714U, nr_band::n41, subcarrier_spacing::kHz30).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6712U, nr_band::n41, subcarrier_spacing::kHz30).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(9444U, nr_band::n46, subcarrier_spacing::kHz30).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(9447U, nr_band::n46, subcarrier_spacing::kHz30).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(7911U, nr_band::n48, subcarrier_spacing::kHz30).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(9447U, nr_band::n48, subcarrier_spacing::kHz15).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(7911U, nr_band::n78, subcarrier_spacing::kHz30).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(7700U, nr_band::n78, subcarrier_spacing::kHz30).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band( - 8720U, nr_band::n79, subcarrier_spacing::kHz30, bs_channel_bandwidth_fr1::MHz40) - .is_error()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(5300U, nr_band::n1, subcarrier_spacing::kHz15).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(5500U, nr_band::n1, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(4518U, nr_band::n3, subcarrier_spacing::kHz15).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(4501U, nr_band::n3, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(2177U, nr_band::n5, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(2224U, nr_band::n5, subcarrier_spacing::kHz30).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6618U, nr_band::n7, subcarrier_spacing::kHz15).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6818U, nr_band::n3, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6618U, nr_band::n7, subcarrier_spacing::kHz15).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6818U, nr_band::n3, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(3892U, nr_band::n24, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(3886U, nr_band::n24, subcarrier_spacing::kHz30).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(5043U, nr_band::n34, subcarrier_spacing::kHz15).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(5045U, nr_band::n34, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(5045U, nr_band::n34, subcarrier_spacing::kHz30).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(5051U, nr_band::n34, subcarrier_spacing::kHz30).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6479U, nr_band::n38, subcarrier_spacing::kHz15).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6494U, nr_band::n38, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6494U, nr_band::n38, subcarrier_spacing::kHz30).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6433U, nr_band::n38, subcarrier_spacing::kHz30).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(4747U, nr_band::n39, subcarrier_spacing::kHz15).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(4771U, nr_band::n39, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6696U, nr_band::n41, subcarrier_spacing::kHz15).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6695U, nr_band::n41, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6714U, nr_band::n41, subcarrier_spacing::kHz30).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6712U, nr_band::n41, subcarrier_spacing::kHz30).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(9444U, nr_band::n46, subcarrier_spacing::kHz30).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(9447U, nr_band::n46, subcarrier_spacing::kHz30).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(7911U, nr_band::n48, subcarrier_spacing::kHz30).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(9447U, nr_band::n48, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(7911U, nr_band::n78, subcarrier_spacing::kHz30).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(7700U, nr_band::n78, subcarrier_spacing::kHz30).has_value()); ASSERT_TRUE(band_helper::is_gscn_valid_given_band( - 8716U, nr_band::n79, subcarrier_spacing::kHz30, bs_channel_bandwidth_fr1::MHz40) - .is_error()); + 8720U, nr_band::n79, subcarrier_spacing::kHz30, bs_channel_bandwidth_fr1::MHz40) + .has_value()); ASSERT_FALSE(band_helper::is_gscn_valid_given_band( - 8716U, nr_band::n79, subcarrier_spacing::kHz30, bs_channel_bandwidth_fr1::MHz10) - .is_error()); + 8716U, nr_band::n79, subcarrier_spacing::kHz30, bs_channel_bandwidth_fr1::MHz40) + .has_value()); ASSERT_TRUE(band_helper::is_gscn_valid_given_band( - 8885U, nr_band::n79, subcarrier_spacing::kHz30, bs_channel_bandwidth_fr1::MHz10) - .is_error()); + 8716U, nr_band::n79, subcarrier_spacing::kHz30, bs_channel_bandwidth_fr1::MHz10) + .has_value()); ASSERT_FALSE(band_helper::is_gscn_valid_given_band( - 6717U, nr_band::n90, subcarrier_spacing::kHz15, bs_channel_bandwidth_fr1::MHz10) - .is_error()); + 8885U, nr_band::n79, subcarrier_spacing::kHz30, bs_channel_bandwidth_fr1::MHz10) + .has_value()); ASSERT_TRUE(band_helper::is_gscn_valid_given_band( - 6718U, nr_band::n90, subcarrier_spacing::kHz15, bs_channel_bandwidth_fr1::MHz10) - .is_error()); + 6717U, nr_band::n90, subcarrier_spacing::kHz15, bs_channel_bandwidth_fr1::MHz10) + .has_value()); ASSERT_FALSE(band_helper::is_gscn_valid_given_band( - 6718U, nr_band::n90, subcarrier_spacing::kHz15, bs_channel_bandwidth_fr1::MHz5) - .is_error()); + 6718U, nr_band::n90, subcarrier_spacing::kHz15, bs_channel_bandwidth_fr1::MHz10) + .has_value()); ASSERT_TRUE(band_helper::is_gscn_valid_given_band( - 6244U, nr_band::n90, subcarrier_spacing::kHz15, bs_channel_bandwidth_fr1::MHz5) - .is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6554U, nr_band::n90, subcarrier_spacing::kHz30).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6250U, nr_band::n90, subcarrier_spacing::kHz30).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(10062U, nr_band::n96, subcarrier_spacing::kHz30).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(9991U, nr_band::n96, subcarrier_spacing::kHz30).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(4754U, nr_band::n101, subcarrier_spacing::kHz15).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(4750U, nr_band::n101, subcarrier_spacing::kHz15).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(4760U, nr_band::n101, subcarrier_spacing::kHz30).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(4754U, nr_band::n101, subcarrier_spacing::kHz30).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(9798U, nr_band::n102, subcarrier_spacing::kHz30).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(9799U, nr_band::n102, subcarrier_spacing::kHz30).is_error()); - ASSERT_FALSE(band_helper::is_gscn_valid_given_band(10169U, nr_band::n104, subcarrier_spacing::kHz30).is_error()); - ASSERT_TRUE(band_helper::is_gscn_valid_given_band(10165U, nr_band::n104, subcarrier_spacing::kHz30).is_error()); + 6718U, nr_band::n90, subcarrier_spacing::kHz15, bs_channel_bandwidth_fr1::MHz5) + .has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band( + 6244U, nr_band::n90, subcarrier_spacing::kHz15, bs_channel_bandwidth_fr1::MHz5) + .has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(6554U, nr_band::n90, subcarrier_spacing::kHz30).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(6250U, nr_band::n90, subcarrier_spacing::kHz30).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(10062U, nr_band::n96, subcarrier_spacing::kHz30).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(9991U, nr_band::n96, subcarrier_spacing::kHz30).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(4754U, nr_band::n101, subcarrier_spacing::kHz15).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(4750U, nr_band::n101, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(4760U, nr_band::n101, subcarrier_spacing::kHz30).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(4754U, nr_band::n101, subcarrier_spacing::kHz30).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(9798U, nr_band::n102, subcarrier_spacing::kHz30).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(9799U, nr_band::n102, subcarrier_spacing::kHz30).has_value()); + ASSERT_TRUE(band_helper::is_gscn_valid_given_band(10169U, nr_band::n104, subcarrier_spacing::kHz30).has_value()); + ASSERT_FALSE(band_helper::is_gscn_valid_given_band(10165U, nr_band::n104, subcarrier_spacing::kHz30).has_value()); } diff --git a/tests/unittests/rlc/rlc_tx_am_test.cpp b/tests/unittests/rlc/rlc_tx_am_test.cpp index 0dae890a07..7f207d7b85 100644 --- a/tests/unittests/rlc/rlc_tx_am_test.cpp +++ b/tests/unittests/rlc/rlc_tx_am_test.cpp @@ -38,6 +38,8 @@ class rlc_tx_am_test_frame : public rlc_tx_upper_layer_data_notifier, public: std::list highest_transmitted_pdcp_sn_list; std::list highest_delivered_pdcp_sn_list; + std::list highest_retransmitted_pdcp_sn_list; + std::list highest_delivered_retransmitted_pdcp_sn_list; rlc_am_sn_size sn_size; rlc_am_status_pdu status; bool status_required = false; @@ -61,6 +63,18 @@ class rlc_tx_am_test_frame : public rlc_tx_upper_layer_data_notifier, highest_delivered_pdcp_sn_list.push_back(max_deliv_pdcp_sn); } + void on_retransmitted_sdu(uint32_t max_retx_pdcp_sn) override + { + // 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 + { + // store in list + highest_delivered_retransmitted_pdcp_sn_list.push_back(max_deliv_retx_pdcp_sn); + } + // rlc_tx_upper_layer_control_notifier interface void on_protocol_failure() override { proto_err_count++; } void on_max_retx() override { max_retx_count++; } @@ -148,7 +162,7 @@ class rlc_tx_am_test : public ::testing::Test, public ::testing::WithParamInterf sdu_bufs[i] = test_helpers::create_pdcp_pdu(config.pdcp_sn_len, i, sdu_size, i); // write SDU into upper end - rlc->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move - keep local copy for later comparison + rlc->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } pcell_worker.run_pending_tasks(); EXPECT_EQ(tester->bsr_count, ++n_bsr); @@ -216,7 +230,7 @@ class rlc_tx_am_test : public ::testing::Test, public ::testing::WithParamInterf sdu_bufs[i] = test_helpers::create_pdcp_pdu(config.pdcp_sn_len, i, sdu_size, i); // write SDU into upper end - rlc->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move - keep local copy for later comparison + rlc->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } pcell_worker.run_pending_tasks(); EXPECT_EQ(tester->bsr_count, ++n_bsr); @@ -349,7 +363,7 @@ TEST_P(rlc_tx_am_test, tx_insufficient_space_new_sdu) const uint32_t fit_size = header_min_size + sdu_size; EXPECT_EQ(rlc->get_buffer_state(), 0); - rlc->handle_sdu(test_helpers::create_pdcp_pdu(config.pdcp_sn_len, 0, sdu_size)); + rlc->handle_sdu(test_helpers::create_pdcp_pdu(config.pdcp_sn_len, 0, sdu_size), false); EXPECT_EQ(rlc->get_buffer_state(), sdu_size + header_min_size); pcell_worker.run_pending_tasks(); EXPECT_EQ(tester->bsr, sdu_size + header_min_size); @@ -387,7 +401,7 @@ TEST_P(rlc_tx_am_test, tx_insufficient_space_continued_sdu) const uint32_t min_size_seg = header_min_size + header_so_size + 1; EXPECT_EQ(rlc->get_buffer_state(), 0); - rlc->handle_sdu(test_helpers::create_pdcp_pdu(config.pdcp_sn_len, 0, sdu_size)); + rlc->handle_sdu(test_helpers::create_pdcp_pdu(config.pdcp_sn_len, 0, sdu_size), false); pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc->get_buffer_state(), sdu_size + header_min_size); EXPECT_EQ(tester->bsr, sdu_size + header_min_size); @@ -446,7 +460,7 @@ TEST_P(rlc_tx_am_test, sdu_discard) sdu_bufs[i] = test_helpers::create_pdcp_pdu(config.pdcp_sn_len, i, sdu_size, i); // write SDU into upper end - rlc->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move - keep local copy for later comparison + rlc->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } pcell_worker.run_pending_tasks(); EXPECT_EQ(tester->bsr_count, ++n_bsr); @@ -558,7 +572,7 @@ TEST_P(rlc_tx_am_test, sdu_discard_with_pdcp_sn_wraparound) sdu_bufs[i] = test_helpers::create_pdcp_pdu(config.pdcp_sn_len, pdcp_sn_start + i, sdu_size, i); // write SDU into upper end - rlc->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move, keep copy for later comparison + rlc->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } pcell_worker.run_pending_tasks(); EXPECT_EQ(tester->bsr_count, ++n_bsr); @@ -1607,7 +1621,7 @@ TEST_P(rlc_tx_am_test, status_report_priority) const uint32_t pdu_size = header_min_size + sdu_size; EXPECT_EQ(rlc->get_buffer_state(), 0); - rlc->handle_sdu(test_helpers::create_pdcp_pdu(config.pdcp_sn_len, 0, sdu_size)); + rlc->handle_sdu(test_helpers::create_pdcp_pdu(config.pdcp_sn_len, 0, sdu_size), false); pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc->get_buffer_state(), pdu_size); EXPECT_EQ(tester->bsr, pdu_size); @@ -1788,7 +1802,7 @@ TEST_P(rlc_tx_am_test, expired_poll_retransmit_timer_sets_polling_bit) // push SDU to SDU queue so that it is not empty uint32_t n_bsr = tester->bsr_count; byte_buffer sdu_buf = test_helpers::create_pdcp_pdu(config.pdcp_sn_len, 7, sdu_size, 7); - rlc->handle_sdu(sdu_buf.deep_copy().value()); // no std::move - keep local copy for later comparison + rlc->handle_sdu(sdu_buf.deep_copy().value(), false); // keep local copy for later comparison pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc->get_buffer_state(), pdu_size); EXPECT_EQ(tester->bsr_count, ++n_bsr); diff --git a/tests/unittests/rlc/rlc_tx_tm_test.cpp b/tests/unittests/rlc/rlc_tx_tm_test.cpp index a130c9a17e..1a8f04e162 100644 --- a/tests/unittests/rlc/rlc_tx_tm_test.cpp +++ b/tests/unittests/rlc/rlc_tx_tm_test.cpp @@ -44,6 +44,8 @@ class rlc_tx_tm_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) override {} void on_delivered_sdu(uint32_t max_deliv_pdcp_sn) override {} + void on_retransmitted_sdu(uint32_t max_retx_pdcp_sn) override {} + void on_delivered_retransmitted_sdu(uint32_t max_deliv_retx_pdcp_sn) override {} // rlc_tx_upper_layer_control_notifier interface void on_protocol_failure() override {} @@ -119,7 +121,7 @@ TEST_F(rlc_tx_tm_test, test_tx) byte_buffer sdu_buf = test_helpers::create_pdcp_pdu(pdcp_sn_size::size12bits, count, sdu_size, count); // write SDU into upper end - rlc->handle_sdu(sdu_buf.deep_copy().value()); // no std::move - keep local copy for later comparison + rlc->handle_sdu(sdu_buf.deep_copy().value(), false); // keep local copy for later comparison pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc->get_buffer_state(), sdu_size); EXPECT_EQ(tester->bsr, sdu_size); @@ -151,7 +153,7 @@ TEST_F(rlc_tx_tm_test, test_tx) count++; sdu_buf = test_helpers::create_pdcp_pdu(pdcp_sn_size::size12bits, count, sdu_size, count); - rlc->handle_sdu(sdu_buf.deep_copy().value()); // no std::move - keep local copy for later comparison + rlc->handle_sdu(sdu_buf.deep_copy().value(), false); // keep local copy for later comparison pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc->get_buffer_state(), sdu_size); EXPECT_EQ(tester->bsr, sdu_size); @@ -172,7 +174,7 @@ TEST_F(rlc_tx_tm_test, test_tx) byte_buffer sdu_buf2 = test_helpers::create_pdcp_pdu(pdcp_sn_size::size12bits, count, sdu_size, count); // write SDU into upper end - rlc->handle_sdu(sdu_buf2.deep_copy().value()); // no std::move - keep local copy for later comparison + rlc->handle_sdu(sdu_buf2.deep_copy().value(), false); // keep local copy for later comparison pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc->get_buffer_state(), 2 * sdu_size); EXPECT_EQ(tester->bsr, 2 * sdu_size); @@ -211,7 +213,7 @@ TEST_F(rlc_tx_tm_test, discard_sdu_increments_discard_failure_counter) byte_buffer sdu_buf = test_helpers::create_pdcp_pdu(pdcp_sn_size::size12bits, count, sdu_size, count); // write SDU into upper end - rlc->handle_sdu(sdu_buf.deep_copy().value()); // no std::move - keep local copy for later comparison + rlc->handle_sdu(sdu_buf.deep_copy().value(), false); // keep local copy for later comparison pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc->get_buffer_state(), sdu_size); EXPECT_EQ(tester->bsr, sdu_size); @@ -248,7 +250,7 @@ TEST_F(rlc_tx_tm_test, test_tx_metrics) byte_buffer sdu_buf = test_helpers::create_pdcp_pdu(pdcp_sn_size::size12bits, count, sdu_size, count); // write SDU into upper end - rlc->handle_sdu(sdu_buf.deep_copy().value()); // no std::move - keep local copy for later comparison + rlc->handle_sdu(sdu_buf.deep_copy().value(), false); // keep local copy for later comparison pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc->get_buffer_state(), sdu_size); EXPECT_EQ(tester->bsr, sdu_size); diff --git a/tests/unittests/rlc/rlc_um_test.cpp b/tests/unittests/rlc/rlc_um_test.cpp index 0f88a57a2d..4864479706 100644 --- a/tests/unittests/rlc/rlc_um_test.cpp +++ b/tests/unittests/rlc/rlc_um_test.cpp @@ -57,6 +57,8 @@ class rlc_test_frame : public rlc_rx_upper_layer_data_notifier, transmitted_pdcp_sn_list.push_back(max_tx_pdcp_sn); } void on_delivered_sdu(uint32_t max_deliv_pdcp_sn) override {} + void on_retransmitted_sdu(uint32_t max_retx_pdcp_sn) override {} + void on_delivered_retransmitted_sdu(uint32_t max_deliv_retx_pdcp_sn) override {} // rlc_tx_upper_layer_control_notifier interface void on_protocol_failure() override {} @@ -174,7 +176,7 @@ class rlc_um_test : public ::testing::Test, public ::testing::WithParamInterface } // write SDU into upper end - rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move - keep local copy for later comparison + rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } buffer_state = rlc1_tx_lower->get_buffer_state(); EXPECT_EQ(num_sdus * (sdu_size + 1), buffer_state); @@ -349,7 +351,7 @@ TEST_P(rlc_um_test, tx_without_segmentation) sdu_bufs[i] = test_helpers::create_pdcp_pdu(config.tx.pdcp_sn_len, i + 13, sdu_size, i); // write SDU into upper end - rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move - keep local copy for later comparison + rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc1_tx_lower->get_buffer_state(), num_sdus * (sdu_size + 1)); @@ -418,7 +420,7 @@ TEST_P(rlc_um_test, tx_with_segmentation) sdu_bufs[i] = test_helpers::create_pdcp_pdu(config.tx.pdcp_sn_len, i, sdu_size, i); // write SDU into upper end - rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move - keep local copy for later comparison + rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc1_tx_lower->get_buffer_state(), num_sdus * (sdu_size + 1)); @@ -503,7 +505,7 @@ TEST_P(rlc_um_test, sdu_discard) sdu_bufs[i] = test_helpers::create_pdcp_pdu(config.tx.pdcp_sn_len, i, sdu_size, i); // write SDU into upper end - rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move - keep local copy for later comparison + rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } tester1.bsr_count = 0; // reset @@ -604,7 +606,7 @@ TEST_P(rlc_um_test, sdu_discard_with_pdcp_sn_wraparound) sdu_bufs[i] = test_helpers::create_pdcp_pdu(config.tx.pdcp_sn_len, (pdcp_sn_start + i) % pdcp_sn_mod, sdu_size, i); // write SDU into upper end - rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move, keep copy for later comparison + rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } tester1.bsr_count = 0; // reset @@ -700,7 +702,7 @@ TEST_P(rlc_um_test, tx_with_segmentation_reverse_rx) sdu_bufs[i] = test_helpers::create_pdcp_pdu(config.tx.pdcp_sn_len, i, sdu_size, i); // write SDU into upper end - rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move - keep local copy for later comparison + rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc1_tx_lower->get_buffer_state(), num_sdus * (sdu_size + 1)); @@ -785,7 +787,7 @@ TEST_P(rlc_um_test, tx_multiple_SDUs_with_segmentation) sdu_bufs[i] = test_helpers::create_pdcp_pdu(config.tx.pdcp_sn_len, i, sdu_size, i); // write SDU into upper end - rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move - keep local copy for later comparison + rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc1_tx_lower->get_buffer_state(), num_sdus * (sdu_size + 1)); @@ -906,7 +908,7 @@ TEST_P(rlc_um_test, reassembly_window_wrap_around) uint32_t rx_sdu_idx = 0; for (uint32_t i = 0; i < num_sdus; i++) { // create and write SDU into upper end - rlc1_tx_upper->handle_sdu(test_helpers::create_pdcp_pdu(config.tx.pdcp_sn_len, i, sdu_size, i)); + rlc1_tx_upper->handle_sdu(test_helpers::create_pdcp_pdu(config.tx.pdcp_sn_len, i, sdu_size, i), false); pcell_worker.run_pending_tasks(); // check buffer state @@ -969,7 +971,7 @@ TEST_P(rlc_um_test, lost_PDU_outside_reassembly_window) uint32_t rx_sdu_idx = 0; for (uint32_t i = 0; i < num_sdus; i++) { // create and write SDU into upper end - rlc1_tx_upper->handle_sdu(test_helpers::create_pdcp_pdu(config.tx.pdcp_sn_len, i, sdu_size, i)); + rlc1_tx_upper->handle_sdu(test_helpers::create_pdcp_pdu(config.tx.pdcp_sn_len, i, sdu_size, i), false); pcell_worker.run_pending_tasks(); // check buffer state @@ -1041,7 +1043,7 @@ TEST_P(rlc_um_test, lost_segment_outside_reassembly_window) sdu_bufs[i] = test_helpers::create_pdcp_pdu(config.tx.pdcp_sn_len, i, sdu_size, i); // write SDU into upper end - rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move - keep local copy for later comparison + rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc1_tx_lower->get_buffer_state(), num_sdus * (sdu_size + 1)); @@ -1124,7 +1126,7 @@ TEST_P(rlc_um_test, out_of_order_segments_across_SDUs) sdu_bufs[i] = test_helpers::create_pdcp_pdu(config.tx.pdcp_sn_len, i, sdu_size, i); // write SDU into upper end - rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value()); // no std::move - keep local copy for later comparison + rlc1_tx_upper->handle_sdu(sdu_bufs[i].deep_copy().value(), false); // keep local copy for later comparison } pcell_worker.run_pending_tasks(); EXPECT_EQ(rlc1_tx_lower->get_buffer_state(), num_sdus * (sdu_size + 1)); diff --git a/tests/unittests/rrc/test_helpers.h b/tests/unittests/rrc/test_helpers.h index 37b8b801b7..5072e68504 100644 --- a/tests/unittests/rrc/test_helpers.h +++ b/tests/unittests/rrc/test_helpers.h @@ -148,7 +148,7 @@ class dummy_rrc_ue_cu_cp_adapter : public rrc_ue_context_update_notifier, public } std::optional - on_measurement_config_request(nr_cell_id_t nci, std::optional current_meas_config = {}) override + on_measurement_config_request(nr_cell_identity nci, std::optional current_meas_config = {}) override { std::optional meas_cfg; return meas_cfg; @@ -166,7 +166,7 @@ class dummy_rrc_ue_cu_cp_adapter : public rrc_ue_context_update_notifier, public class dummy_rrc_du_cu_cp_adapter : public rrc_du_measurement_config_notifier { public: - bool on_cell_config_update_request(nr_cell_id_t nci, const serving_cell_meas_config& serv_cell_cfg) override + bool on_cell_config_update_request(nr_cell_identity nci, const serving_cell_meas_config& serv_cell_cfg) override { return true; } diff --git a/tests/unittests/scheduler/policy/scheduler_policy_test.cpp b/tests/unittests/scheduler/policy/scheduler_policy_test.cpp index 7103abd236..4e2a5028be 100644 --- a/tests/unittests/scheduler/policy/scheduler_policy_test.cpp +++ b/tests/unittests/scheduler/policy/scheduler_policy_test.cpp @@ -21,7 +21,9 @@ */ #include "../test_utils/config_generators.h" +#include "lib/scheduler/logging/scheduler_result_logger.h" #include "lib/scheduler/pdcch_scheduling/pdcch_resource_allocator_impl.h" +#include "lib/scheduler/policy/scheduler_policy_factory.h" #include "lib/scheduler/policy/scheduler_time_rr.h" #include "lib/scheduler/pucch_scheduling/pucch_allocator_impl.h" #include "lib/scheduler/uci_scheduling/uci_allocator_impl.h" @@ -35,17 +37,25 @@ using namespace srsran; -enum class policy_type { time_rr }; +/// \brief Type of policy scheduler. +/// +/// The current types are: +/// - time_rr - Time based Round-Robin scheduler. +/// - time_pf - Time based Proportional Fair scheduler. +enum class policy_scheduler_type { time_rr, time_pf }; class base_scheduler_policy_test { protected: base_scheduler_policy_test( - policy_type policy, + policy_scheduler_type policy, scheduler_expert_config sched_cfg_ = config_helpers::make_default_scheduler_expert_config(), const sched_cell_configuration_request_message& msg = test_helpers::make_default_sched_cell_configuration_request()) : - logger(srslog::fetch_basic_logger("SCHED", true)), sched_cfg(sched_cfg_), cell_cfg(*[this, &msg]() { + logger(srslog::fetch_basic_logger("SCHED", true)), + res_logger(false, msg.pci), + sched_cfg(sched_cfg_), + cell_cfg(*[this, &msg]() { return cell_cfg_list.emplace(to_du_cell_index(0), std::make_unique(sched_cfg, msg)).get(); }()) { @@ -55,12 +65,13 @@ class base_scheduler_policy_test grid_alloc.add_cell(to_du_cell_index(0), pdcch_alloc, uci_alloc, res_grid); ue_res_grid.add_cell(res_grid); - switch (policy) { - case policy_type::time_rr: - sched = std::make_unique(sched_cfg.ue); - break; - default: - report_fatal_error("Invalid policy"); + if (policy == policy_scheduler_type::time_pf) { + sched_cfg.ue.strategy_cfg = time_pf_scheduler_expert_config{}; + } + sched = create_scheduler_strategy(sched_cfg.ue); + + if (sched == nullptr) { + report_fatal_error("Invalid policy"); } } @@ -68,6 +79,8 @@ class base_scheduler_policy_test void run_slot() { + logger.set_context(next_slot.sfn(), next_slot.slot_index()); + grid_alloc.slot_indication(next_slot); res_grid.slot_indication(next_slot); @@ -80,7 +93,21 @@ class base_scheduler_policy_test sched->ul_sched(grid_alloc, ue_res_grid, ues); } - next_slot++; + // Log scheduler results. + res_logger.on_scheduler_result(res_grid[0].result); + + ++next_slot; + } + + bool run_until(unique_function condition, unsigned max_slot_count = 1000) + { + for (unsigned count = 0; count != max_slot_count; ++count) { + if (condition()) { + return true; + } + run_slot(); + } + return false; } ue& add_ue(du_ue_index_t ue_index, const std::initializer_list& lcids_to_activate) @@ -139,6 +166,7 @@ class base_scheduler_policy_test } srslog::basic_logger& logger; + scheduler_result_logger res_logger; scheduler_expert_config sched_cfg; cell_common_configuration_list cell_cfg_list; std::vector> ue_ded_cell_cfg_list; @@ -159,7 +187,7 @@ class base_scheduler_policy_test slot_point next_slot{0, test_rgen::uniform_int(0, 10239)}; }; -class scheduler_policy_test : public base_scheduler_policy_test, public ::testing::TestWithParam +class scheduler_policy_test : public base_scheduler_policy_test, public ::testing::TestWithParam { protected: scheduler_policy_test() : base_scheduler_policy_test(GetParam()) {} @@ -269,8 +297,86 @@ TEST_P(scheduler_policy_test, scheduler_allocates_ues_with_sr_opportunity_first_ << fmt::format("UE with SR opportunity should have been scheduled first."); } +TEST_P(scheduler_policy_test, scheduler_allocates_ues_with_ul_retx_first_than_ues_with_newtx) +{ + const lcg_id_t lcg_id = uint_to_lcg_id(2); + ue& u1 = add_ue(make_ue_create_req(to_du_ue_index(0), to_rnti(0x4601), {uint_to_lcid(5)}, lcg_id)); + ue& u2 = add_ue(make_ue_create_req(to_du_ue_index(1), to_rnti(0x4602), {uint_to_lcid(5)}, lcg_id)); + + // Push high enough UL BSR such that grant occupies entire BWP CRBs. + notify_ul_bsr(u1.ue_index, lcg_id, 20000); + notify_ul_bsr(u2.ue_index, lcg_id, 20000); + + bool pusch_scheduled = run_until([this]() { return not this->res_grid[0].result.ul.puschs.empty(); }); + const slot_point current_slot = next_slot - 1; + ASSERT_TRUE(pusch_scheduled); + const ul_sched_info& pusch = this->res_grid[0].result.ul.puschs[0]; + // Send CRC=KO to trigger retransmission. + const ul_crc_pdu_indication crc_pdu{ + pusch.pusch_cfg.rnti, pusch.context.ue_index, to_harq_id(pusch.pusch_cfg.harq_id), false}; + if (pusch.context.ue_index == u1.ue_index) { + u1.get_pcell().handle_crc_pdu(current_slot, crc_pdu); + } else if (pusch.context.ue_index == u2.ue_index) { + u2.get_pcell().handle_crc_pdu(current_slot, crc_pdu); + } + const du_ue_index_t ue_with_retx = pusch.context.ue_index; + + pusch_scheduled = run_until([this]() { return not this->res_grid[0].result.ul.puschs.empty(); }); + ASSERT_TRUE(pusch_scheduled); + ASSERT_EQ(this->res_grid[0].result.ul.puschs[0].context.ue_index, ue_with_retx); +} + +TEST_P(scheduler_policy_test, scheduler_allocates_ues_with_dl_retx_first_than_ues_with_newtx) +{ + const lcg_id_t lcg_id = uint_to_lcg_id(2); + ue& u1 = add_ue(make_ue_create_req(to_du_ue_index(0), to_rnti(0x4601), {uint_to_lcid(5)}, lcg_id)); + ue& u2 = add_ue(make_ue_create_req(to_du_ue_index(1), to_rnti(0x4602), {uint_to_lcid(5)}, lcg_id)); + + // Push high enough DL buffer status such that grant occupies entire BWP CRBs. + push_dl_bs(u1.ue_index, uint_to_lcid(5), 100000); + push_dl_bs(u2.ue_index, uint_to_lcid(5), 100000); + + bool pdsch_scheduled = run_until([this]() { return not this->res_grid[0].result.dl.ue_grants.empty(); }); + ASSERT_TRUE(pdsch_scheduled); + const bool pdsch_ack_scheduled = run_until([this]() { return not this->res_grid[0].result.ul.pucchs.empty(); }); + ASSERT_TRUE(pdsch_ack_scheduled); + const slot_point current_slot = next_slot - 1; + const pucch_info& pucch = this->res_grid[0].result.ul.pucchs[0]; + // Auto NACK HARQ. + unsigned nof_ack_bits = 0; + du_ue_index_t ue_with_retx; + switch (pucch.format) { + case pucch_format::FORMAT_0: + nof_ack_bits = pucch.format_0.harq_ack_nof_bits; + break; + case pucch_format::FORMAT_1: + nof_ack_bits = pucch.format_1.harq_ack_nof_bits; + break; + case pucch_format::FORMAT_2: + nof_ack_bits = pucch.format_2.harq_ack_nof_bits; + break; + default: + break; + } + if (pucch.crnti == u1.crnti) { + ue_with_retx = u1.ue_index; + for (unsigned harq_bit_idx = 0; harq_bit_idx < nof_ack_bits; ++harq_bit_idx) { + u1.get_pcell().handle_dl_ack_info(current_slot, mac_harq_ack_report_status::ack, harq_bit_idx, 100); + } + } else if (pucch.crnti == u2.crnti) { + ue_with_retx = u2.ue_index; + for (unsigned harq_bit_idx = 0; harq_bit_idx < nof_ack_bits; ++harq_bit_idx) { + u2.get_pcell().handle_dl_ack_info(current_slot, mac_harq_ack_report_status::ack, harq_bit_idx, 100); + } + } + + pdsch_scheduled = run_until([this]() { return not this->res_grid[0].result.dl.ue_grants.empty(); }); + ASSERT_TRUE(pdsch_scheduled); + ASSERT_EQ(this->res_grid[0].result.dl.ue_grants[0].context.ue_index, ue_with_retx); +} + class scheduler_policy_partial_slot_tdd_test : public base_scheduler_policy_test, - public ::testing::TestWithParam + public ::testing::TestWithParam { protected: scheduler_policy_partial_slot_tdd_test() : @@ -334,7 +440,7 @@ TEST_P(scheduler_policy_partial_slot_tdd_test, scheduler_allocates_in_partial_sl class scheduler_round_robin_test : public base_scheduler_policy_test, public ::testing::Test { protected: - scheduler_round_robin_test() : base_scheduler_policy_test(policy_type::time_rr) {} + scheduler_round_robin_test() : base_scheduler_policy_test(policy_scheduler_type::time_rr) {} }; TEST_F(scheduler_round_robin_test, round_robin_does_not_account_ues_with_empty_buffers) @@ -391,8 +497,161 @@ TEST_F(scheduler_round_robin_test, round_robin_must_not_attempt_to_allocate_twic } } +class scheduler_pf_test : public base_scheduler_policy_test, public ::testing::Test +{ +protected: + scheduler_pf_test() : base_scheduler_policy_test(policy_scheduler_type::time_pf) {} +}; + +TEST_F(scheduler_pf_test, pf_does_not_account_ues_with_empty_buffers) +{ + lcg_id_t lcg_id = uint_to_lcg_id(2); + const ue& u1 = add_ue(make_ue_create_req(to_du_ue_index(0), to_rnti(0x4601), {uint_to_lcid(5)}, lcg_id)); + const ue& u2 = add_ue(make_ue_create_req(to_du_ue_index(1), to_rnti(0x4602), {uint_to_lcid(5)}, lcg_id)); + const ue& u3 = add_ue(make_ue_create_req(to_du_ue_index(2), to_rnti(0x4603), {uint_to_lcid(5)}, lcg_id)); + + push_dl_bs(u1.ue_index, uint_to_lcid(5), 1000000); + notify_ul_bsr(u1.ue_index, lcg_id, 1000000); + push_dl_bs(u3.ue_index, uint_to_lcid(5), 1000000); + notify_ul_bsr(u3.ue_index, lcg_id, 1000000); + + std::set dl_scheduled_ues; + std::set ul_scheduled_ues; + for (unsigned i = 0; i != 10; ++i) { + run_slot(); + for (const auto& grant : this->res_grid[0].result.dl.ue_grants) { + dl_scheduled_ues.emplace(grant.context.ue_index); + ASSERT_NE(grant.context.ue_index, u2.ue_index); + } + for (const auto& grant : this->res_grid[0].result.ul.puschs) { + ul_scheduled_ues.emplace(grant.context.ue_index); + ASSERT_NE(grant.context.ue_index, u2.ue_index); + } + } + ASSERT_EQ(dl_scheduled_ues.size(), 2); + ASSERT_EQ(ul_scheduled_ues.size(), 2); +} + +TEST_F(scheduler_pf_test, pf_ensures_fairness_in_dl_when_ues_have_different_channel_conditions) +{ + const lcg_id_t lcg_id = uint_to_lcg_id(2); + ue& u1 = add_ue(make_ue_create_req(to_du_ue_index(0), to_rnti(0x4601), {uint_to_lcid(5)}, lcg_id)); + ue& u2 = add_ue(make_ue_create_req(to_du_ue_index(1), to_rnti(0x4602), {uint_to_lcid(5)}, lcg_id)); + ue& u3 = add_ue(make_ue_create_req(to_du_ue_index(2), to_rnti(0x4603), {uint_to_lcid(5)}, lcg_id)); + + // Report different CQIs for different UEs. + // Best channel condition. + u1.get_pcell().handle_csi_report( + csi_report_data{std::nullopt, std::nullopt, std::nullopt, std::nullopt, cqi_value{15U}}); + // Worst channel condition. + u2.get_pcell().handle_csi_report( + csi_report_data{std::nullopt, std::nullopt, std::nullopt, std::nullopt, cqi_value{10U}}); + u3.get_pcell().handle_csi_report( + csi_report_data{std::nullopt, std::nullopt, std::nullopt, std::nullopt, cqi_value{12U}}); + + // Push high enough DL buffer status to ensure UEs occupy entire BWP CRBs when scheduled. + push_dl_bs(u1.ue_index, uint_to_lcid(5), 1000000); + push_dl_bs(u2.ue_index, uint_to_lcid(5), 1000000); + push_dl_bs(u3.ue_index, uint_to_lcid(5), 1000000); + + std::unordered_map ue_pdsch_scheduled_count; + ue_pdsch_scheduled_count[u1.ue_index] = 0; + ue_pdsch_scheduled_count[u2.ue_index] = 0; + ue_pdsch_scheduled_count[u3.ue_index] = 0; + + for (unsigned i = 0; i != 100; ++i) { + run_slot(); + const slot_point current_slot = next_slot - 1; + for (const auto& grant : this->res_grid[0].result.dl.ue_grants) { + ++ue_pdsch_scheduled_count[grant.context.ue_index]; + } + + // Auto ACK HARQs. + for (const pucch_info& pucch : this->res_grid[0].result.ul.pucchs) { + unsigned nof_ack_bits = 0; + switch (pucch.format) { + case pucch_format::FORMAT_0: + nof_ack_bits = pucch.format_0.harq_ack_nof_bits; + break; + case pucch_format::FORMAT_1: + nof_ack_bits = pucch.format_1.harq_ack_nof_bits; + break; + case pucch_format::FORMAT_2: + nof_ack_bits = pucch.format_2.harq_ack_nof_bits; + break; + default: + break; + } + if (pucch.crnti == u1.crnti) { + for (unsigned harq_bit_idx = 0; harq_bit_idx < nof_ack_bits; ++harq_bit_idx) { + u1.get_pcell().handle_dl_ack_info(current_slot, mac_harq_ack_report_status::ack, harq_bit_idx, 100); + } + } else if (pucch.crnti == u2.crnti) { + for (unsigned harq_bit_idx = 0; harq_bit_idx < nof_ack_bits; ++harq_bit_idx) { + u2.get_pcell().handle_dl_ack_info(current_slot, mac_harq_ack_report_status::ack, harq_bit_idx, 100); + } + } else if (pucch.crnti == u3.crnti) { + for (unsigned harq_bit_idx = 0; harq_bit_idx < nof_ack_bits; ++harq_bit_idx) { + u3.get_pcell().handle_dl_ack_info(current_slot, mac_harq_ack_report_status::ack, harq_bit_idx, 100); + } + } + } + } + + // PF scheduler ensures fairness by scheduling UE2 more than UE1 and UE3 due to having worse channel conditions when + // compared to that of UE1 and UE3. + ASSERT_TRUE(ue_pdsch_scheduled_count[u2.ue_index] > ue_pdsch_scheduled_count[u1.ue_index] and + ue_pdsch_scheduled_count[u2.ue_index] > ue_pdsch_scheduled_count[u3.ue_index]); +} + +TEST_F(scheduler_pf_test, pf_ensures_fairness_in_ul_when_ues_have_different_channel_conditions) +{ + const lcg_id_t lcg_id = uint_to_lcg_id(2); + ue& u1 = add_ue(make_ue_create_req(to_du_ue_index(0), to_rnti(0x4601), {uint_to_lcid(5)}, lcg_id)); + ue& u2 = add_ue(make_ue_create_req(to_du_ue_index(1), to_rnti(0x4602), {uint_to_lcid(5)}, lcg_id)); + ue& u3 = add_ue(make_ue_create_req(to_du_ue_index(2), to_rnti(0x4603), {uint_to_lcid(5)}, lcg_id)); + + // Push high enough UL BSR to ensure UEs occupy entire BWP CRBs when scheduled. + notify_ul_bsr(u1.ue_index, lcg_id, 1000000); + notify_ul_bsr(u2.ue_index, lcg_id, 1000000); + notify_ul_bsr(u3.ue_index, lcg_id, 1000000); + + std::unordered_map ue_pusch_scheduled_count; + ue_pusch_scheduled_count[u1.ue_index] = 0; + ue_pusch_scheduled_count[u2.ue_index] = 0; + ue_pusch_scheduled_count[u3.ue_index] = 0; + + for (unsigned i = 0; i != 100; ++i) { + run_slot(); + const slot_point current_slot = next_slot - 1; + for (const auto& grant : this->res_grid[0].result.ul.puschs) { + ++ue_pusch_scheduled_count[grant.context.ue_index]; + // Auto send CRC=OK. + ul_crc_pdu_indication crc_pdu{ + grant.pusch_cfg.rnti, grant.context.ue_index, to_harq_id(grant.pusch_cfg.harq_id), true}; + if (grant.context.ue_index == u1.ue_index) { + // Worst channel condition. + crc_pdu.ul_sinr_dB = 10; + u1.get_pcell().handle_crc_pdu(current_slot, crc_pdu); + } else if (grant.context.ue_index == u2.ue_index) { + crc_pdu.ul_sinr_dB = 15; + u2.get_pcell().handle_crc_pdu(current_slot, crc_pdu); + } else if (grant.context.ue_index == u3.ue_index) { + // Best channel condition. + crc_pdu.ul_sinr_dB = 30; + u3.get_pcell().handle_crc_pdu(current_slot, crc_pdu); + } + } + } + + // PF scheduler ensures fairness by scheduling UE1 more than UE2 and UE3 due to having worse channel conditions when + // compared to that of UE2 and UE3. + ASSERT_TRUE(ue_pusch_scheduled_count[u1.ue_index] > ue_pusch_scheduled_count[u2.ue_index] and + ue_pusch_scheduled_count[u1.ue_index] > ue_pusch_scheduled_count[u3.ue_index]); +} + class scheduler_policy_alloc_bounds_test : public base_scheduler_policy_test, - public ::testing::TestWithParam + public ::testing::TestWithParam { protected: scheduler_policy_alloc_bounds_test() : @@ -406,6 +665,7 @@ class scheduler_policy_alloc_bounds_test : public base_scheduler_policy_test, return sched_cfg_; }()) { + run_slot(); } }; @@ -452,8 +712,12 @@ TEST_P(scheduler_policy_alloc_bounds_test, scheduler_allocates_pusch_within_conf ASSERT_TRUE(this->res_grid[0].result.ul.puschs.back().pusch_cfg.rbs.type1() == expected_vrb_interval); } -INSTANTIATE_TEST_SUITE_P(scheduler_policy, scheduler_policy_test, testing::Values(policy_type::time_rr)); +INSTANTIATE_TEST_SUITE_P(scheduler_policy, + scheduler_policy_test, + testing::Values(policy_scheduler_type::time_rr, policy_scheduler_type::time_pf)); INSTANTIATE_TEST_SUITE_P(scheduler_policy, scheduler_policy_partial_slot_tdd_test, - testing::Values(policy_type::time_rr)); -INSTANTIATE_TEST_SUITE_P(scheduler_policy, scheduler_policy_alloc_bounds_test, testing::Values(policy_type::time_rr)); + testing::Values(policy_scheduler_type::time_rr, policy_scheduler_type::time_pf)); +INSTANTIATE_TEST_SUITE_P(scheduler_policy, + scheduler_policy_alloc_bounds_test, + testing::Values(policy_scheduler_type::time_rr, policy_scheduler_type::time_pf)); diff --git a/tests/unittests/scheduler/ue_scheduling/ue_cell_test.cpp b/tests/unittests/scheduler/ue_scheduling/ue_cell_test.cpp index a1ca5f2157..e621162213 100644 --- a/tests/unittests/scheduler/ue_scheduling/ue_cell_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/ue_cell_test.cpp @@ -92,11 +92,11 @@ class ue_cell_tester : public ::testing::Test ue_cell_configuration ue_cc_cfg; }; -TEST_F(ue_cell_tester, when_dl_nof_prb_allocated_increases_estimated_dl_bit_rate_increases) +TEST_F(ue_cell_tester, when_dl_nof_prb_allocated_increases_estimated_dl_rate_increases) { ue_cell ue_cc{to_du_ue_index(0), to_rnti(0x4601), ue_cc_cfg, {}}; - double current_bitrate = 0; + double current_rate = 0; // We keep MCS constant for this test. const sch_mcs_index ue_mcs = 28; @@ -106,22 +106,22 @@ TEST_F(ue_cell_tester, when_dl_nof_prb_allocated_increases_estimated_dl_bit_rate for (unsigned nof_prbs = 0; nof_prbs < MAX_NOF_PRBS; ++nof_prbs) { for (const auto& pdsch_td_cfg : ue_cc.cfg().cell_cfg_common.dl_cfg_common.init_dl_bwp.pdsch_common.pdsch_td_alloc_list) { - const pdsch_config_params pdsch_cfg = get_pdsch_cfg_params(ue_cc, pdsch_td_cfg, dci_type); - const double estimated_bitrate = ue_cc.get_estimated_dl_brate_kbps(pdsch_cfg, ue_mcs, nof_prbs); - ASSERT_GE(estimated_bitrate, current_bitrate); - current_bitrate = estimated_bitrate; + const pdsch_config_params pdsch_cfg = get_pdsch_cfg_params(ue_cc, pdsch_td_cfg, dci_type); + const double estimated_rate = ue_cc.get_estimated_dl_rate(pdsch_cfg, ue_mcs, nof_prbs); + ASSERT_GE(estimated_rate, current_rate); + current_rate = estimated_rate; } } } -TEST_F(ue_cell_tester, when_mcs_increases_estimated_dl_bit_rate_increases) +TEST_F(ue_cell_tester, when_mcs_increases_estimated_dl_rate_increases) { // Maximum MCS value for 64QAM MCS table. const sch_mcs_index max_mcs = 28; ue_cell ue_cc{to_du_ue_index(0), to_rnti(0x4601), ue_cc_cfg, {}}; - double current_bitrate = 0; + double current_rate = 0; // We keep nof. PRBs allocated constant for this test. const unsigned nof_prbs = 20; @@ -131,21 +131,21 @@ TEST_F(ue_cell_tester, when_mcs_increases_estimated_dl_bit_rate_increases) for (sch_mcs_index ue_mcs = 1; ue_mcs < max_mcs; ++ue_mcs) { for (const auto& pdsch_td_cfg : ue_cc.cfg().cell_cfg_common.dl_cfg_common.init_dl_bwp.pdsch_common.pdsch_td_alloc_list) { - const pdsch_config_params pdsch_cfg = get_pdsch_cfg_params(ue_cc, pdsch_td_cfg, dci_type); - const double estimated_bitrate = ue_cc.get_estimated_dl_brate_kbps(pdsch_cfg, ue_mcs, nof_prbs); + const pdsch_config_params pdsch_cfg = get_pdsch_cfg_params(ue_cc, pdsch_td_cfg, dci_type); + const double estimated_rate = ue_cc.get_estimated_dl_rate(pdsch_cfg, ue_mcs, nof_prbs); // NOTE: In case of 64QAM MCS table MCS 17 has lower spectral density than MCS 16 but the estimated bitrate will // remain equal hence its required to use the check greater than or equal below. - ASSERT_GE(estimated_bitrate, current_bitrate); - current_bitrate = estimated_bitrate; + ASSERT_GE(estimated_rate, current_rate); + current_rate = estimated_rate; } } } -TEST_F(ue_cell_tester, when_ul_nof_prb_allocated_increases_estimated_ul_bit_rate_increases) +TEST_F(ue_cell_tester, when_ul_nof_prb_allocated_increases_estimated_ul_rate_increases) { ue_cell ue_cc{to_du_ue_index(0), to_rnti(0x4601), ue_cc_cfg, {}}; - double current_bitrate = 0; + double current_rate = 0; // We keep MCS constant for this test. const sch_mcs_index ue_mcs = 15; @@ -155,22 +155,22 @@ TEST_F(ue_cell_tester, when_ul_nof_prb_allocated_increases_estimated_ul_bit_rate for (unsigned nof_prbs = 0; nof_prbs < MAX_NOF_PRBS; ++nof_prbs) { for (const auto& pusch_td_cfg : ue_cc.cfg().cell_cfg_common.ul_cfg_common.init_ul_bwp.pusch_cfg_common->pusch_td_alloc_list) { - const pusch_config_params pusch_cfg = get_pusch_cfg_params(ue_cc, pusch_td_cfg, dci_type); - const double estimated_bitrate = ue_cc.get_estimated_ul_brate_kbps(pusch_cfg, ue_mcs, nof_prbs); - ASSERT_GE(estimated_bitrate, current_bitrate); - current_bitrate = estimated_bitrate; + const pusch_config_params pusch_cfg = get_pusch_cfg_params(ue_cc, pusch_td_cfg, dci_type); + const double estimated_rate = ue_cc.get_estimated_ul_rate(pusch_cfg, ue_mcs, nof_prbs); + ASSERT_GE(estimated_rate, current_rate); + current_rate = estimated_rate; } } } -TEST_F(ue_cell_tester, when_mcs_increases_estimated_ul_bit_rate_increases) +TEST_F(ue_cell_tester, when_mcs_increases_estimated_ul_rate_increases) { // Maximum MCS value for 64QAM MCS table. const sch_mcs_index max_mcs = 28; ue_cell ue_cc{to_du_ue_index(0), to_rnti(0x4601), ue_cc_cfg, {}}; - double current_bitrate = 0; + double current_rate = 0; // We keep nof. PRBs allocated constant for this test. const unsigned nof_prbs = 20; @@ -180,12 +180,12 @@ TEST_F(ue_cell_tester, when_mcs_increases_estimated_ul_bit_rate_increases) for (sch_mcs_index ue_mcs = 1; ue_mcs < max_mcs; ++ue_mcs) { for (const auto& pusch_td_cfg : ue_cc.cfg().cell_cfg_common.ul_cfg_common.init_ul_bwp.pusch_cfg_common->pusch_td_alloc_list) { - const pusch_config_params pusch_cfg = get_pusch_cfg_params(ue_cc, pusch_td_cfg, dci_type); - const double estimated_bitrate = ue_cc.get_estimated_ul_brate_kbps(pusch_cfg, ue_mcs, nof_prbs); + const pusch_config_params pusch_cfg = get_pusch_cfg_params(ue_cc, pusch_td_cfg, dci_type); + const double estimated_rate = ue_cc.get_estimated_ul_rate(pusch_cfg, ue_mcs, nof_prbs); // NOTE: In case of 64QAM MCS table MCS 17 has lower spectral density than MCS 16 but the estimated bitrate will // remain equal hence its required to use the check greater than or equal below. - ASSERT_GE(estimated_bitrate, current_bitrate); - current_bitrate = estimated_bitrate; + ASSERT_GE(estimated_rate, current_rate); + current_rate = estimated_rate; } } } diff --git a/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp b/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp index c8a1665c20..652e19ad07 100644 --- a/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp @@ -142,7 +142,7 @@ TEST_F(ue_grid_allocator_tester, .h_id = to_harq_id(0), .recommended_nof_bytes = nof_bytes_to_schedule}; - ASSERT_EQ(alloc.allocate_dl_grant(grant), alloc_outcome::success); + ASSERT_EQ(alloc.allocate_dl_grant(grant).status, alloc_status::success); ASSERT_TRUE(crb_lims.contains(res_grid[0].result.dl.ue_grants.back().pdsch_cfg.rbs.type1())); } @@ -165,7 +165,7 @@ TEST_F(ue_grid_allocator_tester, when_using_non_fallback_dci_format_use_mcs_tabl .h_id = to_harq_id(0), .recommended_nof_bytes = nof_bytes_to_schedule}; - ASSERT_EQ(alloc.allocate_dl_grant(grant), alloc_outcome::success); + ASSERT_EQ(alloc.allocate_dl_grant(grant).status, alloc_status::success); ASSERT_EQ(res_grid[0].result.dl.ue_grants.back().pdsch_cfg.codewords.back().mcs_table, srsran::pdsch_mcs_table::qam256); } @@ -186,7 +186,7 @@ TEST_F(ue_grid_allocator_tester, remaining_dl_rbs_are_allocated_if_max_pucch_per .user = &u1, .cell_index = to_du_cell_index(0), .h_id = to_harq_id(0), .recommended_nof_bytes = sched_bytes}; // Successfully allocates RBs corresponding to the grant. - ASSERT_EQ(alloc.allocate_dl_grant(grant1), alloc_outcome::success); + ASSERT_EQ(alloc.allocate_dl_grant(grant1).status, alloc_status::success); ASSERT_GE(res_grid[0].result.dl.ue_grants.back().pdsch_cfg.codewords.back().tb_size_bytes, sched_bytes); // Since UE dedicated SearchSpace is a UE specific SearchSpace (Not CSS). Entire BWP CRBs can be used for allocation. @@ -197,7 +197,7 @@ TEST_F(ue_grid_allocator_tester, remaining_dl_rbs_are_allocated_if_max_pucch_per .user = &u2, .cell_index = to_du_cell_index(0), .h_id = to_harq_id(0), .recommended_nof_bytes = sched_bytes}; // Allocates all remaining RBs to UE2. - ASSERT_EQ(alloc.allocate_dl_grant(grant2), alloc_outcome::success); + ASSERT_EQ(alloc.allocate_dl_grant(grant2).status, alloc_status::success); ASSERT_EQ(res_grid[0].result.dl.ue_grants.back().pdsch_cfg.rbs.type1().length(), (total_crbs - crbs_allocated)); } @@ -224,7 +224,7 @@ TEST_F(ue_grid_allocator_tester, remaining_ul_rbs_are_allocated_if_max_ul_grant_ .max_nof_rbs = max_nof_rbs_to_schedule}; // Successfully allocates RBs corresponding to the grant. - ASSERT_EQ(alloc.allocate_ul_grant(grant1), alloc_outcome::success); + ASSERT_EQ(alloc.allocate_ul_grant(grant1).status, alloc_status::success); unsigned k2 = cell_cfg.ul_cfg_common.init_ul_bwp.pusch_cfg_common ->pusch_td_alloc_list[res_grid[0].result.dl.ul_pdcchs.back().dci.c_rnti_f0_1.time_resource] .k2; @@ -238,7 +238,7 @@ TEST_F(ue_grid_allocator_tester, remaining_ul_rbs_are_allocated_if_max_ul_grant_ .max_nof_rbs = max_nof_rbs_to_schedule}; // Allocates all remaining RBs to UE2. - ASSERT_EQ(alloc.allocate_ul_grant(grant2), alloc_outcome::success); + ASSERT_EQ(alloc.allocate_ul_grant(grant2).status, alloc_status::success); k2 = cell_cfg.ul_cfg_common.init_ul_bwp.pusch_cfg_common ->pusch_td_alloc_list[res_grid[0].result.dl.ul_pdcchs.back().dci.c_rnti_f0_1.time_resource] .k2; @@ -260,7 +260,7 @@ TEST_F(ue_grid_allocator_tester, no_two_pdschs_are_allocated_in_same_slot_for_a_ .h_id = to_harq_id(0), .recommended_nof_bytes = nof_bytes_to_schedule}; - ASSERT_EQ(alloc.allocate_dl_grant(grant), alloc_outcome::success); + ASSERT_EQ(alloc.allocate_dl_grant(grant).status, alloc_status::success); // Second PDSCH grant for the UE. const ue_pdsch_grant grant2{.user = &u, @@ -269,7 +269,7 @@ TEST_F(ue_grid_allocator_tester, no_two_pdschs_are_allocated_in_same_slot_for_a_ .recommended_nof_bytes = nof_bytes_to_schedule}; // Second PDSCH grant should not be allocated. - ASSERT_NE(alloc.allocate_dl_grant(grant2), alloc_outcome::success); + ASSERT_NE(alloc.allocate_dl_grant(grant2).status, alloc_status::success); } TEST_F(ue_grid_allocator_tester, no_two_puschs_are_allocated_in_same_slot_for_a_ue) @@ -287,7 +287,7 @@ TEST_F(ue_grid_allocator_tester, no_two_puschs_are_allocated_in_same_slot_for_a_ .h_id = to_harq_id(0), .recommended_nof_bytes = nof_bytes_to_schedule}; - ASSERT_EQ(alloc.allocate_ul_grant(grant), alloc_outcome::success); + ASSERT_EQ(alloc.allocate_ul_grant(grant).status, alloc_status::success); // Second PUSCH grant for the UE. const ue_pusch_grant grant2{.user = &u, @@ -296,7 +296,7 @@ TEST_F(ue_grid_allocator_tester, no_two_puschs_are_allocated_in_same_slot_for_a_ .recommended_nof_bytes = nof_bytes_to_schedule}; // Second PUSCH grant should not be allocated. - ASSERT_NE(alloc.allocate_ul_grant(grant2), alloc_outcome::success); + ASSERT_NE(alloc.allocate_ul_grant(grant2).status, alloc_status::success); } TEST_F(ue_grid_allocator_tester, consecutive_puschs_for_a_ue_are_allocated_in_increasing_order_of_time) @@ -316,7 +316,7 @@ TEST_F(ue_grid_allocator_tester, consecutive_puschs_for_a_ue_are_allocated_in_in .h_id = to_harq_id(0), .recommended_nof_bytes = nof_bytes_to_schedule}; - ASSERT_EQ(alloc.allocate_ul_grant(grant), alloc_outcome::success); + ASSERT_EQ(alloc.allocate_ul_grant(grant).status, alloc_status::success); unsigned k2 = cell_cfg.ul_cfg_common.init_ul_bwp.pusch_cfg_common ->pusch_td_alloc_list[res_grid[0].result.dl.ul_pdcchs.back().dci.c_rnti_f0_1.time_resource] .k2; @@ -329,7 +329,7 @@ TEST_F(ue_grid_allocator_tester, consecutive_puschs_for_a_ue_are_allocated_in_in .recommended_nof_bytes = nof_bytes_to_schedule}; const auto outcome = alloc.allocate_ul_grant(grant2); - if (outcome == srsran::alloc_outcome::success) { + if (outcome.status == alloc_status::success) { k2 = cell_cfg.ul_cfg_common.init_ul_bwp.pusch_cfg_common ->pusch_td_alloc_list[res_grid[0].result.dl.ul_pdcchs.back().dci.c_rnti_f0_1.time_resource] .k2; @@ -344,7 +344,7 @@ TEST_F(ue_grid_allocator_tester, consecutive_puschs_for_a_ue_are_allocated_in_in .cell_index = to_du_cell_index(0), .h_id = to_harq_id(2), .recommended_nof_bytes = nof_bytes_to_schedule}; - ASSERT_EQ(alloc.allocate_ul_grant(grant3), alloc_outcome::success); + ASSERT_EQ(alloc.allocate_ul_grant(grant3).status, alloc_status::success); k2 = cell_cfg.ul_cfg_common.init_ul_bwp.pusch_cfg_common ->pusch_td_alloc_list[res_grid[0].result.dl.ul_pdcchs.back().dci.c_rnti_f0_1.time_resource] .k2; @@ -369,7 +369,7 @@ TEST_F(ue_grid_allocator_tester, .h_id = to_harq_id(0), .recommended_nof_bytes = nof_bytes_to_schedule}; - ASSERT_EQ(alloc.allocate_dl_grant(grant), alloc_outcome::success); + ASSERT_EQ(alloc.allocate_dl_grant(grant).status, alloc_status::success); const search_space_info* ss_info = u.get_pcell().cfg().find_search_space(res_grid[0].result.dl.dl_pdcchs.back().ctx.context.ss_id); unsigned k1 = @@ -384,7 +384,7 @@ TEST_F(ue_grid_allocator_tester, .recommended_nof_bytes = nof_bytes_to_schedule}; const auto outcome = alloc.allocate_dl_grant(grant2); - if (outcome == srsran::alloc_outcome::success) { + if (outcome.status == srsran::alloc_status::success) { ss_info = u.get_pcell().cfg().find_search_space(res_grid[0].result.dl.dl_pdcchs.back().ctx.context.ss_id); k1 = ss_info->get_k1_candidates() [*res_grid[0].result.dl.dl_pdcchs.back().dci.c_rnti_f1_1.pdsch_harq_fb_timing_indicator]; @@ -399,7 +399,7 @@ TEST_F(ue_grid_allocator_tester, .cell_index = to_du_cell_index(0), .h_id = to_harq_id(2), .recommended_nof_bytes = nof_bytes_to_schedule}; - ASSERT_EQ(alloc.allocate_dl_grant(grant3), alloc_outcome::success); + ASSERT_EQ(alloc.allocate_dl_grant(grant3).status, alloc_status::success); ss_info = u.get_pcell().cfg().find_search_space(res_grid[0].result.dl.dl_pdcchs.back().ctx.context.ss_id); k1 = ss_info diff --git a/tests/unittests/scheduler/ue_scheduling/ue_pxsch_alloc_param_candidate_searcher_test.cpp b/tests/unittests/scheduler/ue_scheduling/ue_pxsch_alloc_param_candidate_searcher_test.cpp index 7d18ea87d9..120330cd58 100644 --- a/tests/unittests/scheduler/ue_scheduling/ue_pxsch_alloc_param_candidate_searcher_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/ue_pxsch_alloc_param_candidate_searcher_test.cpp @@ -79,7 +79,7 @@ TEST_F(ue_pxsch_alloc_param_candidate_searcher_test, only_searchspaces_in_ue_ded span ss_list = ue_cc->cfg().cfg_dedicated().init_dl_bwp.pdcch_cfg->search_spaces; ue_pdsch_alloc_param_candidate_searcher dl_searcher( - *ue_ptr, to_du_cell_index(0), ue_cc->harqs.dl_harq(h_id), slot_point{0, 0}); + *ue_ptr, to_du_cell_index(0), ue_cc->harqs.dl_harq(h_id), slot_point{0, 0}, {}); ASSERT_TRUE(not dl_searcher.is_empty()); for (const auto& candidate : dl_searcher) { bool ss_present_in_ue_ded_cfg = @@ -89,7 +89,7 @@ TEST_F(ue_pxsch_alloc_param_candidate_searcher_test, only_searchspaces_in_ue_ded ASSERT_TRUE(ss_present_in_ue_ded_cfg); } ue_pusch_alloc_param_candidate_searcher ul_searcher( - *ue_ptr, to_du_cell_index(0), ue_cc->harqs.ul_harq(h_id), slot_point{0, 0}); + *ue_ptr, to_du_cell_index(0), ue_cc->harqs.ul_harq(h_id), slot_point{0, 0}, {}); ASSERT_TRUE(not dl_searcher.is_empty()); for (const auto& candidate : ul_searcher) { bool ss_present_in_ue_ded_cfg = diff --git a/tests/unittests/support/protocol_transaction_manager_test.cpp b/tests/unittests/support/protocol_transaction_manager_test.cpp index 4e18235f01..8fc31588b6 100644 --- a/tests/unittests/support/protocol_transaction_manager_test.cpp +++ b/tests/unittests/support/protocol_transaction_manager_test.cpp @@ -189,7 +189,7 @@ TEST_F(protocol_transaction_test, // Test Section. ASSERT_TRUE(t.ready()); - ASSERT_TRUE(t.get().is_error()); + ASSERT_FALSE(t.get().has_value()); ASSERT_EQ(t.get().error(), protocol_transaction_failure::timeout); } @@ -215,6 +215,6 @@ TEST_F( tick(); } ASSERT_TRUE(t.ready()); - ASSERT_TRUE(t.get().is_error()); + ASSERT_FALSE(t.get().has_value()); ASSERT_EQ(t.get().error(), protocol_transaction_failure::timeout); } diff --git a/utils/trx_srsran/trx_srsran.cpp b/utils/trx_srsran/trx_srsran.cpp index f83d7cc817..1b8ef9db84 100644 --- a/utils/trx_srsran/trx_srsran.cpp +++ b/utils/trx_srsran/trx_srsran.cpp @@ -149,7 +149,7 @@ struct trx_srsran_session_context { std::array tx_port_args; std::array rx_port_args; std::string factory_str; - std::string log_level; + srslog::basic_levels log_level; // Radio factory. std::unique_ptr factory; // Radio session. @@ -620,10 +620,11 @@ int trx_driver_init(TRXState* s1) // Parse logging level. char* log_level_char = trx_get_param_string(s1, "log_level"); if (log_level_char != nullptr) { - context.log_level = std::string(log_level_char); + auto value = srslog::str_to_basic_level(std::string(log_level_char)); + context.log_level = value.has_value() ? value.value() : srslog::basic_levels::info; free(log_level_char); } else { - context.log_level = "info"; + context.log_level = srslog::basic_levels::info; } // Parse noise spectral density.