diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 426537b51d..5edb66acf9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,6 +36,9 @@ include: - project: softwareradiosystems/ci/tools ref: "20" file: .gitlab/ci-shared/tools/test_reporter.yml + - project: softwareradiosystems/ci/tools + ref: "20" + file: .gitlab/ci-shared/tools/tagger.yml - local: .gitlab/ci/builders/version.yml - local: .gitlab/ci/build.yml - local: .gitlab/ci/trx.yml @@ -281,6 +284,7 @@ clangsa: tags: - ${AMD64_TAG} interruptible: false + timeout: 2 hours script: - | mkdir -p build @@ -457,13 +461,28 @@ pages: # Release dryrun ################################################################################ update agpl main dryrun: - extends: .update agpl main + extends: update private branch stage: .post rules: - if: $CI_DESCRIPTION =~ /Nightly Build Unit Tests/ when: always - variables: - MODE: "dryrun" + variables: + PRIVATE_BRANCH: agpl_main + HEADERS: "true" + MODE: "dryrun" + needs: [] + +create-tags: + extends: .create-tag + stage: .post + rules: + - if: $CI_DESCRIPTION =~ /Nightly Build Unit Tests/ + when: always + script: + - | + for name in $TAG_NAME_ARRAY; do + create_tag "${name}_$(date -u +"%Y.%m.%d")" + done needs: [] ################################################################################ diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index d52d236c25..012125ee9b 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -133,6 +133,16 @@ variables: artifacts: false before_script: - !reference [.fetch_src_cache, script] + - | + # Print build parameters + echo "INFRASTRUCTURE_TAG=${INFRASTRUCTURE_TAG}" + echo "OS=${OS}" + echo "COMPILER=${COMPILER}" + echo "TEST_MODE=${TEST_MODE}" + echo "BUILD_ARGS=${BUILD_ARGS}" + echo "MAKE_ARGS=${MAKE_ARGS}" + echo "UHD_VERSION=${UHD_VERSION}" + echo "DPDK_VERSION=${DPDK_VERSION}" - | build_srsgnb() { start_time=$(date +%s) @@ -196,7 +206,7 @@ variables: if [ -n "${DPDK_VERSION}" ]; then BUILD_CMD="-d ${DPDK_VERSION} ${BUILD_CMD}" - export LD_LIBRARY_PATH=/opt/dpdk/${DPDK_VERSION}/lib/x86_64-linux-gnu/ + export LD_LIBRARY_PATH=/opt/dpdk/${DPDK_VERSION}/lib/x86_64-linux-gnu/:/opt/dpdk/${DPDK_VERSION}/lib/aarch64-linux-gnu/:${LD_LIBRARY_PATH} fi if [ -n "${UHD_VERSION}" ]; then BUILD_CMD="-u ${UHD_VERSION} ${BUILD_CMD}" @@ -333,7 +343,7 @@ variables: mv /tmp/srscu ${CI_PROJECT_DIR}/build/apps/cu/srscu mv /tmp/srsdu ${CI_PROJECT_DIR}/build/apps/du/srsdu fi - timeout: 4h + timeout: 6h artifacts: &build_artifacts when: always reports: diff --git a/.gitlab/ci/docker.yml b/.gitlab/ci/docker.yml index 75979a04ec..8a08195a6a 100644 --- a/.gitlab/ci/docker.yml +++ b/.gitlab/ci/docker.yml @@ -277,11 +277,8 @@ grafana server image latest: changes: <<: *srs_container_changes variables: - MODE: build - if: $CI_DESCRIPTION =~ /Nightly Build Unit Tests/ interruptible: false - variables: - MODE: publish variables: KUBERNETES_CPU_REQUEST: 6 KUBERNETES_CPU_LIMIT: 6 @@ -290,13 +287,14 @@ grafana server image latest: REGISTRY_URI: $GITLAB_REGISTRY_URI CONTEXT: ${CI_PROJECT_DIR} DOCKERFILE: docker - VERSION: latest - OVERWRITE: "true" + MODE: build + timeout: 2 hours tags: - ${TAG} before_script: - | - export NAME="srsran_${SPLIT}_${SUFFIX}" + export NAME="srsran-project" + export VERSION="${SPLIT}_${SUFFIX}" export BUILD_ARGS="LIB=${LIB};LIB_VERSION=${LIB_VERSION};ARCH=${ARCH};NUM_CORES=${KUBERNETES_CPU_LIMIT};EXTRA_CMAKE_ARGS=\"${EXTRA_CMAKE_ARGS}\"" needs: - job: gnb docker compose diff --git a/.gitlab/ci/e2e.yml b/.gitlab/ci/e2e.yml index c18dc0523a..cfd8698c6f 100644 --- a/.gitlab/ci/e2e.yml +++ b/.gitlab/ci/e2e.yml @@ -139,6 +139,15 @@ e2e request and config validation: reports: junit: tests/e2e/out.xml script: + - | + # Print E2E parameters + echo "TESTBED=${TESTBED}" + echo "MARKERS=${MARKERS}" + echo "KEYWORDS=${KEYWORDS}" + echo "PYTEST_ARGS=${PYTEST_ARGS}" + echo "RETINA_PARAM_ARGS=${RETINA_PARAM_ARGS}" + echo "RETINA_LAUNCHER_ARGS=${RETINA_LAUNCHER_ARGS}" + echo "E2E_LOG_LEVEL=${E2E_LOG_LEVEL}" # Clean LFS files - | while read -r line; do @@ -151,11 +160,9 @@ e2e request and config validation: - | echo "" >> .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 - + # Modify request to shared the complete folder with the gnb container + - | + yq -i '(.[] | select(.type == "gnb") | .shared_files) += [{"local_path": "../../", "remote_path": env(CI_PROJECT_DIR), "is_executable": false}]' ${CI_PROJECT_DIR}/.gitlab/ci/e2e/retina_request_${TESTBED}.yml # Set username for retina - | cd tests/e2e @@ -368,19 +375,39 @@ amari 32UE memcheck: - *txrx-lib - *retina-needs -amari 4 cudu: +cudu amari 8UE: extends: .zmq variables: TESTBED: zmq_cudu - MARKERS: "smoke" - RETINA_PARAM_ARGS: "gnb.all.pcap=True gnb.all.mac_enable=True gnb.all.rlc_enable=True gnb.all.enable_integrity_protection=True" + 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" allow_failure: true needs: - job: "basic relwithdeb" artifacts: true - *txrx-lib - *retina-needs + parallel: + matrix: + - KEYWORDS: ["reestablishment and sequentially"] + +cudu amari 32UE: + extends: .zmq + variables: + TESTBED: zmq_cudu + 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" + allow_failure: true + needs: + - job: "basic relwithdeb" + artifacts: true + - *txrx-lib + - *retina-needs + parallel: + matrix: + - KEYWORDS: ["ping", "iperf and tcp and not band:3 and bandwidth:50"] ################################################################################ # TEST MODE diff --git a/.gitlab/ci/e2e/.env b/.gitlab/ci/e2e/.env index 3bd92a1fde..17054138de 100644 --- a/.gitlab/ci/e2e/.env +++ b/.gitlab/ci/e2e/.env @@ -1,8 +1,6 @@ -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.50.11 +RETINA_VERSION=0.51.5 UBUNTU_VERSION=24.04 AMARISOFT_VERSION=2023-09-08 SRSUE_VERSION=23.11 diff --git a/.gitlab/ci/e2e/retina_request_android_b200.yml b/.gitlab/ci/e2e/retina_request_android_b200.yml index 894279c8bb..cdecc4350e 100644 --- a/.gitlab/ci/e2e/retina_request_android_b200.yml +++ b/.gitlab/ci/e2e/retina_request_android_b200.yml @@ -45,8 +45,8 @@ - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - local_path: ${GNB_BINARY_PATH} - remote_path: ${GNB_REMOTE_PATH} - is_executable: ${GNB_IS_EXECUTABLE} + remote_path: /usr/local/bin/gnb + is_executable: true - 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 8dfb117e5d..3bb197fa53 100644 --- a/.gitlab/ci/e2e/retina_request_android_n300.yml +++ b/.gitlab/ci/e2e/retina_request_android_n300.yml @@ -46,8 +46,8 @@ - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - local_path: ${GNB_BINARY_PATH} - remote_path: ${GNB_REMOTE_PATH} - is_executable: ${GNB_IS_EXECUTABLE} + remote_path: /usr/local/bin/gnb + is_executable: true - 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 12cd8e3b7b..75f5d7e4e9 100644 --- a/.gitlab/ci/e2e/retina_request_android_x300.yml +++ b/.gitlab/ci/e2e/retina_request_android_x300.yml @@ -46,8 +46,8 @@ - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - local_path: ${GNB_BINARY_PATH} - remote_path: ${GNB_REMOTE_PATH} - is_executable: ${GNB_IS_EXECUTABLE} + remote_path: /usr/local/bin/gnb + is_executable: true - 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 7d85400bfe..0fb9ea0274 100644 --- a/.gitlab/ci/e2e/retina_request_rf_b200.yml +++ b/.gitlab/ci/e2e/retina_request_rf_b200.yml @@ -50,8 +50,8 @@ - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - local_path: ${GNB_BINARY_PATH} - remote_path: ${GNB_REMOTE_PATH} - is_executable: ${GNB_IS_EXECUTABLE} + remote_path: /usr/local/bin/gnb + is_executable: true - 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 b973546855..67f761939a 100644 --- a/.gitlab/ci/e2e/retina_request_test_mode.yml +++ b/.gitlab/ci/e2e/retina_request_test_mode.yml @@ -29,8 +29,8 @@ - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - local_path: ${GNB_BINARY_PATH} - remote_path: ${GNB_REMOTE_PATH} - is_executable: ${GNB_IS_EXECUTABLE} + remote_path: /usr/local/bin/gnb + is_executable: true - name: open5gs type: 5gc diff --git a/.gitlab/ci/e2e/retina_request_viavi.yml b/.gitlab/ci/e2e/retina_request_viavi.yml index 13d36d3af9..8ae401c245 100644 --- a/.gitlab/ci/e2e/retina_request_viavi.yml +++ b/.gitlab/ci/e2e/retina_request_viavi.yml @@ -34,8 +34,8 @@ - LD_LIBRARY_PATH: /opt/dpdk/${DPDK_VERSION}/lib/x86_64-linux-gnu/ shared_files: - local_path: ${GNB_BINARY_PATH} - remote_path: ${GNB_REMOTE_PATH} - is_executable: ${GNB_IS_EXECUTABLE} + remote_path: /usr/local/bin/gnb + is_executable: true - name: metrics-server type: generic @@ -47,7 +47,7 @@ requests: "500Mi" ephemeral-storage: requests: "1G" - taints: ["retina"] + labels: ["kubernetes.io/hostname=k8s-worker-uhd1"] environment: - URL: ${RETINA_METRICS_SERVER_URL} - ORG: ${RETINA_METRICS_SERVER_ORG} diff --git a/.gitlab/ci/e2e/retina_request_zmq.yml b/.gitlab/ci/e2e/retina_request_zmq.yml index bcf0e0cc6d..821ae50498 100644 --- a/.gitlab/ci/e2e/retina_request_zmq.yml +++ b/.gitlab/ci/e2e/retina_request_zmq.yml @@ -54,8 +54,8 @@ - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - local_path: ${GNB_BINARY_PATH} - remote_path: ${GNB_REMOTE_PATH} - is_executable: ${GNB_IS_EXECUTABLE} + remote_path: /usr/local/bin/gnb + is_executable: true - 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 d3384737d9..ddd442b725 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml @@ -57,8 +57,8 @@ - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - local_path: ${GNB_BINARY_PATH} - remote_path: ${GNB_REMOTE_PATH} - is_executable: ${GNB_IS_EXECUTABLE} + remote_path: /usr/local/bin/gnb + is_executable: true - name: open5gs type: 5gc diff --git a/.gitlab/ci/e2e/retina_request_zmq_cudu.yml b/.gitlab/ci/e2e/retina_request_zmq_cudu.yml index fc8cc614a8..f448bf81f1 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_cudu.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_cudu.yml @@ -51,9 +51,6 @@ resources: - type: zmq shared_files: - - local_path: ${GNB_BINARY_PATH} - remote_path: ${GNB_REMOTE_PATH} - is_executable: ${GNB_IS_EXECUTABLE} - local_path: ../../build/apps/cu/srscu remote_path: /usr/local/bin/srscu 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 73b0bcb54e..78b9cb20f2 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_single_ue.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_single_ue.yml @@ -53,8 +53,8 @@ - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - local_path: ${GNB_BINARY_PATH} - remote_path: ${GNB_REMOTE_PATH} - is_executable: ${GNB_IS_EXECUTABLE} + remote_path: /usr/local/bin/gnb + is_executable: true - 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 68ed57d13c..6e5b272406 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_srsue.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_srsue.yml @@ -47,8 +47,8 @@ - PATH: ${PATH}:/builds/softwareradiosystems/srsgnb/build/apps/gnb shared_files: - local_path: ${GNB_BINARY_PATH} - remote_path: ${GNB_REMOTE_PATH} - is_executable: ${GNB_IS_EXECUTABLE} + remote_path: /usr/local/bin/gnb + is_executable: true - name: open5gs type: 5gc diff --git a/.gitlab/ci/release.yml b/.gitlab/ci/release.yml index 225637325b..274341cee3 100644 --- a/.gitlab/ci/release.yml +++ b/.gitlab/ci/release.yml @@ -19,37 +19,94 @@ stages: - public - release -.update agpl main: +.my-rules: + script: + - &on_public_push $CI_COMMIT_TAG =~ /^\d{4}\.\d{2}\.\d{2}$/ + - &on_public_release $CI_COMMIT_TAG =~ /^\d{2}\.\d{2}$/ + - &on_custom_push $CI_COMMIT_TAG =~ /^\w+_\d{4}\.\d{2}\.\d{2}$/ + +################################################################################ +# Load variables +################################################################################ +load release variables: + stage: .pre + image: ubuntu:24.04 + rules: + - if: *on_public_push + - if: *on_public_release + - if: *on_custom_push + variables: + GIT_STRATEGY: none + script: + - | + if [[ $CI_COMMIT_TAG =~ ^([a-zA-Z0-9_]+)_([0-9]{4})\.([0-9]{2})\.([0-9]{2})$ ]]; then + KEY="${BASH_REMATCH[1]}" + echo "PRIVATE_BRANCH=dev_$KEY" >> repo.env + echo "PUBLIC_NAME=srsRAN $KEY" >> repo.env + echo "PUBLIC_REPO=softwareradiosystems/srsRAN_$KEY" >> repo.env + echo "HEADERS=" >> repo.env + echo "PUBLIC_BRANCH=main" >> repo.env + else + echo "PRIVATE_BRANCH=agpl_main" >> repo.env + echo "PUBLIC_NAME=srsRAN Project" >> repo.env + echo "PUBLIC_REPO=srsran/srsRAN_Project" >> repo.env + echo "HEADERS=true" >> repo.env + fi + - cat repo.env + artifacts: + reports: + dotenv: repo.env + +################################################################################ +# Update private branches +################################################################################ + +update private branch: stage: private interruptible: false variables: GIT_STRATEGY: none - BRANCH: agpl_main - MODE: "" - image: ubuntu:22.04 + PRIVATE_BRANCH: "" + HEADERS: "" + MODE: "push" + image: ubuntu:24.04 + rules: + - if: *on_public_push + - if: *on_public_release + - if: *on_custom_push before_script: - DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends git git-lfs apt-transport-https ca-certificates + - git lfs install + - git lfs uninstall || true + # Extra LFS filters for OS (like Ubuntu) where 'git lfs uninstall' has no effect + - git config --global filter.lfs.smudge "git-lfs smudge --skip -- %f" + - git config --global filter.lfs.process "git-lfs filter-process --skip" - git config --global user.name "${CODEBOT_USERNAME}" - git config --global user.email "${CODEBOT_LONG_USERNAME}@noreply.gitlab.com" script: - git clone https://${CODEBOT_USERNAME}:${CODEBOT_TOKEN}@gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}.git /${CI_PROJECT_NAME} - cd /${CI_PROJECT_NAME} - git fetch -q origin ${CI_COMMIT_SHA} && git checkout ${CI_COMMIT_SHA} - - .gitlab/ci/release/auto_merge.sh ${CI_COMMIT_SHA} ${BRANCH} ${MODE} - -update agpl main: - extends: .update agpl main - rules: - - if: $ON_TAG - variables: - MODE: "push" + - .gitlab/ci/release/auto_merge.sh ${CI_COMMIT_SHA} ${PRIVATE_BRANCH} + - | + if [ "$HEADERS" = "true" ]; then + .gitlab/ci/release/update_headers.sh + git commit -a --amend --no-edit + fi + - | + if [ "$MODE" = "push" ]; then + git push origin "${PRIVATE_BRANCH}" + fi + needs: &release-var-needs + - job: "load release variables" + artifacts: true generate testvector tar gz: stage: private rules: - - if: $ON_TAG + - if: *on_public_release interruptible: false - image: ubuntu:22.04 + image: ubuntu:24.04 script: - find . -name "*.tar.gz" -exec tar -rvf phy_testvectors.tar.gz {} \; artifacts: @@ -61,10 +118,11 @@ coverity-agpl: extends: .coverity_base stage: private rules: - - if: $ON_TAG + - if: *on_public_push + - if: *on_public_release variables: GIT_STRATEGY: none - PRIVATE_BRANCH: agpl_main + PRIVATE_BRANCH: "" before_script: - export PROJECT_NAME="srsRAN_5G_agpl" - export DESCRIPTION="srsRAN Project AGPL build" @@ -75,79 +133,113 @@ coverity-agpl: - git clone --depth 1 --branch $PRIVATE_BRANCH https://${CODEBOT_USERNAME}:${CODEBOT_TOKEN}@gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}.git /${CI_PROJECT_NAME} - cd /${CI_PROJECT_NAME} needs: - - job: update agpl main + - *release-var-needs + - job: update private branch optional: false artifacts: false +################################################################################ +# Publish +################################################################################ + .publish: stage: public interruptible: false image: alpine:3.16.0 # sh entrypoint variables: GIT_STRATEGY: none - PRIVATE_BRANCH: agpl_main - PUBLIC_REPO: srsran/srsRAN_Project + PRIVATE_BRANCH: "" + PUBLIC_REPO: "" PUBLIC_BRANCH: "" before_script: - - apk add git + - apk add git git-lfs + - git lfs install + - git lfs uninstall || true + - git config --global filter.lfs.smudge "git-lfs smudge --skip -- %f" + - git config --global filter.lfs.process "git-lfs filter-process --skip" script: # Download private repo and add public as origin - git clone --branch $PRIVATE_BRANCH https://${CODEBOT_USERNAME}:${CODEBOT_TOKEN}@gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}.git /${CI_PROJECT_NAME} - cd /${CI_PROJECT_NAME} - git remote add github_public https://${PUBLIC_TOKEN}@github.com/${PUBLIC_REPO}.git - # Push code to github main + # Push code to public repo - git push github_public ${PRIVATE_BRANCH}:${PUBLIC_BRANCH} -publish branch test: +publish test: extends: .publish rules: - - if: $ON_TAG - when: manual - allow_failure: false + - if: *on_public_push + - if: *on_public_release + when: manual + allow_failure: false variables: PUBLIC_BRANCH: test needs: - - job: update agpl main + - *release-var-needs + - job: update private branch optional: false artifacts: false publish main: extends: .publish rules: - - if: $ON_TAG - when: manual - allow_failure: false + - if: *on_public_push + - if: *on_public_release + when: manual + allow_failure: false variables: PUBLIC_BRANCH: main needs: - - job: publish branch test + - *release-var-needs + - job: publish test optional: false artifacts: false -notify main published: +publish public: + extends: .publish + rules: + - if: *on_custom_push + needs: + - *release-var-needs + - job: update private branch + optional: false + artifacts: true + +notify branch published: extends: .notifier stage: public rules: - - if: $ON_TAG + - if: *on_public_push + - if: *on_public_release + - if: *on_custom_push needs: + - *release-var-needs - job: publish main - optional: false - artifacts: false + optional: true + artifacts: true + - job: publish public + optional: true + artifacts: true variables: - CI_DESCRIPTION: "Public GitHub Updated" - MSG: "main branch has been updated." + CI_DESCRIPTION: "$PUBLIC_REPO GitHub Updated" + MSG: "$PUBLIC_BRANCH branch has been updated." SLACK_CHANNEL: $SLACK_CHANNEL_OK LEVEL: "ok" +################################################################################ +# Release +################################################################################ + release public: stage: release rules: - - if: $ON_TAG - when: manual - allow_failure: false + - if: *on_public_release + when: manual + allow_failure: false interruptible: false image: alpine:3.16.0 # sh entrypoint needs: + - *release-var-needs - job: publish main optional: false artifacts: false @@ -157,9 +249,9 @@ release public: variables: GIT_STRATEGY: none ARTIFACT: phy_testvectors.tar.gz - PUBLIC_NAME: srsRAN Project - PUBLIC_REPO: srsran/srsRAN_Project - PUBLIC_BRANCH: main + PUBLIC_NAME: "" + PUBLIC_REPO: "" + PUBLIC_BRANCH: "" before_script: - apk add git curl jq script: @@ -204,8 +296,9 @@ notify release published: extends: .notifier stage: release rules: - - if: $ON_TAG + - if: *on_public_release needs: + - *release-var-needs - job: release public optional: false artifacts: false diff --git a/.gitlab/ci/release/auto_merge.sh b/.gitlab/ci/release/auto_merge.sh index 0d6138093c..ccbebbb0f8 100755 --- a/.gitlab/ci/release/auto_merge.sh +++ b/.gitlab/ci/release/auto_merge.sh @@ -95,6 +95,8 @@ remove_lfs_files() { while read -r line; do git rm --cached "$line" done < <(git lfs ls-files | sed -r 's/^.{13}//') + git lfs untrack "*test_data.tar.gz" + git lfs untrack "*.png" } main() { @@ -107,7 +109,6 @@ main() { local source_branch=$1 local target_branch=$2 - local mode="${3:-push}" # Checkout target branch git fetch -q origin "$target_branch" @@ -131,13 +132,7 @@ main() { git commit --no-edit fi remove_lfs_files - "$(dirname "$0")/update_headers.sh" git commit -a --amend --no-edit - - # Push - if [ "$mode" = "push" ]; then - git push origin "$target_branch" - fi } main "$@" diff --git a/.gitlab/configuration.json b/.gitlab/configuration.json index 63f95e6d13..dc1a6e3c4f 100644 --- a/.gitlab/configuration.json +++ b/.gitlab/configuration.json @@ -130,6 +130,30 @@ ], "code_owner_approval_required": false, "name": "agpl_main" + }, + { + "allow_force_push": false, + "allowed_to_merge": [ + { + "id": 120427278, + "access_level": 40, + "access_level_description": "Maintainers", + "group_id": null, + "user_id": null + } + ], + "allowed_to_push": [ + { + "id": 133149184, + "access_level": 40, + "access_level_description": "Maintainers", + "group_id": null, + "user_id": null, + "deploy_key_id": null + } + ], + "code_owner_approval_required": false, + "name": "dev_*" } ], "protectedtags": [ @@ -137,8 +161,9 @@ "name": "*", "create_access_levels": [ { - "access_level": 40, - "access_level_description": "Maintainers", + "id": 8850508, + "access_level": 30, + "access_level_description": "Developers + Maintainers", "group_id": null, "user_id": null, "deploy_key_id": null diff --git a/apps/services/metrics_log_helper.cpp b/apps/services/metrics_log_helper.cpp index 3ea0ec7cf3..0f38ab3daa 100644 --- a/apps/services/metrics_log_helper.cpp +++ b/apps/services/metrics_log_helper.cpp @@ -28,92 +28,6 @@ using namespace srsran; -static std::string scaled_fmt_integer(uint64_t num) -{ - static constexpr std::array suffixes = {"", "k", "M", "G", "T", "P", "E", "Z"}; - static const std::array max_nums = []() { - std::array nums{0}; - for (unsigned i = 0, e = nums.size(); i != e; ++i) { - nums[i] = (uint64_t)std::pow(10, i * 3); - } - return nums; - }(); - - if (num < max_nums[1]) { - return fmt::format("{}", num); - } - - for (unsigned i = 1, e = max_nums.size() - 1; i != e; ++i) { - if (num < max_nums[i + 1]) { - return fmt::format("{:.3g}{}", num / static_cast(max_nums[i]), suffixes[i]); - } - } - - return "Invalid number"; -} - -static std::string float_to_string(float f, int digits, int field_width) -{ - std::ostringstream os; - int precision; - - if (std::isnan(f) || std::abs(f) < 0.0001f) { - f = 0.f; - precision = digits - 1; - } else { - precision = digits - (int)(std::log10(std::abs(f + 0.0001f)) - 2 * DBL_EPSILON); - } - - precision = std::max(precision, 0); - - os << std::fixed << std::setprecision(precision) << f; - return os.str(); -} - -static std::string float_to_eng_string(float f, int digits) -{ - static char const* const prefixes[2][9] = { - { - "", - "m", - "u", - "n", - "p", - "f", - "a", - "z", - "y", - }, - { - "", - "k", - "M", - "G", - "T", - "P", - "E", - "Z", - "Y", - }, - }; - - const int degree = (f == 0.f) ? 0 : std::lrint(std::floor(std::log10(std::abs(f)) / 3)); - - std::string factor; - - if (std::abs(degree) < 9) { - factor = prefixes[(degree < 0) ? 0 : 1][std::abs(degree)]; - } else { - return "failed"; - } - - const double scaled = f * std::pow(1000.0, -degree); - if (degree != 0) { - return float_to_string(scaled, digits, 5) + factor; - } - return float_to_string(scaled, digits, 5 - factor.length()) + factor; -} - void metrics_log_helper::report_metrics(const scheduler_cell_metrics& metrics) { fmt::memory_buffer buffer; @@ -147,7 +61,7 @@ void metrics_log_helper::report_metrics(const scheduler_cell_metrics& metrics) fmt::format_to(buffer, " dl_mcs={}", int(ue.dl_mcs.to_uint())); if (ue.dl_brate_kbps > 0) { - fmt::format_to(buffer, " dl_brate_kbps={}", float_to_eng_string(ue.dl_brate_kbps * 1e3, 1)); + fmt::format_to(buffer, " dl_brate_kbps={}", float_to_eng_string(ue.dl_brate_kbps * 1e3, 1, false)); } else { fmt::format_to(buffer, " dl_brate_kbps={}", 0); } @@ -159,7 +73,7 @@ void metrics_log_helper::report_metrics(const scheduler_cell_metrics& metrics) } else { fmt::format_to(buffer, " dl_error_rate={}%", 0); } - fmt::format_to(buffer, " dl_bs={}", scaled_fmt_integer(ue.dl_bs)); + fmt::format_to(buffer, " dl_bs={}", scaled_fmt_integer(ue.dl_bs, false)); if (!std::isnan(ue.pusch_snr_db) && !iszero(ue.pusch_snr_db)) { fmt::format_to(buffer, " pusch_snr_db={:.1f}", std::clamp(ue.pusch_snr_db, -99.9f, 99.9f)); @@ -179,7 +93,7 @@ void metrics_log_helper::report_metrics(const scheduler_cell_metrics& metrics) fmt::format_to(buffer, " ul_mcs={}", ue.ul_mcs.to_uint()); if (ue.ul_brate_kbps > 0) { - fmt::format_to(buffer, " ul_brate_kbps={}", float_to_eng_string(ue.ul_brate_kbps * 1e3, 1)); + fmt::format_to(buffer, " ul_brate_kbps={}", float_to_eng_string(ue.ul_brate_kbps * 1e3, 1, false)); } else { fmt::format_to(buffer, " ul_brate_kbps={}", 0); } @@ -192,9 +106,9 @@ void metrics_log_helper::report_metrics(const scheduler_cell_metrics& metrics) } else { fmt::format_to(buffer, " ul_error_rate={}%", 0); } - fmt::format_to(buffer, " bsr={}", scaled_fmt_integer(ue.bsr)); + fmt::format_to(buffer, " bsr={}", scaled_fmt_integer(ue.bsr, false)); if (ue.last_ta.has_value()) { - fmt::format_to(buffer, " last_ta={}s", float_to_eng_string(ue.last_ta->to_seconds(), 0)); + fmt::format_to(buffer, " last_ta={}s", float_to_eng_string(ue.last_ta->to_seconds(), 0, false)); } else { fmt::format_to(buffer, " last_ta=n/a"); } @@ -216,10 +130,10 @@ void metrics_log_helper::report_metrics(const rlc_metrics& metrics) } fmt::memory_buffer buffer; fmt::format_to(buffer, "RLC Metrics:"); - fmt::format_to(buffer, " du_index={}", metrics.du_index); - fmt::format_to(buffer, " ue_index={}", metrics.ue_index); - fmt::format_to(buffer, " rb_id={}", metrics.rb_id); - fmt::format_to(buffer, " TX=[{}]", metrics.tx); - fmt::format_to(buffer, " RX=[{}] ", metrics.rx); + fmt::format_to(buffer, " du={}", metrics.du_index); + fmt::format_to(buffer, " ue={}", metrics.ue_index); + fmt::format_to(buffer, " rb={}", metrics.rb_id); + fmt::format_to(buffer, " TX=[{}]", format_rlc_tx_metrics(metrics.metrics_period, metrics.tx)); + fmt::format_to(buffer, " RX=[{}] ", format_rlc_rx_metrics(metrics.metrics_period, metrics.rx)); logger.debug("{}", to_c_str(buffer)); } diff --git a/apps/services/metrics_plotter_stdout.cpp b/apps/services/metrics_plotter_stdout.cpp index f86df2cdb3..e8f23be998 100644 --- a/apps/services/metrics_plotter_stdout.cpp +++ b/apps/services/metrics_plotter_stdout.cpp @@ -21,6 +21,7 @@ */ #include "metrics_plotter_stdout.h" +#include "srsran/support/engineering_notation.h" #include "srsran/support/math_utils.h" #include #include @@ -28,30 +29,6 @@ using namespace srsran; -static std::string scaled_fmt_integer(uint64_t num) -{ - static constexpr std::array suffixes = {"", "k", "M", "G", "T", "P", "E", "Z"}; - static const std::array max_nums = []() { - std::array nums{0}; - for (unsigned i = 0, e = nums.size(); i != e; ++i) { - nums[i] = (uint64_t)std::pow(10, i * 3); - } - return nums; - }(); - - if (num < max_nums[1]) { - return fmt::format("{:>6}", num); - } - - for (unsigned i = 1, e = max_nums.size() - 1; i != e; ++i) { - if (num < max_nums[i + 1]) { - return fmt::format("{:>5.3g}{}", num / static_cast(max_nums[i]), suffixes[i]); - } - } - - return "Invalid number"; -} - static void print_header() { fmt::print("\n"); @@ -62,68 +39,6 @@ static void print_header() " ta phr\n"); } -static std::string float_to_string(float f, int digits, int field_width) -{ - std::ostringstream os; - int precision; - - if (std::isnan(f) || std::abs(f) < 0.0001f) { - f = 0.f; - precision = digits - 1; - } else { - precision = digits - (int)(std::log10(std::abs(f + 0.0001f)) - 2 * DBL_EPSILON); - } - - precision = std::max(precision, 0); - - os << std::setw(field_width) << std::fixed << std::setprecision(precision) << f; - return os.str(); -} - -static std::string float_to_eng_string(float f, int digits) -{ - static char const* const prefixes[2][9] = { - { - "", - "m", - "u", - "n", - "p", - "f", - "a", - "z", - "y", - }, - { - "", - "k", - "M", - "G", - "T", - "P", - "E", - "Z", - "Y", - }, - }; - - const int degree = (f == 0.f) ? 0 : std::lrint(std::floor(std::log10(std::abs(f)) / 3)); - - std::string factor; - - if (std::abs(degree) < 9) { - factor = prefixes[(degree < 0) ? 0 : 1][std::abs(degree)]; - } else { - return "failed"; - } - - const double scaled = f * std::pow(1000.0, -degree); - if (degree != 0) { - return float_to_string(scaled, digits, 5) + factor; - } - return " " + float_to_string(scaled, digits, 5 - factor.length()) + factor; -} - void metrics_plotter_stdout::report_metrics(const scheduler_cell_metrics& metrics) { if (!print_metrics) { @@ -155,7 +70,7 @@ void metrics_plotter_stdout::report_metrics(const scheduler_cell_metrics& metric fmt::print(" {:>2}", int(ue.dl_mcs.to_uint())); if (ue.dl_brate_kbps > 0) { - fmt::print(" {:>6.6}", float_to_eng_string(ue.dl_brate_kbps * 1e3, 1)); + fmt::print(" {:>6.6}", float_to_eng_string(ue.dl_brate_kbps * 1e3, 1, true)); } else { fmt::print(" {:>6}", 0); } @@ -167,7 +82,7 @@ void metrics_plotter_stdout::report_metrics(const scheduler_cell_metrics& metric } else { fmt::print(" {:>3}%", 0); } - fmt::print(" {}", scaled_fmt_integer(ue.dl_bs)); + fmt::print(" {}", scaled_fmt_integer(ue.dl_bs, true)); fmt::print(" |"); @@ -189,7 +104,7 @@ void metrics_plotter_stdout::report_metrics(const scheduler_cell_metrics& metric fmt::print(" {:>2}", ue.ul_mcs.to_uint()); if (ue.ul_brate_kbps > 0) { - fmt::print(" {:>6.6}", float_to_eng_string(ue.ul_brate_kbps * 1e3, 1)); + fmt::print(" {:>6.6}", float_to_eng_string(ue.ul_brate_kbps * 1e3, 1, true)); } else { fmt::print(" {:>6}", 0); } @@ -202,9 +117,9 @@ void metrics_plotter_stdout::report_metrics(const scheduler_cell_metrics& metric } else { fmt::print(" {:>3}%", 0); } - fmt::print(" {}", scaled_fmt_integer(ue.bsr)); + fmt::print(" {}", scaled_fmt_integer(ue.bsr, true)); if (ue.last_ta.has_value()) { - fmt::print(" {}", float_to_eng_string(ue.last_ta->to_seconds(), 0)); + fmt::print(" {}", float_to_eng_string(ue.last_ta->to_seconds(), 0, true)); } else { fmt::print(" n/a"); } diff --git a/apps/services/worker_manager.cpp b/apps/services/worker_manager.cpp index 9e3a58cf99..75e2949014 100644 --- a/apps/services/worker_manager.cpp +++ b/apps/services/worker_manager.cpp @@ -81,14 +81,6 @@ worker_manager::worker_manager(const dynamic_du_unit_config& du_cfg, ru)); } - create_low_prio_executors(expert_appcfg, - cu_cp_pcap_cfg, - cu_up_pcap_cfg, - du_cfg.du_high_cfg.config.pcaps, - du_cfg.du_high_cfg.config.cells_cfg.size(), - gtpu_queue_size); - associate_low_prio_executors(); - // Determine whether the gnb app is running in realtime or in simulated environment. bool is_blocking_mode_active = false; if (std::holds_alternative(du_cfg.ru_cfg)) { @@ -96,6 +88,15 @@ worker_manager::worker_manager(const dynamic_du_unit_config& du_cfg, is_blocking_mode_active = sdr_cfg.device_driver == "zmq"; } + create_low_prio_executors(expert_appcfg, + cu_cp_pcap_cfg, + cu_up_pcap_cfg, + du_cfg.du_high_cfg.config.pcaps, + du_cfg.du_high_cfg.config.cells_cfg.size(), + gtpu_queue_size, + not is_blocking_mode_active); + associate_low_prio_executors(); + create_du_executors(is_blocking_mode_active, nof_cells, du_cfg.du_low_cfg, du_cfg.fapi_cfg); create_ru_executors(du_cfg.ru_cfg, du_cfg.du_high_cfg.config); } @@ -323,7 +324,8 @@ void worker_manager::create_low_prio_executors(const expert_execution_appconfig& const cu_up_unit_pcap_config& cu_up_pcaps, const du_high_unit_pcap_config& du_pcaps, unsigned nof_cells, - unsigned gtpu_queue_size) + unsigned gtpu_queue_size, + bool rt_mode) { using namespace execution_config_helper; // TODO: split executor creation and association to workers @@ -333,7 +335,7 @@ void worker_manager::create_low_prio_executors(const expert_execution_appconfig& // Used for PCAP writing. non_rt_pool.executors.emplace_back("low_prio_exec", task_priority::max - 1); // Used for control plane and timer management. - non_rt_pool.executors.emplace_back("high_prio_exec", task_priority::max); + non_rt_pool.executors.push_back({"high_prio_exec", task_priority::max}); // Used to serialize all CU-UP tasks, while CU-UP does not support multithreading. non_rt_pool.executors.push_back({"cu_up_strand", task_priority::max - 1, @@ -347,9 +349,11 @@ void worker_manager::create_low_prio_executors(const expert_execution_appconfig& // Configuration of strands for PCAP writing. These strands will use the low priority executor. append_pcap_strands(low_prio_strands, cu_cp_pcaps, cu_up_pcaps, du_pcaps); - // Configuration of strand for the control plane handling (CU-CP and DU-high control plane). This strand will - // support two priority levels, the highest being for timer management. - strand cp_strand{{{"timer_exec", concurrent_queue_policy::lockfree_spsc, task_worker_queue_size}, + // Configuration of strand for the control plane handling (CU-CP and DU-high control plane). + // This strand will support two priority levels, the highest being for timer management. + // Note: In case of non-RT operation, we make the timer_exec synchronous. This will have the effect of stopping + // the lower layers from running faster than this strand. + strand cp_strand{{{"timer_exec", concurrent_queue_policy::lockfree_spsc, task_worker_queue_size, not rt_mode}, {"ctrl_exec", concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}}; high_prio_strands.push_back(cp_strand); @@ -427,7 +431,7 @@ void worker_manager::create_du_low_executors(bool is_blocking_mode_active, task_worker_queue_size, {{"phy_exec"}}, affinity_mng.front().calcute_affinity_mask(sched_affinity_mask_types::l1_dl), - os_thread_realtime_priority::max()); + os_thread_realtime_priority::no_realtime()); for (unsigned cell_id = 0, cell_end = nof_cells; cell_id != cell_end; ++cell_id) { upper_pusch_exec.push_back(exec_mng.executors().at("phy_exec")); diff --git a/apps/services/worker_manager.h b/apps/services/worker_manager.h index 1d80c6d89e..59d6ef2b49 100644 --- a/apps/services/worker_manager.h +++ b/apps/services/worker_manager.h @@ -137,7 +137,8 @@ struct worker_manager : public worker_manager_executor_getter { const cu_up_unit_pcap_config& cu_up_pcaps, const du_high_unit_pcap_config& du_pcaps, unsigned nof_cells, - unsigned gtpu_queue_size); + unsigned gtpu_queue_size, + bool rt_mode); void associate_low_prio_executors(); std::vector create_fapi_workers(unsigned nof_cells); 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 4f02222608..309cf43ee5 100644 --- a/apps/units/flexible_du/du_high/du_high_config.h +++ b/apps/units/flexible_du/du_high/du_high_config.h @@ -221,10 +221,12 @@ struct du_high_unit_pucch_config { int p0_nominal = -90; /// \c PUCCH-Config parameters. - /// Number of PUCCH Format 1 resources per UE for HARQ-ACK reporting. Values {1,...,8}. - unsigned nof_ue_pucch_f1_res_harq = 8; + /// Number of PUCCH Format 0/1 resources per UE for HARQ-ACK reporting. Values {1,...,8}. + unsigned nof_ue_pucch_f0_or_f1_res_harq = 8; /// Number of PUCCH Format 2 resources per UE for HARQ-ACK reporting. Values {1,...,8}. unsigned nof_ue_pucch_f2_res_harq = 6; + /// Force Format 0 for the PUCCH resources belonging to PUCCH resource set 0. + bool use_format_0 = false; /// \brief Number of separate PUCCH resource sets for HARQ-ACK reporting that are available in a cell. /// \remark UEs will be distributed possibly over different HARQ-ACK PUCCH sets; the more sets, the fewer UEs will /// have to share the same set, which reduces the chances that UEs won't be allocated PUCCH due to lack of @@ -240,19 +242,19 @@ struct du_high_unit_pucch_config { /// these are the only ones supported. Values: {1, 2, 2.5, 4, 5, 8, 10, 16, 20, 40, 80, 160, 320}. float sr_period_msec = 20.0F; + /// PUCCH F0 resource parameter. + /// Set true for PUCCH Format 0 intra-slot frequency hopping. + bool f0_intraslot_freq_hopping = false; + /// PUCCH F1 resource parameters. - /// Number of symbols for PUCCH Format 1. Values {4, 14}. - unsigned f1_nof_symbols = 14; - bool f1_enable_occ = true; + /// \brief Enable Orthogonal Cover Code for PUCCH Format 1. + bool f1_enable_occ = true; /// \brief Number of different Initial Cyclic Shifts that can be used for PUCCH Format 1. /// Values: {1, 2, 3, 4, 6, 12}; 0 corresponds to "no cyclic shift". unsigned nof_cyclic_shift = 2; /// Set true for PUCCH Format 1 intra-slot frequency hopping. bool f1_intraslot_freq_hopping = false; - /// PUCCH F2 resource parameters. - /// Number of symbols for PUCCH Format 2. Values {1, 2}. - unsigned f2_nof_symbols = 2; /// Max number of PRBs for PUCCH Format 2. Values {1,...,16}. unsigned f2_max_nof_rbs = 1; /// \brief Maximum payload in bits that can be carried by PUCCH Format 2. Values {-1,...,11}. @@ -498,6 +500,8 @@ struct du_high_unit_prach_config { struct du_high_unit_base_cell_config { /// Physical cell identifier. pci_t pci = 1; + /// Sector Id (4-14 bits) that gets concatenated with gNB-Id to form the NR Cell Identity (NCI). + std::optional sector_id; /// Downlink arfcn. unsigned dl_arfcn = 536020; /// Common subcarrier spacing for the entire resource grid. It must be supported by the band SS raster. 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 a169ef446c..4e2338e905 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 @@ -772,19 +772,21 @@ static void configure_cli11_pucch_args(CLI::App& app, du_high_unit_pucch_config& ->check(CLI::IsMember({1.0F, 2.0F, 2.5F, 4.0F, 5.0F, 8.0F, 10.0F, 16.0F, 20.0F, 40.0F, 80.0F, 160.0F, 320.0F})); add_option(app, "--f1_nof_ue_res_harq", - pucch_params.nof_ue_pucch_f1_res_harq, - "Number of PUCCH F1 resources available per UE for HARQ") + pucch_params.nof_ue_pucch_f0_or_f1_res_harq, + "Number of PUCCH F0/F1 resources available per UE for HARQ") ->capture_default_str() ->check(CLI::Range(1, 8)); add_option(app, - "--f1_nof_cell_res_sr", + "--f0_or_f1_nof_cell_res_sr", pucch_params.nof_cell_sr_resources, "Number of PUCCH F1 resources available per cell for SR") ->capture_default_str() ->check(CLI::Range(1, 50)); - add_option(app, "--f1_nof_symbols", pucch_params.f1_nof_symbols, "Number of symbols for PUCCH F1 resources") - ->capture_default_str() - ->check(CLI::Range(4, 14)); + add_option(app, + "--f0_intraslot_freq_hop", + pucch_params.f0_intraslot_freq_hopping, + "Enable intra-slot frequency hopping for PUCCH F0") + ->capture_default_str(); add_option(app, "--f1_enable_occ", pucch_params.f1_enable_occ, "Enable OCC for PUCCH F1")->capture_default_str(); add_option(app, "--f1_nof_cyclic_shifts", @@ -816,9 +818,6 @@ static void configure_cli11_pucch_args(CLI::App& app, du_high_unit_pucch_config& "Number of PUCCH F2 resources available per cell for CSI") ->capture_default_str() ->check(CLI::Range(0, 50)); - add_option(app, "--f2_nof_symbols", pucch_params.f2_nof_symbols, "Number of symbols for PUCCH F2 resources") - ->capture_default_str() - ->check(CLI::Range(1, 2)); add_option(app, "--f2_max_nof_rbs", pucch_params.f2_max_nof_rbs, "Max number of RBs for PUCCH F2 resources") ->capture_default_str() ->check(CLI::Range(1, 16)); @@ -1035,6 +1034,13 @@ static void configure_cli11_sib_args(CLI::App& app, du_high_unit_sib_config& sib static void configure_cli11_common_cell_args(CLI::App& app, du_high_unit_base_cell_config& cell_params) { add_option(app, "--pci", cell_params.pci, "PCI")->capture_default_str()->check(CLI::Range(0, 1007)); + add_option(app, + "--sector_id", + cell_params.sector_id, + "Sector ID (4-14 bits). This value is concatenated with the gNB Id to form the NR Cell Identity " + "(NCI). If not specified, a unique value for the DU is automatically derived") + ->capture_default_str() + ->check(CLI::Range(0U, (1U << 14) - 1U)); add_option(app, "--dl_arfcn", cell_params.dl_arfcn, "Downlink ARFCN")->capture_default_str(); add_auto_enum_option(app, "--band", cell_params.band, "NR band"); add_option_function( @@ -1657,7 +1663,17 @@ static void derive_cell_auto_params(du_high_unit_base_cell_config& cell_cfg) static void derive_auto_params(du_high_unit_config& config) { + unsigned next_sector_id = 0; for (auto& cell : config.cells_cfg) { + if (not cell.cell.sector_id.has_value()) { + // Auto-derive sector ID if not defined. + cell.cell.sector_id = next_sector_id; + next_sector_id++; + } else { + // If sector ID defined for this cell, recompute the next sector ID. + next_sector_id = std::max(next_sector_id, cell.cell.sector_id.value() + 1); + } + derive_cell_auto_params(cell.cell); } } 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 e68e1bfd58..cbb0d5bd9b 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 @@ -225,7 +225,6 @@ std::vector srsran::generate_du_cell_config(const du_high_unit_c std::vector out_cfg; out_cfg.reserve(config.cells_cfg.size()); - unsigned cell_id = 0; for (const auto& cell : config.cells_cfg) { cell_config_builder_params param; const du_high_unit_base_cell_config& base_cell = cell.cell; @@ -290,7 +289,7 @@ std::vector srsran::generate_du_cell_config(const du_high_unit_c // Set the rest of the parameters. 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.nr_cgi.nci = nr_cell_identity::create(config.gnb_id, base_cell.sector_id.value()).value(); out_cell.tac = base_cell.tac; out_cell.searchspace0_idx = param.search_space0_index; @@ -518,20 +517,24 @@ std::vector srsran::generate_du_cell_config(const du_high_unit_c // Parameters for PUCCH-Config builder (these parameters will be used later on to generate the PUCCH resources). pucch_builder_params& du_pucch_cfg = out_cell.pucch_cfg; const du_high_unit_pucch_config& user_pucch_cfg = base_cell.pucch_cfg; - du_pucch_cfg.nof_ue_pucch_f1_res_harq = user_pucch_cfg.nof_ue_pucch_f1_res_harq; + du_pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq = user_pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq; du_pucch_cfg.nof_ue_pucch_f2_res_harq = user_pucch_cfg.nof_ue_pucch_f2_res_harq; du_pucch_cfg.nof_cell_harq_pucch_res_sets = user_pucch_cfg.nof_cell_harq_pucch_sets; du_pucch_cfg.nof_sr_resources = user_pucch_cfg.nof_cell_sr_resources; du_pucch_cfg.nof_csi_resources = user_pucch_cfg.nof_cell_csi_resources; - du_pucch_cfg.f1_params.nof_symbols = user_pucch_cfg.f1_nof_symbols; - du_pucch_cfg.f1_params.occ_supported = user_pucch_cfg.f1_enable_occ; - du_pucch_cfg.f1_params.nof_cyc_shifts = static_cast(user_pucch_cfg.nof_cyclic_shift); - du_pucch_cfg.f1_params.intraslot_freq_hopping = user_pucch_cfg.f1_intraslot_freq_hopping; - du_pucch_cfg.f2_params.nof_symbols = user_pucch_cfg.f2_nof_symbols; - du_pucch_cfg.f2_params.max_code_rate = user_pucch_cfg.max_code_rate; - du_pucch_cfg.f2_params.max_nof_rbs = user_pucch_cfg.f2_max_nof_rbs; - du_pucch_cfg.f2_params.intraslot_freq_hopping = user_pucch_cfg.f2_intraslot_freq_hopping; - du_pucch_cfg.f2_params.max_payload_bits = user_pucch_cfg.max_payload_bits; + if (user_pucch_cfg.use_format_0) { + auto& f0_params = du_pucch_cfg.f0_or_f1_params.emplace(); + f0_params.intraslot_freq_hopping = user_pucch_cfg.f0_intraslot_freq_hopping; + } else { + auto& f1_params = du_pucch_cfg.f0_or_f1_params.emplace(); + f1_params.occ_supported = user_pucch_cfg.f1_enable_occ; + f1_params.nof_cyc_shifts = static_cast(user_pucch_cfg.nof_cyclic_shift); + f1_params.intraslot_freq_hopping = user_pucch_cfg.f1_intraslot_freq_hopping; + } + du_pucch_cfg.f2_params.max_code_rate = user_pucch_cfg.max_code_rate; + du_pucch_cfg.f2_params.max_nof_rbs = user_pucch_cfg.f2_max_nof_rbs; + du_pucch_cfg.f2_params.intraslot_freq_hopping = user_pucch_cfg.f2_intraslot_freq_hopping; + du_pucch_cfg.f2_params.max_payload_bits = user_pucch_cfg.max_payload_bits; // Parameters for PUSCH-Config. if (not out_cell.ue_ded_serv_cell_cfg.ul_config.has_value()) { @@ -629,7 +632,6 @@ std::vector srsran::generate_du_cell_config(const du_high_unit_c if (!error) { report_error("Invalid configuration DU cell detected.\n> {}\n", error.error()); } - ++cell_id; } return out_cfg; 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 165342373c..a5d21b4145 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 @@ -22,6 +22,7 @@ #include "du_high_config_validator.h" #include "srsran/ran/duplex_mode.h" +#include "srsran/ran/nr_cell_identity.h" #include "srsran/ran/pdcch/pdcch_type0_css_coreset_config.h" #include "srsran/ran/prach/prach_helper.h" #include "srsran/rlc/rlc_config.h" @@ -757,7 +758,7 @@ static bool validate_cell_unit_config(const du_high_unit_cell_config& config) } /// Validates the given list of cell application configuration. Returns true on success, otherwise false. -static bool validate_cells_unit_config(span config) +static bool validate_cells_unit_config(span config, const gnb_id_t& gnb_id) { unsigned tac = config[0].cell.tac; for (const auto& cell : config) { @@ -768,6 +769,12 @@ static bool validate_cells_unit_config(span conf fmt::print("The TAC must be the same for all cells\n"); return false; } + + if (cell.cell.sector_id.has_value() and + not nr_cell_identity::create(gnb_id, cell.cell.sector_id.value()).has_value()) { + fmt::print("Invalid Sector ID {}, for a gNB Id of {} bits\n", cell.cell.sector_id.value(), gnb_id.bit_length); + return false; + } } // Checks parameter collisions between cells that can lead to problems. @@ -784,6 +791,13 @@ static bool validate_cells_unit_config(span conf cell1.cell.dl_arfcn, cell1.cell.pci); } + if (cell1.cell.sector_id.has_value() and cell1.cell.sector_id == cell2.cell.sector_id and + cell1.cell.plmn == cell2.cell.plmn) { + fmt::print("Error: two cells with the same PLMN (i.e., {}) and cell ID (i.e., {})\n", + cell1.cell.plmn, + cell1.cell.sector_id); + return false; + } // Two cells on the same frequency should not share the same PRACH root sequence index. if ((cell1.cell.prach_cfg.prach_frequency_start == cell2.cell.prach_cfg.prach_frequency_start) && @@ -1049,7 +1063,7 @@ static bool validate_qos_config(span config) bool srsran::validate_du_high_config(const du_high_unit_config& config, const os_sched_affinity_bitmask& available_cpus) { - if (!validate_cells_unit_config(config.cells_cfg)) { + if (!validate_cells_unit_config(config.cells_cfg, config.gnb_id)) { return false; } diff --git a/apps/units/flexible_du/du_high/du_high_config_yaml_writer.cpp b/apps/units/flexible_du/du_high/du_high_config_yaml_writer.cpp index 2747c2050d..ae42493862 100644 --- a/apps/units/flexible_du/du_high/du_high_config_yaml_writer.cpp +++ b/apps/units/flexible_du/du_high/du_high_config_yaml_writer.cpp @@ -420,16 +420,15 @@ static YAML::Node build_du_high_pucch_section(const du_high_unit_pucch_config& c node["p0_nominal"] = config.p0_nominal; node["sr_period_ms"] = config.sr_period_msec; - node["f1_nof_ue_res_harq"] = config.nof_ue_pucch_f1_res_harq; - node["f1_nof_cell_res_sr"] = config.nof_cell_sr_resources; - node["f1_nof_symbols"] = config.f1_nof_symbols; + node["f0_or_f1_nof_ue_res_harq"] = config.nof_ue_pucch_f0_or_f1_res_harq; + node["f0_or_f1_nof_cell_res_sr"] = config.nof_cell_sr_resources; + node["f0_intraslot_freq_hop"] = config.f0_intraslot_freq_hopping; node["f1_enable_occ"] = config.f1_enable_occ; node["f1_nof_cyclic_shifts"] = config.nof_cyclic_shift; node["f1_intraslot_freq_hop"] = config.f1_intraslot_freq_hopping; node["nof_cell_harq_pucch_res_sets"] = config.nof_cell_harq_pucch_sets; node["f2_nof_ue_res_harq"] = config.nof_ue_pucch_f2_res_harq; node["f2_nof_cell_res_csi"] = config.nof_cell_csi_resources; - node["f2_nof_symbols"] = config.f2_nof_symbols; node["f2_max_nof_rbs"] = config.f2_max_nof_rbs; node["f2_max_code_rate"] = to_string(config.max_code_rate); node["f2_intraslot_freq_hop"] = config.f2_intraslot_freq_hopping; diff --git a/apps/units/flexible_du/du_high/du_high_wrapper_config_helper.cpp b/apps/units/flexible_du/du_high/du_high_wrapper_config_helper.cpp index 49dfde577d..132445d334 100644 --- a/apps/units/flexible_du/du_high/du_high_wrapper_config_helper.cpp +++ b/apps/units/flexible_du/du_high/du_high_wrapper_config_helper.cpp @@ -34,8 +34,9 @@ using namespace srsran; void srsran::configure_du_high_metrics(const du_high_unit_config& du_high_unit_cfg, metrics_plotter_stdout& metrics_stdout, - metrics_plotter_json& metrics_json, metrics_log_helper& metrics_logger, + metrics_plotter_json& metrics_json, + rlc_metrics_notifier& rlc_json_metrics, e2_metric_connector_manager& e2_metric_connectors, metrics_hub& metrics_hub) { @@ -80,6 +81,29 @@ void srsran::configure_du_high_metrics(const du_high_unit_config& du_high_unit metrics_hub.add_source(std::move(source)); } + + // Set up sources for the DU Scheruler UE metrics and add them to metric hub. + for (unsigned i = 0; i != cells.size(); i++) { + if (du_high_unit_cfg.metrics.rlc.report_period != 0) { + // This source aggregates the RLC metrics from all DRBs in a single DU. + std::string source_name = "DU RLC " + std::to_string(i); + auto rlc_source = std::make_unique(source_name); + + if (du_high_unit_cfg.metrics.rlc.json_enabled) { + // Connect JSON metrics plotter to RLC metric source. + rlc_source->add_subscriber(rlc_json_metrics); + } + if (metrics_logger.is_enabled()) { + rlc_source->add_subscriber(metrics_logger); + } + if (du_high_unit_cfg.e2_cfg.enable_du_e2) { + // Connect E2 agent to RLC metric source. + rlc_source->add_subscriber(e2_metric_connectors.get_e2_du_metric_notifier(i)); + } + // Pass RLC metric source to the DU high. + metrics_hub.add_source(std::move(rlc_source)); + } + } } void srsran::fill_du_high_wrapper_config(du_high_wrapper_config& out_cfg, @@ -91,10 +115,8 @@ void srsran::fill_du_high_wrapper_config(du_high_wrapper_config& out_cfg, timer_manager& timer_mng, mac_pcap& mac_p, rlc_pcap& rlc_p, - metrics_log_helper& metrics_logger, e2_connection_client& e2_client_handler, e2_metric_connector_manager& e2_metric_connectors, - rlc_metrics_notifier& rlc_json_metrics, metrics_hub& metrics_hub) { // DU-high configuration. @@ -115,31 +137,14 @@ void srsran::fill_du_high_wrapper_config(du_high_wrapper_config& out_cfg, // Assign different initial C-RNTIs to different DUs. du_hi_cfg.mac_cfg.initial_crnti = to_rnti(0x4601 + (0x1000 * du_idx)); du_hi_cfg.sched_ue_metrics_notifier = metrics_hub.get_scheduler_ue_metrics_source("DU " + std::to_string(du_idx)); + du_hi_cfg.rlc_metrics_notif = metrics_hub.get_rlc_metrics_source("DU RLC " + std::to_string(du_idx)); du_hi_cfg.sched_cfg = generate_scheduler_expert_config(du_high_unit_cfg); - // Connect RLC metrics to sinks, if required - if (du_high_unit_cfg.metrics.rlc.json_enabled || du_high_unit_cfg.e2_cfg.enable_du_e2) { - // This source aggregates the RLC metrics from all DRBs in a single DU. - std::string source_name = "rlc_metric_aggr_du_" + std::to_string(du_idx); - auto rlc_source = std::make_unique(source_name); - - if (du_high_unit_cfg.metrics.rlc.json_enabled) { - // Connect JSON metrics plotter to RLC metric source. - rlc_source->add_subscriber(rlc_json_metrics); - } - if (metrics_logger.is_enabled()) { - rlc_source->add_subscriber(metrics_logger); - } - if (du_high_unit_cfg.e2_cfg.enable_du_e2) { - // Connect E2 agent to RLC metric source. - du_hi_cfg.e2_client = &e2_client_handler; - du_hi_cfg.e2ap_config = generate_e2_config(du_high_unit_cfg); - du_hi_cfg.e2_du_metric_iface = &(e2_metric_connectors.get_e2_du_metrics_interface(du_idx)); - rlc_source->add_subscriber(e2_metric_connectors.get_e2_du_metric_notifier(du_idx)); - } - // Pass RLC metric source to the DU high. - metrics_hub.add_source(std::move(rlc_source)); - du_hi_cfg.rlc_metrics_notif = metrics_hub.get_rlc_metrics_source(source_name); + if (du_high_unit_cfg.e2_cfg.enable_du_e2) { + // Connect E2 agent to RLC metric source. + du_hi_cfg.e2_client = &e2_client_handler; + du_hi_cfg.e2ap_config = generate_e2_config(du_high_unit_cfg); + du_hi_cfg.e2_du_metric_iface = &(e2_metric_connectors.get_e2_du_metrics_interface(du_idx)); } // Configure test mode diff --git a/apps/units/flexible_du/du_high/du_high_wrapper_config_helper.h b/apps/units/flexible_du/du_high/du_high_wrapper_config_helper.h index 05a895d70b..9d1c032cfe 100644 --- a/apps/units/flexible_du/du_high/du_high_wrapper_config_helper.h +++ b/apps/units/flexible_du/du_high/du_high_wrapper_config_helper.h @@ -47,8 +47,9 @@ struct du_high_wrapper_dependencies; /// Set up sources for the DU high metrics. void configure_du_high_metrics(const du_high_unit_config& du_high_unit_cfg, metrics_plotter_stdout& metrics_stdout, - metrics_plotter_json& metrics_json, metrics_log_helper& metrics_logger, + metrics_plotter_json& metrics_json, + rlc_metrics_notifier& rlc_metrics_json, e2_metric_connector_manager& e2_metric_connectors, metrics_hub& metrics_hub); @@ -62,10 +63,8 @@ void fill_du_high_wrapper_config(du_high_wrapper_config& out_cfg, timer_manager& timer_mng, mac_pcap& mac_p, rlc_pcap& rlc_p, - metrics_log_helper& metrics_logger, e2_connection_client& e2_client_handler, e2_metric_connector_manager& e2_metric_connectors, - rlc_metrics_notifier& rlc_json_metrics, metrics_hub& metrics_hub); } // namespace srsran diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp b/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp index 0ee13c9dad..b739b588d3 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp @@ -131,7 +131,8 @@ du_unit srsran::create_du(const dynamic_du_unit_config& dyn_du_cfg, const fapi_unit_config& fapi_cfg = dyn_du_cfg.fapi_cfg; // Configure the application unit metrics for the DU high. - configure_du_high_metrics(du_hi, metrics_stdout, metrics_json, metrics_logger, e2_metric_connectors, metrics_hub); + configure_du_high_metrics( + du_hi, metrics_stdout, metrics_logger, metrics_json, rlc_json_metrics, e2_metric_connectors, metrics_hub); auto du_cells = generate_du_cell_config(du_hi); @@ -171,10 +172,8 @@ du_unit srsran::create_du(const dynamic_du_unit_config& dyn_du_cfg, timer_mng, mac_p, rlc_p, - metrics_logger, e2_client_handler, e2_metric_connectors, - rlc_json_metrics, metrics_hub); // FAPI configuration. diff --git a/configs/cell_cfg_max_128_ues.yml b/configs/cell_cfg_max_128_ues.yml index 82ad4a90a3..9cd24c5b24 100644 --- a/configs/cell_cfg_max_128_ues.yml +++ b/configs/cell_cfg_max_128_ues.yml @@ -11,7 +11,7 @@ cell_cfg: pucch: sr_period_ms: 20 # This can be set either 20 or 40 ms. - f1_nof_cell_res_sr: 50 + f0_or_f1_nof_cell_res_sr: 50 f2_nof_cell_res_csi: 50 csi: csi_rs_period: 40 # This can be set either 20 or 40 ms. diff --git a/configs/cell_cfg_max_32_ues.yml b/configs/cell_cfg_max_32_ues.yml index 52459c0e08..62a808ef86 100644 --- a/configs/cell_cfg_max_32_ues.yml +++ b/configs/cell_cfg_max_32_ues.yml @@ -9,7 +9,7 @@ cell_cfg: pucch: sr_period_ms: 20 # This can be set either 20 or 40 ms. - f1_nof_cell_res_sr: 15 + f0_or_f1_nof_cell_res_sr: 15 f2_nof_cell_res_csi: 15 csi: csi_rs_period: 40 # This can be set either 20 or 40 ms. diff --git a/configs/cell_cfg_max_64_ues.yml b/configs/cell_cfg_max_64_ues.yml index 656ef3f665..8c9e472f15 100644 --- a/configs/cell_cfg_max_64_ues.yml +++ b/configs/cell_cfg_max_64_ues.yml @@ -9,7 +9,7 @@ cell_cfg: pucch: sr_period_ms: 20 # This can be set either 20 or 40 ms. - f1_nof_cell_res_sr: 31 + f0_or_f1_nof_cell_res_sr: 31 f2_nof_cell_res_csi: 31 csi: csi_rs_period: 40 # This can be set either 20 or 40 ms. diff --git a/docker/Dockerfile b/docker/Dockerfile index 1a21e366d1..4fc34a9755 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -56,20 +56,21 @@ ARG NUM_CORES # Compile UHD/DPDK RUN /src/docker/scripts/build_${LIB}.sh ${LIB_VERSION} ${ARCH} ${NUM_CORES} -# Compile srsRAN Project +# Compile srsRAN Project and install it in the OS ARG EXTRA_CMAKE_ARGS="" RUN if [ -z "$NUM_CORES" ]; then NUM_CORES=$(nproc); fi && \ LIB_UPPER=$(echo $LIB | tr '[:lower:]' '[:upper:]') && \ export ${LIB_UPPER}_DIR="/opt/${LIB}/${LIB_VERSION}" && \ /src/docker/scripts/builder.sh \ - -m -j${NUM_CORES} \ - -DBUILD_TESTS=False \ + -m "-j${NUM_CORES} install" \ + -DBUILD_TESTS=True \ -DENABLE_${LIB_UPPER}=On \ -DCMAKE_CXX_FLAGS="-march=${ARCH}" \ + -DCMAKE_INSTALL_PREFIX=/opt/srs \ ${EXTRA_CMAKE_ARGS} /src -# Install in the OS filesystem to later copy into the next stage -RUN cd /src/build && make install +# Copy extra binaries +RUN cp /src/build/tests/integrationtests/ofh/ru_emulator /usr/local/bin/ru_emulator ################ # Stage 2: Run # @@ -81,12 +82,14 @@ ARG LIB ARG LIB_VERSION # Copy srsRAN binaries and libraries installed in previous stage -COPY --from=builder /opt/${LIB}/${LIB_VERSION} /usr/local -COPY --from=builder /usr/local /usr/local +COPY --from=builder /opt/${LIB}/${LIB_VERSION} /opt/${LIB}/${LIB_VERSION} +COPY --from=builder /opt/srs /usr/local # Copy the install dependencies scripts ADD docker/scripts/install_${LIB}_dependencies.sh /usr/local/etc/install_lib_dependencies.sh ADD docker/scripts/install_dependencies.sh /usr/local/etc/install_srsran_dependencies.sh +ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/${LIB}/${LIB_VERSION}/lib/:/opt/${LIB}/${LIB_VERSION}/lib/x86_64-linux-gnu/:/opt/${LIB}/${LIB_VERSION}/lib/aarch64-linux-gnu/ +ENV PATH=$PATH:/opt/${LIB}/${LIB_VERSION}/bin/ # Install srsran and lib runtime dependencies RUN /usr/local/etc/install_srsran_dependencies.sh run && \ diff --git a/docker/scripts/install_dpdk_dependencies.sh b/docker/scripts/install_dpdk_dependencies.sh index a5ad6732c0..4ceab0d0e9 100755 --- a/docker/scripts/install_dpdk_dependencies.sh +++ b/docker/scripts/install_dpdk_dependencies.sh @@ -45,7 +45,7 @@ main() { fi if [[ "$mode" == "all" || "$mode" == "run" ]]; then DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends \ - python3-pip libnuma-dev pciutils libfdt-dev + python3-pip libnuma-dev pciutils libfdt-dev libatomic1 pip3 install pyelftools || pip3 install --break-system-packages pyelftools fi else diff --git a/docker/scripts/install_uhd_dependencies.sh b/docker/scripts/install_uhd_dependencies.sh index e6dfbc99f5..fd97c695a7 100755 --- a/docker/scripts/install_uhd_dependencies.sh +++ b/docker/scripts/install_uhd_dependencies.sh @@ -50,7 +50,7 @@ main() { cpufrequtils inetutils-tools libboost-all-dev libncurses5-dev libusb-1.0-0 libusb-1.0-0-dev \ libusb-dev python3-dev python3-requests && apt-get autoremove && apt-get clean && rm -rf /var/lib/apt/lists/* - /usr/local/bin/uhd_images_downloader + uhd_images_downloader fi else echo "OS $ID not supported" diff --git a/include/srsran/cu_cp/cu_cp_types.h b/include/srsran/cu_cp/cu_cp_types.h index 69b14f0773..a0cb2e2aea 100644 --- a/include/srsran/cu_cp/cu_cp_types.h +++ b/include/srsran/cu_cp/cu_cp_types.h @@ -207,13 +207,13 @@ struct cu_cp_nr_mode_info { }; struct cu_cp_served_cell_info { - nr_cell_global_id_t nr_cgi; - pci_t nr_pci; - std::optional five_gs_tac; - std::optional cfg_eps_tac; - std::vector served_plmns; - cu_cp_nr_mode_info nr_mode_info; - byte_buffer meas_timing_cfg; + nr_cell_global_id_t nr_cgi; + pci_t nr_pci; + std::optional five_gs_tac; + std::optional cfg_eps_tac; + std::vector served_plmns; + cu_cp_nr_mode_info nr_mode_info; + byte_buffer meas_timing_cfg; cu_cp_served_cell_info() = default; cu_cp_served_cell_info(const cu_cp_served_cell_info& other) : @@ -309,6 +309,7 @@ struct cu_cp_pdu_session_resource_setup_request { slotted_id_vector pdu_session_res_setup_items; uint64_t ue_aggregate_maximum_bit_rate_dl; plmn_identity serving_plmn = plmn_identity::test_value(); + byte_buffer nas_pdu; ///< optional NAS PDU }; enum class cu_cp_qos_flow_map_ind { ul = 0, dl }; @@ -585,14 +586,13 @@ namespace fmt { template <> struct formatter { template - auto parse(ParseContext& ctx) -> decltype(ctx.begin()) + auto parse(ParseContext& ctx) { return ctx.begin(); } template auto format(const srsran::srs_cu_cp::ue_index_t& idx, FormatContext& ctx) - -> decltype(std::declval().out()) { if (idx == srsran::srs_cu_cp::ue_index_t::invalid) { return format_to(ctx.out(), "invalid"); @@ -605,14 +605,13 @@ struct formatter { template <> struct formatter { template - auto parse(ParseContext& ctx) -> decltype(ctx.begin()) + auto parse(ParseContext& ctx) { return ctx.begin(); } template auto format(const srsran::srs_cu_cp::du_index_t& idx, FormatContext& ctx) - -> decltype(std::declval().out()) { if (idx == srsran::srs_cu_cp::du_index_t::invalid) { return format_to(ctx.out(), "invalid"); @@ -625,14 +624,13 @@ struct formatter { template <> struct formatter { template - auto parse(ParseContext& ctx) -> decltype(ctx.begin()) + auto parse(ParseContext& ctx) { return ctx.begin(); } template auto format(const srsran::srs_cu_cp::cu_up_index_t& idx, FormatContext& ctx) - -> decltype(std::declval().out()) { if (idx == srsran::srs_cu_cp::cu_up_index_t::invalid) { return format_to(ctx.out(), "invalid"); @@ -645,14 +643,13 @@ struct formatter { template <> struct formatter { template - auto parse(ParseContext& ctx) -> decltype(ctx.begin()) + auto parse(ParseContext& ctx) { return ctx.begin(); } template auto format(const srsran::srs_cu_cp::du_cell_index_t& idx, FormatContext& ctx) - -> decltype(std::declval().out()) { if (idx == srsran::srs_cu_cp::du_cell_index_t::invalid) { return format_to(ctx.out(), "invalid"); diff --git a/include/srsran/cu_cp/cu_cp_ue_messages.h b/include/srsran/cu_cp/cu_cp_ue_messages.h index f80b35fdbd..8326659b5b 100644 --- a/include/srsran/cu_cp/cu_cp_ue_messages.h +++ b/include/srsran/cu_cp/cu_cp_ue_messages.h @@ -40,6 +40,7 @@ struct rrc_ue_transfer_context { up_context up_ctx; static_vector srbs; // List of active SRBs (TODO: add PDCP config). byte_buffer handover_preparation_info; + byte_buffer ue_cap_rat_container_list; bool is_inter_cu_handover = false; }; diff --git a/include/srsran/du/du_cell_config.h b/include/srsran/du/du_cell_config.h index 8a5be21e15..219f29e104 100644 --- a/include/srsran/du/du_cell_config.h +++ b/include/srsran/du/du_cell_config.h @@ -47,6 +47,13 @@ inline unsigned format1_cp_step_to_uint(nof_cyclic_shifts opt) return static_cast(opt); } +/// Collects the parameters for PUCCH Format 0 that can be configured. +struct pucch_f0_params { + /// Indicates whether OCCs (as per \c PUCCH-format0, in \c PUCCH-Config, TS 38.331) are supported. + bounded_integer nof_symbols{2}; + bool intraslot_freq_hopping{false}; +}; + /// Collects the parameters for PUCCH Format 1 that can be configured. struct pucch_f1_params { /// Number of possible Initial Cyclic Shifts, equally spaced within the range {0,...,11}, as per \c PUCCH-format1, in @@ -76,8 +83,9 @@ struct pucch_f2_params { struct pucch_builder_params { /// UE specific parameters. Use to set the number of resources per UE for HARQ-ACK reporting (not including SR/CSI /// dedicated resources). NOTE: by default, each UE is assigned 1 SR and 1 CSI resource. - bounded_integer nof_ue_pucch_f1_res_harq = 3; - bounded_integer nof_ue_pucch_f2_res_harq = 6; + /// \remark Format 0 and Format 1 resources are mutually exclusive. + bounded_integer nof_ue_pucch_f0_or_f1_res_harq = 3; + bounded_integer nof_ue_pucch_f2_res_harq = 6; /// \brief Number of separate PUCCH resource sets for HARQ-ACK reporting that are available in a cell. /// \remark UEs will be distributed possibly over different HARQ-ACK PUCCH sets; the more sets, the fewer UEs will /// have to share the same set, which reduces the chances that UEs won't be allocated PUCCH due to lack of resources. @@ -91,8 +99,9 @@ struct pucch_builder_params { unsigned nof_csi_resources = 1; /// PUCCH Format specific parameters. - pucch_f1_params f1_params; - pucch_f2_params f2_params; + // NOTE: Having \c pucch_f1_params force the varint to use the Format 1 in the default constructor. + std::variant f0_or_f1_params; + pucch_f2_params f2_params; }; /// Parameters that are used to initialize or build the \c PhysicalCellGroupConfig, TS 38.331. diff --git a/include/srsran/ngap/ngap.h b/include/srsran/ngap/ngap.h index 6157029922..d1274b28cc 100644 --- a/include/srsran/ngap/ngap.h +++ b/include/srsran/ngap/ngap.h @@ -27,6 +27,7 @@ #include "srsran/ngap/ngap_init_context_setup.h" #include "srsran/ngap/ngap_reset.h" #include "srsran/ngap/ngap_setup.h" +#include "srsran/ngap/ngap_ue_radio_capability_management.h" #include "srsran/support/async/async_task.h" namespace srsran { @@ -115,6 +116,9 @@ class ngap_rrc_ue_control_notifier public: virtual ~ngap_rrc_ue_control_notifier() = default; + /// \brief Get packed packed UE radio access capability info for UE radio capability info indication. + virtual byte_buffer on_ue_radio_access_cap_info_required() = 0; + /// \brief Get packed handover preparation message for inter-gNB handover. virtual byte_buffer on_handover_preparation_message_required() = 0; }; @@ -242,6 +246,18 @@ class ngap_nas_message_handler virtual void handle_ul_nas_transport_message(const cu_cp_ul_nas_transport& msg) = 0; }; +/// Handle NGAP UE Radio Capability Management Messages as per TS 38.413 section 8.14. +class ngap_ue_radio_capability_management_handler +{ +public: + virtual ~ngap_ue_radio_capability_management_handler() = default; + + /// \brief Initiates the UE Radio Capability Info Indication procedure as per TS 38.413 section 8.14.1. + /// \param[in] msg The ue radio capability info indication to transmit. + virtual void + handle_tx_ue_radio_capability_info_indication_required(const ngap_ue_radio_capability_info_indication& msg) = 0; +}; + class ngap_control_message_handler { public: @@ -294,6 +310,7 @@ class ngap_interface : public ngap_message_handler, public ngap_event_handler, public ngap_connection_manager, public ngap_nas_message_handler, + public ngap_ue_radio_capability_management_handler, public ngap_control_message_handler, public ngap_ue_control_manager, public ngap_statistics_handler, @@ -302,14 +319,15 @@ class ngap_interface : public ngap_message_handler, public: virtual ~ngap_interface() = default; - virtual ngap_message_handler& get_ngap_message_handler() = 0; - virtual ngap_event_handler& get_ngap_event_handler() = 0; - virtual ngap_connection_manager& get_ngap_connection_manager() = 0; - virtual ngap_nas_message_handler& get_ngap_nas_message_handler() = 0; - virtual ngap_control_message_handler& get_ngap_control_message_handler() = 0; - virtual ngap_ue_control_manager& get_ngap_ue_control_manager() = 0; - virtual ngap_statistics_handler& get_ngap_statistics_handler() = 0; - virtual ngap_ue_context_removal_handler& get_ngap_ue_context_removal_handler() = 0; + virtual ngap_message_handler& get_ngap_message_handler() = 0; + virtual ngap_event_handler& get_ngap_event_handler() = 0; + virtual ngap_connection_manager& get_ngap_connection_manager() = 0; + virtual ngap_nas_message_handler& get_ngap_nas_message_handler() = 0; + virtual ngap_ue_radio_capability_management_handler& get_ngap_ue_radio_cap_management_handler() = 0; + virtual ngap_control_message_handler& get_ngap_control_message_handler() = 0; + virtual ngap_ue_control_manager& get_ngap_ue_control_manager() = 0; + virtual ngap_statistics_handler& get_ngap_statistics_handler() = 0; + virtual ngap_ue_context_removal_handler& get_ngap_ue_context_removal_handler() = 0; }; } // namespace srs_cu_cp diff --git a/lib/ngap/procedures/ngap_procedure_helpers.h b/include/srsran/ngap/ngap_nas.h similarity index 74% rename from lib/ngap/procedures/ngap_procedure_helpers.h rename to include/srsran/ngap/ngap_nas.h index 9c7518840b..b14414a4d4 100644 --- a/lib/ngap/procedures/ngap_procedure_helpers.h +++ b/include/srsran/ngap/ngap_nas.h @@ -22,17 +22,16 @@ #pragma once -#include "../ue_context/ngap_ue_logger.h" -#include "srsran/ngap/ngap.h" +#include "srsran/cu_cp/cu_cp_types.h" namespace srsran { namespace srs_cu_cp { -inline void handle_nas_pdu(ngap_ue_logger& logger, byte_buffer nas_pdu, ngap_rrc_ue_pdu_notifier& rrc_ue_pdu_notifier) -{ - logger.log_debug("Forwarding NAS PDU to RRC"); - rrc_ue_pdu_notifier.on_new_pdu(std::move(nas_pdu)); -} +struct ngap_dl_nas_transport_message { + ue_index_t ue_index = ue_index_t::invalid; + byte_buffer nas_pdu; + bool ue_cap_info_request = false; +}; } // namespace srs_cu_cp } // namespace srsran diff --git a/include/srsran/ngap/ngap_ue_radio_capability_management.h b/include/srsran/ngap/ngap_ue_radio_capability_management.h new file mode 100644 index 0000000000..0f29c244ce --- /dev/null +++ b/include/srsran/ngap/ngap_ue_radio_capability_management.h @@ -0,0 +1,36 @@ +/* + * + * 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/cu_cp/cu_cp_types.h" + +namespace srsran { +namespace srs_cu_cp { + +struct ngap_ue_radio_capability_info_indication { + ue_index_t ue_index = ue_index_t::invalid; + byte_buffer ue_cap_rat_container_list; +}; + +} // namespace srs_cu_cp +} // namespace srsran diff --git a/include/srsran/phy/generic_functions/precoding/channel_precoder.h b/include/srsran/phy/generic_functions/precoding/channel_precoder.h index 12399457cd..31c88fa36a 100644 --- a/include/srsran/phy/generic_functions/precoding/channel_precoder.h +++ b/include/srsran/phy/generic_functions/precoding/channel_precoder.h @@ -46,8 +46,8 @@ class channel_precoder /// of RE per layer of the input buffer. /// \remark An assertion is triggered if the precoding matrix dimensions do not match the number of layers of the /// input buffer and the number of antenna ports of the output buffer. - virtual void apply_precoding(re_buffer_writer& output, - const re_buffer_reader& input, + virtual void apply_precoding(re_buffer_writer<>& output, + const re_buffer_reader<>& input, const precoding_weight_matrix& precoding) const = 0; /// \brief Maps the input symbols into layers and applies a set of precoding weights. @@ -58,7 +58,7 @@ class channel_precoder /// of RE per layer of the input buffer. /// \remark An assertion is triggered if the precoding matrix dimensions are not consistent with input buffer size and /// the number of antenna ports of the output buffer. - virtual void apply_layer_map_and_precoding(re_buffer_writer& output, + virtual void apply_layer_map_and_precoding(re_buffer_writer<>& output, span input, const precoding_weight_matrix& precoding) const = 0; }; diff --git a/include/srsran/phy/support/re_buffer.h b/include/srsran/phy/support/re_buffer.h index 342d3f0a7c..458c12ea96 100644 --- a/include/srsran/phy/support/re_buffer.h +++ b/include/srsran/phy/support/re_buffer.h @@ -50,7 +50,9 @@ class re_buffer_dimensions } // namespace detail -/// Resource Element buffer read-only interface. +/// \brief Resource Element buffer read-only interface. +/// \tparam T Resource element type. +template class re_buffer_reader : public detail::re_buffer_dimensions { public: @@ -58,10 +60,12 @@ class re_buffer_reader : public detail::re_buffer_dimensions /// \param[in] i_slice Slice identifier. /// \remark An assertion is triggered if the slice identifier is greater than or equal to get_nof_slices(). /// \return A read-only view of the specified slice. - virtual span get_slice(unsigned i_slice) const = 0; + virtual span get_slice(unsigned i_slice) const = 0; }; -/// Resource Element buffer read-write interface. +/// \brief Resource Element buffer read-write interface. +/// \tparam T Resource element type. +template class re_buffer_writer : public detail::re_buffer_dimensions { public: @@ -69,11 +73,13 @@ class re_buffer_writer : public detail::re_buffer_dimensions /// \param[in] i_slice Slice identifier. /// \remark An assertion is triggered if the slice identifier is greater than or equal to get_nof_slices(). /// \return A read-write view of the specified slice. - virtual span get_slice(unsigned i_slice) = 0; + virtual span get_slice(unsigned i_slice) = 0; }; -/// Implements a resource element buffer reader view, with read-only access methods. -class re_buffer_reader_view : public re_buffer_reader +/// \brief Implements a resource element buffer reader view, with read-only access methods. +/// \tparam T Resource element type. +template +class re_buffer_reader_view : public re_buffer_reader { public: /// \brief Constructs a resource element buffer view that gives access to a subspan of the REs for all slices. @@ -81,7 +87,7 @@ class re_buffer_reader_view : public re_buffer_reader /// \param[in] buffer RE buffer accessed by the view. /// \param[in] offset Offset in number of RE. It determines the start of the RE view across each slice. /// \param[in] count Count in number of RE. It determines the number of RE spanned by the view across each slice. - re_buffer_reader_view(const re_buffer_reader& buffer, unsigned offset, unsigned count) : + re_buffer_reader_view(const re_buffer_reader& buffer, unsigned offset, unsigned count) : view_offset(offset), view_count(count), data(buffer) { srsran_assert((buffer.get_nof_re() >= offset + count), @@ -104,7 +110,7 @@ class re_buffer_reader_view : public re_buffer_reader /// /// \param[in] i_slice Slice identifier. /// \return A read-only view of the specified slice. - span get_slice(unsigned i_slice) const override + span get_slice(unsigned i_slice) const override { return data.get_slice(i_slice).subspan(view_offset, view_count); } @@ -116,11 +122,13 @@ class re_buffer_reader_view : public re_buffer_reader unsigned view_count; /// Internal data storage. - const re_buffer_reader& data; + const re_buffer_reader& data; }; -/// Implements a resource element buffer writer view, with read-write access methods. -class re_buffer_writer_view : public re_buffer_writer +/// \brief Implements a resource element buffer writer view, with read-write access methods. +/// \tparam T Resource element type. +template +class re_buffer_writer_view : public re_buffer_writer { public: /// \brief Constructs a resource element buffer view that gives access to a subspan of the REs for all slices. @@ -128,7 +136,7 @@ class re_buffer_writer_view : public re_buffer_writer /// \param[in] buffer RE buffer accessed by the view. /// \param[in] offset Offset in number of RE. It determines the start of the RE view across each slice. /// \param[in] count Count in number of RE. It determines the number of RE spanned by the view across each slice. - re_buffer_writer_view(re_buffer_writer& buffer, unsigned offset, unsigned count) : + re_buffer_writer_view(re_buffer_writer& buffer, unsigned offset, unsigned count) : view_offset(offset), view_count(count), data(buffer) { srsran_assert((buffer.get_nof_re() >= offset + count), @@ -145,7 +153,7 @@ class re_buffer_writer_view : public re_buffer_writer /// /// \param[in] i_slice Slice identifier. /// \return A read-write view of the specified slice. - span get_slice(unsigned i_slice) override { return data.get_slice(i_slice).subspan(view_offset, view_count); } + span get_slice(unsigned i_slice) override { return data.get_slice(i_slice).subspan(view_offset, view_count); } // See interface for documentation. unsigned get_nof_slices() const override { return data.get_nof_slices(); } @@ -160,11 +168,13 @@ class re_buffer_writer_view : public re_buffer_writer unsigned view_count; /// Internal data storage. - re_buffer_writer& data; + re_buffer_writer& data; }; -/// Implements a dynamic resource element buffer. -class dynamic_re_buffer : public re_buffer_reader, public re_buffer_writer +/// \brief Implements a dynamic resource element buffer. +/// \tparam T Resource element type. +template +class dynamic_re_buffer : public re_buffer_reader, public re_buffer_writer { public: /// \brief Constructs a resource element buffer for a given maximum number of slices and resource elements. @@ -200,10 +210,10 @@ class dynamic_re_buffer : public re_buffer_reader, public re_buffer_writer unsigned get_nof_re() const override { return data.get_dimension_size(dims::re); } // See interface for documentation. - span get_slice(unsigned i_slice) override { return data.get_view<1>({i_slice}); } + span get_slice(unsigned i_slice) override { return data.get_view({i_slice}); } // See interface for documentation. - span get_slice(unsigned i_slice) const override { return data.get_view<1>({i_slice}); } + span get_slice(unsigned i_slice) const override { return data.get_view({i_slice}); } private: /// Internal tensor dimensions. @@ -215,12 +225,15 @@ class dynamic_re_buffer : public re_buffer_reader, public re_buffer_writer unsigned max_nof_re; /// Internal data storage. - dynamic_tensor(dims::all), cf_t, dims> data; + dynamic_tensor(dims::all), T, dims> data; }; -/// Implements a static resource element buffer. -template -class static_re_buffer : public re_buffer_reader, public re_buffer_writer +/// \brief Implements a static resource element buffer. +/// \tparam MaxNofSlices Maximum number of slices. +/// \tparam MaxNofRe Maximum number of resource elements. +/// \tparam T Resource element type. +template +class static_re_buffer : public re_buffer_reader, public re_buffer_writer { public: /// Default constructor - creates an empty resource element buffer. @@ -258,17 +271,89 @@ class static_re_buffer : public re_buffer_reader, public re_buffer_writer unsigned get_nof_re() const override { return data.get_dimension_size(dims::re); } // See interface for documentation. - span get_slice(unsigned i_slice) override { return data.get_view({i_slice}); } + span get_slice(unsigned i_slice) override { return data.get_view({i_slice}); } // See interface for documentation. - span get_slice(unsigned i_slice) const override { return data.get_view({i_slice}); } + span get_slice(unsigned i_slice) const override { return data.get_view({i_slice}); } private: /// Internal tensor dimensions. enum class dims : unsigned { re = 0, slice = 1, all = 2 }; /// Internal data storage. - static_tensor(dims::all), cf_t, MaxNofSlices * MaxNofRe, dims> data; + static_tensor(dims::all), T, MaxNofSlices * MaxNofRe, dims> data; +}; + +/// \brief Implements a modular resource element buffer reader. +/// +/// In this implementation, each slice is a view to an external block of contiguous REs that must be loaded with the +/// \ref set_slice method. +/// +/// \tparam T Resource element type. +template +class modular_re_buffer_reader : public re_buffer_reader +{ +public: + /// \brief Constructs a modular resource element buffer for a given maximum number of slices and resource elements. + /// \param[in] max_nof_slices Maximum number of slices. + modular_re_buffer_reader(unsigned max_nof_slices) : nof_slices(0), nof_re(0), data(max_nof_slices) {} + + /// \brief Resizes the buffer view to a desired number of RE and slices. + /// \param[in] nof_slices Number of slices. + /// \param[in] nof_re Number of resource elements. + /// \remark An assertion is triggered if the number of slices exceeds \ref max_nof_slices. + /// \remark An assertion is triggered if the number of resource elements exceeds \ref max_nof_re. + void resize(unsigned nof_slices_, unsigned nof_re_) + { + nof_slices = nof_slices_; + nof_re = nof_re_; + srsran_assert(nof_slices <= data.size(), + "The number of slices (i.e., {}) exceeds the maximum (i.e., {}).", + nof_slices, + data.size()); + + // Empty all slices. + std::fill_n(data.begin(), nof_slices, span()); + } + + /// \brief Sets the view for a given slice. + /// \param[in] i_slice Slice identifier. + /// \param[in] view Slice view. + /// \remark An assertion is triggered if the view size is not equal to the number of resource elements. + void set_slice(unsigned i_slice, span view) + { + srsran_assert(view.size() == nof_re, + "The view size (i.e., {}) must be equal to the number of resource elements (i.e., {}).", + view.size(), + nof_re); + data[i_slice] = view; + } + + // See interface for documentation. + unsigned get_nof_slices() const override { return nof_slices; } + + // See interface for documentation. + unsigned get_nof_re() const override { return nof_re; } + + // See interface for documentation. + span get_slice(unsigned i_slice) const override + { + srsran_assert(i_slice < nof_slices, + "The slice index (i.e., {}) exceeds the number of slices (i.e., {}).", + i_slice, + nof_slices); + srsran_assert(!data[i_slice].empty(), "Data for slice {} is empty.", i_slice); + return data[i_slice]; + } + +private: + /// Current number of slices. + unsigned nof_slices; + /// Current number of resource elements. + unsigned nof_re; + + /// Internal data storage. + std::vector> data; }; } // namespace srsran diff --git a/include/srsran/phy/support/resource_grid_mapper.h b/include/srsran/phy/support/resource_grid_mapper.h index 15b349d6a6..9f65e550d1 100644 --- a/include/srsran/phy/support/resource_grid_mapper.h +++ b/include/srsran/phy/support/resource_grid_mapper.h @@ -23,12 +23,12 @@ #pragma once #include "srsran/adt/complex.h" #include "srsran/adt/span.h" +#include "srsran/phy/support/re_buffer.h" #include "srsran/support/srsran_assert.h" namespace srsran { struct re_pattern; -class re_buffer_reader; class re_pattern_list; class precoding_configuration; @@ -101,7 +101,7 @@ class resource_grid_mapper /// \param[in] pattern Data allocation pattern in the resource grid. /// \param[in] precoding Precoding configuration. virtual void - map(const re_buffer_reader& input, const re_pattern& pattern, const precoding_configuration& precoding) = 0; + map(const re_buffer_reader& input, const re_pattern& pattern, const precoding_configuration& precoding) = 0; /// \brief Maps complex symbols onto the resource grid. /// \param[in] buffer Buffer containing the complex symbols to map. diff --git a/include/srsran/phy/upper/equalization/channel_equalizer.h b/include/srsran/phy/upper/equalization/channel_equalizer.h index c4488c44fd..dcbae11a93 100644 --- a/include/srsran/phy/upper/equalization/channel_equalizer.h +++ b/include/srsran/phy/upper/equalization/channel_equalizer.h @@ -25,8 +25,9 @@ #pragma once -#include "srsran/phy/upper/channel_estimation.h" -#include "srsran/phy/upper/re_measurement.h" +#include "srsran/adt/complex.h" +#include "srsran/adt/span.h" +#include "srsran/phy/support/re_buffer.h" namespace srsran { @@ -36,37 +37,26 @@ namespace srsran { /// by TS38.211 Section 6.3.1.3. class channel_equalizer { -private: - /// Dimensions, i.e., number of coordinates, spanned by each indexing level of the Resource Element data. - enum class re_dims : unsigned { - /// Resource Element. - re = 0, - /// Set of all REs corresponding to a single receive port or a single transmit layer. - slice = 1, - /// Total number of dimensions. - nof_dims = 2 - }; +public: + /// \brief Interface for the list of channel estimates. + class ch_est_list + { + public: + /// Default destructor. + virtual ~ch_est_list() = default; - /// Dimensions, i.e. number of coordinates, spanned by each indexing level of the channel estimation data. - enum class ch_dims : unsigned { - /// Channel coefficient for a single Resource Element and a single Tx–Rx channel path. - re = 0, - /// Set of all channel coefficients corresponding to a single Tx–Rx channel path. - rx_port = 1, - /// Set of all channel coefficients corresponding to all Tx–Rx paths for a single Tx layer. - tx_layer = 2, - /// Total number of dimensions. - nof_dims = 3 - }; + /// Gets a read-only channel given a receive port and layer indices. + virtual span get_channel(unsigned i_rx_port, unsigned i_layer) const = 0; -public: - /// \brief Container for input and output Resource Elements. - /// \remark Dimension indexing given by \ref channel_equalizer::re_dims. - using re_list = tensor(re_dims::nof_dims), cbf16_t, re_dims>; + /// Gets the number of resource elements. + virtual unsigned get_nof_re() const = 0; + + /// Gets the number of receive ports. + virtual unsigned get_nof_rx_ports() const = 0; - /// \brief Container for the channel estimates. - /// \remark Dimension indexing given by \ref channel_equalizer::ch_dims. - using ch_est_list = tensor(ch_dims::nof_dims), cbf16_t, ch_dims>; + /// Gets the number of transmit layers. + virtual unsigned get_nof_tx_layers() const = 0; + }; /// Default destructor. virtual ~channel_equalizer() = default; @@ -93,12 +83,12 @@ class channel_equalizer /// \warning If the \c noise_var_estimates noise variances have ill-formed values (zero, negative, infinity or NaN), /// the corresponding equalized modulation REs and equalized noise variances will be set to zero and infinity, /// respectively. - virtual void equalize(span eq_symbols, - span eq_noise_vars, - const re_list& ch_symbols, - const ch_est_list& ch_estimates, - span noise_var_estimates, - float tx_scaling) = 0; + virtual void equalize(span eq_symbols, + span eq_noise_vars, + const re_buffer_reader& ch_symbols, + const ch_est_list& ch_estimates, + span noise_var_estimates, + float tx_scaling) = 0; }; } // namespace srsran diff --git a/include/srsran/phy/upper/equalization/dynamic_ch_est_list.h b/include/srsran/phy/upper/equalization/dynamic_ch_est_list.h new file mode 100644 index 0000000000..e2fc799f57 --- /dev/null +++ b/include/srsran/phy/upper/equalization/dynamic_ch_est_list.h @@ -0,0 +1,85 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "srsran/adt/tensor.h" +#include "srsran/phy/upper/equalization/channel_equalizer.h" + +namespace srsran { + +/// Channel estimates list with based in a tensor storage. +class dynamic_ch_est_list : public channel_equalizer::ch_est_list +{ +public: + dynamic_ch_est_list() = default; + + dynamic_ch_est_list(unsigned nof_re, unsigned nof_rx_ports, unsigned nof_layers) : + data({nof_re, nof_rx_ports, nof_layers}) + { + } + + /// Gets a read-write channel given a receive port and layer indices. + span get_channel(unsigned i_rx_port, unsigned i_layer) { return data.get_view({i_rx_port, i_layer}); } + + // See interface for documentation. + span get_channel(unsigned i_rx_port, unsigned i_layer) const override + { + return data.get_view({i_rx_port, i_layer}); + } + + // See interface for documentation. + unsigned get_nof_re() const override { return data.get_dimension_size(ch_dims::re); } + + // See interface for documentation. + unsigned get_nof_rx_ports() const override { return data.get_dimension_size(ch_dims::rx_port); } + + // See interface for documentation. + unsigned get_nof_tx_layers() const override { return data.get_dimension_size(ch_dims::tx_layer); } + + /// Resize the channel estimates list. + void resize(unsigned nof_re, unsigned nof_rx_ports, unsigned nof_layers) + { + data.resize({nof_re, nof_rx_ports, nof_layers}); + } + + /// Gets a raw data view. + span get_data() { return data.get_data(); } + +private: + /// Dimensions, i.e. number of coordinates, spanned by each indexing level of the channel estimation data. + enum class ch_dims : unsigned { + /// Channel coefficient for a single Resource Element and a single Tx–Rx channel path. + re = 0, + /// Set of all channel coefficients corresponding to a single Tx–Rx channel path. + rx_port = 1, + /// Set of all channel coefficients corresponding to all Tx–Rx paths for a single Tx layer. + tx_layer = 2, + /// Total number of dimensions. + nof_dims = 3 + }; + + // Data storage as a tensor. + dynamic_tensor(ch_dims::nof_dims), cbf16_t, ch_dims> data; +}; + +} // namespace srsran \ No newline at end of file diff --git a/include/srsran/phy/upper/equalization/modular_ch_est_list.h b/include/srsran/phy/upper/equalization/modular_ch_est_list.h new file mode 100644 index 0000000000..26212487e1 --- /dev/null +++ b/include/srsran/phy/upper/equalization/modular_ch_est_list.h @@ -0,0 +1,115 @@ +/* + * + * 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/tensor.h" +#include "srsran/phy/upper/equalization/channel_equalizer.h" + +namespace srsran { + +/// Implements the list of channel estimates with views to each of the channels. +class modular_ch_est_list : public channel_equalizer::ch_est_list +{ +public: + /// \brief Creates a list of channel estimates from a maximum number of receive ports and layers. + /// \param[in] max_nof_rx_ports_ Maximum number of receive ports. + /// \param[in] max_nof_layers_ Maximum number of layers. + modular_ch_est_list(unsigned max_nof_rx_ports_, unsigned max_nof_layers_) : + max_nof_rx_ports(max_nof_rx_ports_), max_nof_layers(max_nof_layers_), data({max_nof_rx_ports, max_nof_layers}) + { + } + + /// \brief Sets the contents of a channel. + /// + /// \param[in] buffer Channel view for the given port and layer. + /// \param[in] i_rx_port Receive port index. + /// \param[in] i_layer Transmit layer index. + void set_channel(span buffer, unsigned i_rx_port, unsigned i_layer) + { + srsran_assert(buffer.size() == nof_re, + "Buffer size (i.e., {}) is not equal to the number of configured RE (i.e., {}).", + buffer.size(), + nof_re); + data[{i_rx_port, i_layer}] = buffer; + } + + /// \brief Resizes the channel estimates list. + /// + /// \remark An assertion is triggered if the number of receive ports exceeds the maximum number of receive ports. + /// \remark An assertion is triggered if the number of layers exceeds the maximum number of layers. + void resize(unsigned nof_re_, unsigned nof_rx_ports, unsigned nof_layers) + { + srsran_assert(nof_rx_ports <= max_nof_rx_ports, + "The number of receive ports (i.e., {}) exceeds the maximum number of receive ports (i.e., {}).", + nof_rx_ports, + max_nof_rx_ports); + srsran_assert(nof_layers <= max_nof_layers, + "The number of layers (i.e., {}) exceeds the maximum number of layers (i.e., {}).", + nof_rx_ports, + max_nof_layers); + nof_re = nof_re_; + data.resize({nof_rx_ports, nof_layers}); + + // Reset buffer views. + for (span& view : data.get_data()) { + view = {}; + } + } + + // See interface for documentation. + span get_channel(unsigned i_rx_port, unsigned i_layer) const override + { + return data[{i_rx_port, i_layer}]; + } + + // See interface for documentation. + unsigned get_nof_re() const override { return nof_re; } + + // See interface for documentation. + unsigned get_nof_rx_ports() const override { return data.get_dimension_size(ch_dims::rx_port); } + + // See interface for documentation. + unsigned get_nof_tx_layers() const override { return data.get_dimension_size(ch_dims::tx_layer); } + +private: + /// Dimensions, i.e. number of coordinates, spanned by each indexing level of the channel estimation data. + enum class ch_dims : unsigned { + /// Set of all channel coefficients corresponding to a single Tx–Rx channel path. + rx_port = 0, + /// Set of all channel coefficients corresponding to all Tx–Rx paths for a single Tx layer. + tx_layer = 1, + /// Total number of dimensions. + nof_dims = 2 + }; + + /// Number of resource elements. + unsigned nof_re = 0; + /// Maximum number of receive ports. + unsigned max_nof_rx_ports; + /// Maximum number of layers. + unsigned max_nof_layers; + /// Data storage as a tensor of views for each channel. + dynamic_tensor(ch_dims::nof_dims), span, ch_dims> data; +}; + +} // namespace srsran \ No newline at end of file diff --git a/include/srsran/phy/upper/log_likelihood_ratio.h b/include/srsran/phy/upper/log_likelihood_ratio.h index 1b9335f72c..67ff9bafd6 100644 --- a/include/srsran/phy/upper/log_likelihood_ratio.h +++ b/include/srsran/phy/upper/log_likelihood_ratio.h @@ -303,6 +303,24 @@ V log_likelihood_ratio::dot_prod(const T& x, const U& y, V init) }); } +/// \brief Clamps the input values between a lower and higher bound. +/// +/// The equivalent code is: +/// \code +/// std::transform(in.begin(), in.end(), out.begin(), [low, high](auto llr){ +/// return std::clamp(llr, low, high); +/// }); +/// \endcode +/// +/// \param[out] out Resultant values. +/// \param[in] in Input values to clamp. +/// \param[in] low Lower bound to clamp to. +/// \param[in] high Higher bound to clamp to. +void clamp(span out, + span in, + log_likelihood_ratio low, + log_likelihood_ratio high); + /// \brief Squared Euclidean norm of a sequence of LLRs. /// /// Computes the squared Euclidean norm (sum of the squares of the elements) of the input sequence of LLRs. diff --git a/include/srsran/ran/nr_cell_identity.h b/include/srsran/ran/nr_cell_identity.h index 1181c3ee37..1c91c87dc3 100644 --- a/include/srsran/ran/nr_cell_identity.h +++ b/include/srsran/ran/nr_cell_identity.h @@ -33,7 +33,7 @@ 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. +/// Sector ID. class nr_cell_identity { constexpr nr_cell_identity(uint64_t val_) : val(val_) {} @@ -52,17 +52,17 @@ class nr_cell_identity return nr_cell_identity{val}; } - static expected create(gnb_id_t gnb_id, uint16_t local_cell_id) + static expected create(gnb_id_t gnb_id, uint16_t sector_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. + if (sector_id >= (1U << (36U - gnb_id.bit_length))) { + // invalid sector id. return make_unexpected(default_error_t{}); } - return nr_cell_identity{(uint64_t)gnb_id.id << (36U - gnb_id.bit_length) | local_cell_id}; + return nr_cell_identity{(uint64_t)gnb_id.id << (36U - gnb_id.bit_length) | sector_id}; } static expected parse_hex(const std::string& hex_str) @@ -81,11 +81,11 @@ class nr_cell_identity 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 + /// Extract Sector ID from NR Cell Identity. + uint16_t sector_id(unsigned nof_sector_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); + srsran_assert(nof_sector_id_bits >= 4 and nof_sector_id_bits <= 14, "Invalid number of Sector Id bits"); + return val & ((1U << nof_sector_id_bits) - 1U); } /// Extract gNB-DU ID from NR Cell Identity. diff --git a/include/srsran/rlc/rlc_metrics.h b/include/srsran/rlc/rlc_metrics.h index 904f8773c8..6483176993 100644 --- a/include/srsran/rlc/rlc_metrics.h +++ b/include/srsran/rlc/rlc_metrics.h @@ -37,6 +37,7 @@ struct rlc_metrics { rlc_tx_metrics tx; rlc_rx_metrics rx; unsigned counter; + timer_duration metrics_period; }; /// \brief Notifier interface used to report RLC metrics. diff --git a/include/srsran/rlc/rlc_rx_metrics.h b/include/srsran/rlc/rlc_rx_metrics.h index 6778a02b6d..ed0167348b 100644 --- a/include/srsran/rlc/rlc_rx_metrics.h +++ b/include/srsran/rlc/rlc_rx_metrics.h @@ -23,6 +23,8 @@ #pragma once #include "srsran/rlc/rlc_config.h" +#include "srsran/support/engineering_notation.h" +#include "srsran/support/format_utils.h" #include "fmt/format.h" namespace srsran { @@ -81,6 +83,42 @@ class rlc_rx_metrics_interface virtual rlc_rx_metrics get_and_reset_metrics() = 0; virtual void reset_metrics() = 0; }; + +inline std::string format_rlc_rx_metrics(timer_duration metrics_period, const rlc_rx_metrics& m) +{ + fmt::memory_buffer buffer; + fmt::format_to(buffer, + "num_sdus={} sdu_rate={}bps num_pdus={} pdu_rate={}bps", + scaled_fmt_integer(m.num_sdus, false), + float_to_eng_string(static_cast(m.num_sdu_bytes) * 8 * 1000 / metrics_period.count(), 1, false), + scaled_fmt_integer(m.num_pdus, false), + (double)m.num_pdu_bytes * 8 / (double)metrics_period.count()); + + // No TM specific metrics for RX + if ((m.mode == rlc_mode::um_bidir || m.mode == rlc_mode::um_unidir_ul)) { + // Format UM specific metrics for RX + fmt::format_to(buffer, + " num_sdu_segments={} sdu_segmments_rate={}bps", + scaled_fmt_integer(m.mode_specific.um.num_sdu_segments, false), + float_to_eng_string(static_cast(m.mode_specific.um.num_sdu_segment_bytes) * 8 * 1000 / + metrics_period.count(), + 1, + false)); + } else if (m.mode == rlc_mode::am) { + fmt::format_to( + buffer, + " num_sdu_segments={} sdu_segmments_rate={}bps", + " ctrl_pdus={} ctrl_rate={}bps", + scaled_fmt_integer(m.mode_specific.am.num_sdu_segments, false), + float_to_eng_string( + static_cast(m.mode_specific.am.num_sdu_segment_bytes) * 8 * 1000 / metrics_period.count(), 1, false), + scaled_fmt_integer(m.mode_specific.am.num_ctrl_pdus, false), + float_to_eng_string( + static_cast(m.mode_specific.am.num_ctrl_pdu_bytes) * 8 * 1000 / metrics_period.count(), 1, false)); + } + return to_c_str(buffer); +} + } // namespace srsran namespace fmt { diff --git a/include/srsran/rlc/rlc_tx_metrics.h b/include/srsran/rlc/rlc_tx_metrics.h index 3a2151c7d2..6681ef32a5 100644 --- a/include/srsran/rlc/rlc_tx_metrics.h +++ b/include/srsran/rlc/rlc_tx_metrics.h @@ -23,6 +23,8 @@ #pragma once #include "srsran/rlc/rlc_config.h" +#include "srsran/support/engineering_notation.h" +#include "srsran/support/format_utils.h" #include "fmt/format.h" namespace srsran { @@ -87,6 +89,50 @@ class rlc_tx_metrics_interface virtual rlc_tx_metrics get_and_reset_metrics() = 0; virtual void reset_metrics() = 0; }; + +inline std::string format_rlc_tx_metrics(timer_duration metrics_period, const rlc_tx_metrics& m) +{ + fmt::memory_buffer buffer; + fmt::format_to( + buffer, + "num_sdus={} sdu_rate={}bps dropped_sdus={} discarded_sdus={} " + "num_pdus_no_segm={} pdu_rate_no_segm={}bps", + scaled_fmt_integer(m.num_sdus, false), + float_to_eng_string(static_cast(m.num_sdu_bytes) * 8 * 1000 / (metrics_period.count()), 1, false), + scaled_fmt_integer(m.num_dropped_sdus, false), + scaled_fmt_integer(m.num_discarded_sdus, false), + scaled_fmt_integer(m.num_pdus_no_segmentation, false), + float_to_eng_string( + static_cast(m.num_pdu_bytes_no_segmentation) * 8 * 1000 / metrics_period.count(), 1, false)); + + if (m.mode == rlc_mode::tm) { + // No TM specific metrics for RX + } else if ((m.mode == rlc_mode::um_bidir || m.mode == rlc_mode::um_unidir_dl)) { + fmt::format_to(buffer, + " num_pdus_with_segm={} pdu_with_segm_rate={}bps", + m.mode_specific.um.num_pdus_with_segmentation, + static_cast(m.mode_specific.um.num_pdu_bytes_with_segmentation) * 8 / metrics_period.count()); + } else if (m.mode == rlc_mode::am) { + fmt::format_to( + buffer, + " num_pdus_with_segm={} pdu_rate_with_segm={}bps num_retx={} " + "retx_rate={}bps ctrl_pdus={} ctrl_rate={}bps", + scaled_fmt_integer(m.mode_specific.am.num_pdus_with_segmentation, false), + float_to_eng_string(static_cast(m.mode_specific.am.num_pdu_bytes_with_segmentation) * 8 * 1000 / + metrics_period.count(), + 1, + false), + scaled_fmt_integer(m.mode_specific.am.num_retx_pdus, false), + float_to_eng_string( + static_cast(m.mode_specific.am.num_retx_pdu_bytes) * 8 * 1000 / metrics_period.count(), 1, false), + scaled_fmt_integer(m.mode_specific.am.num_ctrl_pdus, false), + float_to_eng_string(static_cast(m.mode_specific.am.num_ctrl_pdu_bytes) * 8 * 1000 / + (double)metrics_period.count(), + 1, + false)); + } + return to_c_str(buffer); +} } // namespace srsran namespace fmt { diff --git a/include/srsran/rrc/rrc_ue.h b/include/srsran/rrc/rrc_ue.h index 9eea88501a..3313cec7c0 100644 --- a/include/srsran/rrc/rrc_ue.h +++ b/include/srsran/rrc/rrc_ue.h @@ -210,10 +210,14 @@ class rrc_ue_control_message_handler /// \returns The Security Mode Command context. virtual rrc_ue_security_mode_command_context get_security_mode_command_context() = 0; - /// \brief Await a RRC Security Mode Command Complete for a handover. - /// \param[in] transaction_id The transaction ID of the RRC Security Mode Command Complete. - /// \returns True if the RRC Security Mode Command Complete was received, false otherwise. - virtual async_task handle_security_mode_command_complete_expected(uint8_t transaction_id) = 0; + /// \brief Await a RRC Security Mode Complete. + /// \param[in] transaction_id The transaction ID of the RRC Security Mode Complete. + /// \returns True if the RRC Security Mode Complete was received, false otherwise. + virtual async_task handle_security_mode_complete_expected(uint8_t transaction_id) = 0; + + /// \brief Get the packed UE Capability RAT Container List. + /// \returns The packed UE Capability RAT Container List. + virtual byte_buffer get_packed_ue_capability_rat_container_list() const = 0; /// \brief Handle an RRC Reconfiguration Request. /// \param[in] msg The new RRC Reconfiguration Request. @@ -264,6 +268,17 @@ class rrc_ue_control_message_handler virtual byte_buffer get_packed_handover_preparation_message() = 0; }; +/// Handler to get the UE radio access capability info to the NGAP. +class rrc_ue_radio_access_capability_handler +{ +public: + virtual ~rrc_ue_radio_access_capability_handler() = default; + + /// \brief Get the packed UE Radio Access Cap Info. + /// \returns The packed UE Radio Access Cap Info. + virtual byte_buffer get_packed_ue_radio_access_cap_info() const = 0; +}; + /// Handler to get the handover preparation context to the NGAP. class rrc_ue_handover_preparation_handler { @@ -402,6 +417,7 @@ class rrc_ue_interface : public rrc_ul_ccch_pdu_handler, public rrc_dl_nas_message_handler, public rrc_ue_srb_handler, public rrc_ue_control_message_handler, + public rrc_ue_radio_access_capability_handler, public rrc_ue_setup_proc_notifier, public rrc_ue_security_mode_command_proc_notifier, public rrc_ue_reconfiguration_proc_notifier, @@ -413,14 +429,15 @@ class rrc_ue_interface : public rrc_ul_ccch_pdu_handler, rrc_ue_interface() = default; virtual ~rrc_ue_interface() = default; - virtual rrc_ue_controller& get_controller() = 0; - virtual rrc_ul_ccch_pdu_handler& get_ul_ccch_pdu_handler() = 0; - virtual rrc_ul_dcch_pdu_handler& get_ul_dcch_pdu_handler() = 0; - virtual rrc_dl_nas_message_handler& get_rrc_dl_nas_message_handler() = 0; - virtual rrc_ue_srb_handler& get_rrc_ue_srb_handler() = 0; - virtual rrc_ue_control_message_handler& get_rrc_ue_control_message_handler() = 0; - virtual rrc_ue_context_handler& get_rrc_ue_context_handler() = 0; - virtual rrc_ue_handover_preparation_handler& get_rrc_ue_handover_preparation_handler() = 0; + virtual rrc_ue_controller& get_controller() = 0; + virtual rrc_ul_ccch_pdu_handler& get_ul_ccch_pdu_handler() = 0; + virtual rrc_ul_dcch_pdu_handler& get_ul_dcch_pdu_handler() = 0; + virtual rrc_dl_nas_message_handler& get_rrc_dl_nas_message_handler() = 0; + virtual rrc_ue_srb_handler& get_rrc_ue_srb_handler() = 0; + virtual rrc_ue_control_message_handler& get_rrc_ue_control_message_handler() = 0; + virtual rrc_ue_radio_access_capability_handler& get_rrc_ue_radio_access_capability_handler() = 0; + virtual rrc_ue_context_handler& get_rrc_ue_context_handler() = 0; + virtual rrc_ue_handover_preparation_handler& get_rrc_ue_handover_preparation_handler() = 0; }; } // namespace srs_cu_cp diff --git a/include/srsran/support/engineering_notation.h b/include/srsran/support/engineering_notation.h new file mode 100644 index 0000000000..938932442a --- /dev/null +++ b/include/srsran/support/engineering_notation.h @@ -0,0 +1,131 @@ + +/* + * + * 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 "fmt/core.h" +#include +#include +#include +#include +#include +#include + +namespace srsran { + +/// \file +/// \brief Helper functions to print numbers in engineering notation, for pretty printing. + +inline std::string scaled_fmt_integer(uint64_t num, bool right_align) +{ + static constexpr std::array suffixes = {"", "k", "M", "G", "T", "P", "E", "Z"}; + static const std::array max_nums = []() { + std::array nums{0}; + for (unsigned i = 0, e = nums.size(); i != e; ++i) { + nums[i] = (uint64_t)std::pow(10, i * 3); + } + return nums; + }(); + + if (num < max_nums[1]) { + return right_align ? fmt::format("{:>6}", num) : fmt::format("{}", num); + } + + for (unsigned i = 1, e = max_nums.size() - 1; i != e; ++i) { + if (num < max_nums[i + 1]) { + if (right_align) { + return fmt::format("{:>5.3g}{}", num / static_cast(max_nums[i]), suffixes[i]); + } + return fmt::format("{:.3g}{}", num / static_cast(max_nums[i]), suffixes[i]); + } + } + + return "Invalid number"; +} + +inline std::string float_to_string(float f, int digits, int field_width, bool align_right) +{ + std::ostringstream os; + int precision; + + if (std::isnan(f) || std::abs(f) < 0.0001f) { + f = 0.f; + precision = digits - 1; + } else { + precision = digits - (int)(std::log10(std::abs(f + 0.0001f)) - 2 * DBL_EPSILON); + } + + precision = std::max(precision, 0); + + if (align_right) { + os << std::setw(field_width) << std::fixed << std::setprecision(precision) << f; + } else { + os << std::fixed << std::setprecision(precision) << f; + } + return os.str(); +} + +inline std::string float_to_eng_string(float f, int digits, bool align_right) +{ + static char const* const prefixes[2][9] = { + { + "", + "m", + "u", + "n", + "p", + "f", + "a", + "z", + "y", + }, + { + "", + "k", + "M", + "G", + "T", + "P", + "E", + "Z", + "Y", + }, + }; + + const int degree = (f == 0.f) ? 0 : std::lrint(std::floor(std::log10(std::abs(f)) / 3)); + + std::string factor; + + if (std::abs(degree) < 9) { + factor = prefixes[(degree < 0) ? 0 : 1][std::abs(degree)]; + } else { + return "failed"; + } + + const double scaled = f * std::pow(1000.0, -degree); + if (degree != 0) { + return float_to_string(scaled, digits, 5, align_right) + factor; + } + return " " + float_to_string(scaled, digits, 5 - factor.length(), align_right) + factor; +} +} // namespace srsran diff --git a/include/srsran/support/executors/task_execution_manager.h b/include/srsran/support/executors/task_execution_manager.h index 2ab39c28a0..e911c92b0d 100644 --- a/include/srsran/support/executors/task_execution_manager.h +++ b/include/srsran/support/executors/task_execution_manager.h @@ -50,6 +50,8 @@ struct strand { concurrent_queue_policy policy; /// \brief Size of the queue used. unsigned size; + /// \brief Whether the caller blocks waiting for task to complete. + bool synchronous = false; }; /// Queues of different priorities. The lower the index, the higher the priority. std::vector queues; diff --git a/include/srsran/support/math/gcd.h b/include/srsran/support/math/lcm.h similarity index 69% rename from include/srsran/support/math/gcd.h rename to include/srsran/support/math/lcm.h index 9478d1275e..7367a5834c 100644 --- a/include/srsran/support/math/gcd.h +++ b/include/srsran/support/math/lcm.h @@ -27,37 +27,12 @@ namespace srsran { -/// Calculates the greatest common divisor (GCD) of two integers. -template -Integer gcd(Integer a, Integer b) -{ - while (true) { - if (a == 0) { - return b; - } - b %= a; - if (b == 0) { - return a; - } - a %= b; - } -} - -/// Calculates the least common multiplier (LCM) of two integers. -template -Integer lcm(Integer a, Integer b) -{ - Integer temp = gcd(a, b); - - return temp != 0 ? (a / temp * b) : 0; -} - /// Calculates the least common multiplier (LCM) for a range of integers. template Integer lcm(span values) { return std::accumulate( - values.begin(), values.end(), Integer(1), [](Integer a, Integer b) { return lcm(a, b); }); + values.begin(), values.end(), Integer(1), [](Integer a, Integer b) { return std::lcm(a, b); }); } } // namespace srsran \ No newline at end of file diff --git a/lib/cu_cp/adapters/du_processor_adapters.h b/lib/cu_cp/adapters/du_processor_adapters.h index fbe8b3dab0..2bd4daacee 100644 --- a/lib/cu_cp/adapters/du_processor_adapters.h +++ b/lib/cu_cp/adapters/du_processor_adapters.h @@ -99,14 +99,6 @@ class du_processor_f1ap_ue_context_adapter : public du_processor_f1ap_ue_context void connect_f1(f1ap_ue_context_manager& handler_) { handler = &handler_; } - async_task - on_ue_context_setup_request(const f1ap_ue_context_setup_request& request, - std::optional rrc_context) override - { - srsran_assert(handler != nullptr, "F1AP handler must not be nullptr"); - return handler->handle_ue_context_setup_request(request, rrc_context); - } - async_task on_ue_context_release_command(const f1ap_ue_context_release_command& msg) override { srsran_assert(handler != nullptr, "F1AP handler must not be nullptr"); @@ -217,6 +209,12 @@ class du_processor_rrc_ue_adapter : public du_processor_rrc_ue_control_message_n return rrc_ue_handler->handle_rrc_reconfiguration_request(msg); } + byte_buffer get_packed_ue_capability_rat_container_list() override + { + srsran_assert(rrc_ue_handler != nullptr, "RRC UE handler must not be nullptr"); + return rrc_ue_handler->get_packed_ue_capability_rat_container_list(); + } + rrc_ue_handover_reconfiguration_context get_rrc_ue_handover_reconfiguration_context(const rrc_reconfiguration_procedure_request& request) override { diff --git a/lib/cu_cp/adapters/ngap_adapters.h b/lib/cu_cp/adapters/ngap_adapters.h index 22d28feb0c..546407f195 100644 --- a/lib/cu_cp/adapters/ngap_adapters.h +++ b/lib/cu_cp/adapters/ngap_adapters.h @@ -191,11 +191,13 @@ class ngap_rrc_ue_adapter : public ngap_rrc_ue_pdu_notifier, public ngap_rrc_ue_ public: ngap_rrc_ue_adapter() = default; - void connect_rrc_ue(rrc_dl_nas_message_handler& rrc_ue_msg_handler_, - rrc_ue_handover_preparation_handler& rrc_ue_ho_prep_handler_) + void connect_rrc_ue(rrc_dl_nas_message_handler& rrc_ue_msg_handler_, + rrc_ue_radio_access_capability_handler& rrc_ue_radio_access_cap_handler_, + rrc_ue_handover_preparation_handler& rrc_ue_ho_prep_handler_) { - rrc_ue_msg_handler = &rrc_ue_msg_handler_; - rrc_ue_ho_prep_handler = &rrc_ue_ho_prep_handler_; + rrc_ue_msg_handler = &rrc_ue_msg_handler_; + rrc_ue_radio_access_cap_handler = &rrc_ue_radio_access_cap_handler_; + rrc_ue_ho_prep_handler = &rrc_ue_ho_prep_handler_; } void on_new_pdu(byte_buffer nas_pdu) override @@ -204,6 +206,12 @@ class ngap_rrc_ue_adapter : public ngap_rrc_ue_pdu_notifier, public ngap_rrc_ue_ rrc_ue_msg_handler->handle_dl_nas_transport_message(std::move(nas_pdu)); } + byte_buffer on_ue_radio_access_cap_info_required() override + { + srsran_assert(rrc_ue_radio_access_cap_handler != nullptr, "RRC UE Radio Access Cap handler must not be nullptr"); + return rrc_ue_radio_access_cap_handler->get_packed_ue_radio_access_cap_info(); + } + byte_buffer on_handover_preparation_message_required() override { srsran_assert(rrc_ue_ho_prep_handler != nullptr, "RRC UE UP manager must not be nullptr"); @@ -211,8 +219,9 @@ class ngap_rrc_ue_adapter : public ngap_rrc_ue_pdu_notifier, public ngap_rrc_ue_ } private: - rrc_dl_nas_message_handler* rrc_ue_msg_handler = nullptr; - rrc_ue_handover_preparation_handler* rrc_ue_ho_prep_handler = nullptr; + rrc_dl_nas_message_handler* rrc_ue_msg_handler = nullptr; + rrc_ue_radio_access_capability_handler* rrc_ue_radio_access_cap_handler = nullptr; + rrc_ue_handover_preparation_handler* rrc_ue_ho_prep_handler = nullptr; }; } // namespace srs_cu_cp diff --git a/lib/cu_cp/cu_cp_impl.cpp b/lib/cu_cp/cu_cp_impl.cpp index 3d0a821741..17dd53100a 100644 --- a/lib/cu_cp/cu_cp_impl.cpp +++ b/lib/cu_cp/cu_cp_impl.cpp @@ -395,6 +395,7 @@ cu_cp_impl::handle_new_initial_context_setup_request(const ngap_init_context_set return routine_mng.start_initial_context_setup_routine( request, *rrc_ue, + ngap_entity->get_ngap_ue_radio_cap_management_handler(), ue->get_security_manager(), du_db.get_du_processor(ue->get_du_index()).get_f1ap_interface().get_f1ap_ue_context_manager(), get_cu_cp_ngap_handler()); @@ -643,6 +644,7 @@ void cu_cp_impl::handle_rrc_ue_creation(ue_index_t ue_index, rrc_ue_interface& r { // Connect RRC UE to NGAP to RRC UE adapter ue_mng.get_ngap_rrc_ue_adapter(ue_index).connect_rrc_ue(rrc_ue.get_rrc_dl_nas_message_handler(), + rrc_ue.get_rrc_ue_radio_access_capability_handler(), rrc_ue.get_rrc_ue_handover_preparation_handler()); // Connect cu-cp to rrc ue adapters diff --git a/lib/cu_cp/du_processor/du_configuration_manager.cpp b/lib/cu_cp/du_processor/du_configuration_manager.cpp index 9446020621..e535ad0f78 100644 --- a/lib/cu_cp/du_processor/du_configuration_manager.cpp +++ b/lib/cu_cp/du_processor/du_configuration_manager.cpp @@ -21,6 +21,7 @@ */ #include "du_configuration_manager.h" +#include "srsran/ngap/ngap_configuration.h" #include "srsran/rrc/rrc_config.h" using namespace srsran; @@ -86,8 +87,8 @@ class du_configuration_manager::du_configuration_handler_impl : public du_config du_configuration_manager& parent; }; -du_configuration_manager::du_configuration_manager(const rrc_cfg_t& rrc_cfg_) : - rrc_cfg(rrc_cfg_), logger(srslog::fetch_basic_logger("CU-CP")) +du_configuration_manager::du_configuration_manager(const ngap_configuration& ngap_cfg_, const rrc_cfg_t& rrc_cfg_) : + ngap_cfg(ngap_cfg_), rrc_cfg(rrc_cfg_), logger(srslog::fetch_basic_logger("CU-CP")) { } @@ -201,7 +202,7 @@ void du_configuration_manager::rem_du(gnb_du_id_t du_id) { auto it = dus.find(du_id); if (it == dus.end()) { - logger.warning("du_id={}: Failed to remove DU. Cause: DU not found", du_id); + logger.warning("du={}: Failed to remove DU. Cause: DU not found", du_id); return; } dus.erase(it); @@ -210,25 +211,45 @@ void du_configuration_manager::rem_du(gnb_du_id_t du_id) error_type du_configuration_manager::validate_new_du_config(const du_setup_request& req) const { - // 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 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 make_unexpected( du_setup_result::rejected{cause_protocol_t::msg_not_compatible_with_receiver_state, "Too many served cells"}); } - // Validate served cell configurations. + // Validate served cell configurations provided in the configuration request. for (const auto& served_cell : req.gnb_du_served_cells_list) { auto ret = validate_cell_config_request(served_cell); if (not ret.has_value()) { return ret; } + + if (served_cell.served_cell_info.nr_cgi.plmn_id != ngap_cfg.plmn) { + return make_unexpected(du_setup_result::rejected{f1ap_cause_radio_network_t::plmn_not_served_by_the_gnb_cu, + "Served Cell CGI PLMN is not supported by the CU-CP"}); + } + + if (std::none_of(served_cell.served_cell_info.served_plmns.begin(), + served_cell.served_cell_info.served_plmns.end(), + [this](const plmn_identity& plmn) { return plmn == ngap_cfg.plmn; })) { + return make_unexpected(du_setup_result::rejected{f1ap_cause_radio_network_t::plmn_not_served_by_the_gnb_cu, + "None of the served cell PLMNs is available in the CU-CP"}); + } + } + + // 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 make_unexpected( + du_setup_result::rejected{cause_protocol_t::msg_not_compatible_with_receiver_state, "Duplicate DU ID"}); + } + for (const auto& cell : du_cfg.served_cells) { + for (const auto& new_cell : req.gnb_du_served_cells_list) { + if (cell.cgi == new_cell.served_cell_info.nr_cgi) { + return make_unexpected(du_setup_result::rejected{cause_protocol_t::msg_not_compatible_with_receiver_state, + "Duplicate served cell CGI"}); + } + } + } } return {}; diff --git a/lib/cu_cp/du_processor/du_configuration_manager.h b/lib/cu_cp/du_processor/du_configuration_manager.h index e028057f2d..5eb98ac1c0 100644 --- a/lib/cu_cp/du_processor/du_configuration_manager.h +++ b/lib/cu_cp/du_processor/du_configuration_manager.h @@ -30,12 +30,13 @@ namespace srsran { namespace srs_cu_cp { struct rrc_cfg_t; +struct ngap_configuration; /// Validator and repository of configurations for DUs handled by the CU-CP. class du_configuration_manager { public: - du_configuration_manager(const rrc_cfg_t& rrc_cfg_); + du_configuration_manager(const ngap_configuration& ngap_cfg_, const rrc_cfg_t& rrc_cfg_); /// Create a new DU configuration handler. std::unique_ptr create_du_handler(); @@ -56,8 +57,9 @@ class du_configuration_manager validation_result validate_du_config_update(const du_config_update_request& req) const; validation_result validate_cell_config_request(const cu_cp_du_served_cells_item& served_cell) const; - const rrc_cfg_t& rrc_cfg; - srslog::basic_logger& logger; + const ngap_configuration& ngap_cfg; + const rrc_cfg_t& rrc_cfg; + srslog::basic_logger& logger; std::unordered_map dus; }; diff --git a/lib/cu_cp/du_processor/du_processor.h b/lib/cu_cp/du_processor/du_processor.h index 7622b03d41..5e9a8c1cca 100644 --- a/lib/cu_cp/du_processor/du_processor.h +++ b/lib/cu_cp/du_processor/du_processor.h @@ -83,11 +83,6 @@ class du_processor_f1ap_ue_context_notifier public: virtual ~du_processor_f1ap_ue_context_notifier() = default; - /// Notify F1AP to establish the UE context. - virtual async_task - on_ue_context_setup_request(const f1ap_ue_context_setup_request& request, - std::optional rrc_context) = 0; - /// \brief Notify the F1AP to initiate the UE Context Release procedure. /// \param[in] msg The UE Context Release message to transmit. /// \return Returns the index of the released UE. @@ -168,6 +163,10 @@ class du_processor_rrc_ue_control_message_notifier /// \returns The result of the rrc reconfiguration. virtual async_task on_rrc_reconfiguration_request(const rrc_reconfiguration_procedure_request& msg) = 0; + /// \brief Get the packed UE Capability RAT Container List. + /// \returns The packed UE Capability RAT Container List. + virtual byte_buffer get_packed_ue_capability_rat_container_list() = 0; + /// \brief Request the RRC Handover Reconfiguration Context. /// \returns The RRC Handover Reconfiguration Context. virtual rrc_ue_handover_reconfiguration_context @@ -191,6 +190,7 @@ class du_processor_rrc_ue_control_message_notifier /// \return The measurement config, if present. virtual std::optional generate_meas_config(std::optional current_meas_config = {}) = 0; + /// \brief Request the packed Handover Preparation Message. virtual byte_buffer get_packed_handover_preparation_message() = 0; /// \brief Notify about the reception of a new Handover Command PDU. diff --git a/lib/cu_cp/du_processor/du_processor_impl.cpp b/lib/cu_cp/du_processor/du_processor_impl.cpp index 792f84a145..23f3b08a85 100644 --- a/lib/cu_cp/du_processor/du_processor_impl.cpp +++ b/lib/cu_cp/du_processor/du_processor_impl.cpp @@ -35,7 +35,7 @@ du_processor_impl::du_processor_impl(du_processor_config_t du_proc du_processor_cu_cp_notifier& cu_cp_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_ue_control_notifier& rrc_ue_ngap_ctrl_notifier, rrc_du_measurement_config_notifier& rrc_du_cu_cp_notifier, common_task_scheduler& common_task_sched_, ue_manager& ue_mng_, @@ -45,7 +45,6 @@ du_processor_impl::du_processor_impl(du_processor_config_t du_proc cu_cp_notifier(cu_cp_notifier_), f1ap_pdu_notifier(f1ap_pdu_notifier_), rrc_ue_nas_pdu_notifier(rrc_ue_nas_pdu_notifier_), - rrc_ue_ngap_ctrl_notifier(rrc_ue_ngap_ctrl_notifier_), ue_mng(ue_mng_), f1ap_ev_notifier(common_task_sched_, *this) { @@ -333,30 +332,6 @@ void du_processor_impl::handle_paging_message(cu_cp_paging_message& msg) f1ap_paging_notifier.on_paging_message(msg); } -void du_processor_impl::send_ngap_ue_context_release_request(ue_index_t ue_index, ngap_cause_t cause) -{ - cu_cp_ue* ue = ue_mng.find_du_ue(ue_index); - srsran_assert(ue != nullptr, "ue={}: Could not find DU UE", ue_index); - - cu_cp_ue_context_release_request req; - req.ue_index = ue_index; - req.cause = cause; - - // Add PDU Session IDs - auto& up_resource_manager = ue->get_up_resource_manager(); - req.pdu_session_res_list_cxt_rel_req = up_resource_manager.get_pdu_sessions(); - - logger.debug("ue={}: Requesting UE context release with cause={}", req.ue_index, cause); - - // Schedule on UE task scheduler - ue->get_task_sched().schedule_async_task(launch_async([this, req](coro_context>& ctx) mutable { - CORO_BEGIN(ctx); - // Notify NGAP to request a release from the AMF - CORO_AWAIT(cu_cp_notifier.on_ue_release_required(req)); - CORO_RETURN(); - })); -} - bool du_processor_impl::has_cell(pci_t pci) { return cfg.du_cfg_hdlr->get_context().find_cell(pci) != nullptr; diff --git a/lib/cu_cp/du_processor/du_processor_impl.h b/lib/cu_cp/du_processor/du_processor_impl.h index ae20f3f71c..7ed09a180e 100644 --- a/lib/cu_cp/du_processor/du_processor_impl.h +++ b/lib/cu_cp/du_processor/du_processor_impl.h @@ -106,19 +106,12 @@ class du_processor_impl : public du_processor, byte_buffer du_to_cu_rrc_container, std::optional rrc_context); - // NGAP senders - /// \brief Request UE context release over NGAP. - /// \param[in] ue_index The UE. - /// \param[in] cause The cause of the failure. - void send_ngap_ue_context_release_request(ue_index_t ue_index, ngap_cause_t cause); - srslog::basic_logger& logger = srslog::fetch_basic_logger("CU-CP"); du_processor_config_t cfg; du_processor_cu_cp_notifier& cu_cp_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; ue_manager& ue_mng; du_processor_f1ap_ue_context_adapter f1ap_ue_context_notifier; du_processor_f1ap_paging_adapter f1ap_paging_notifier; diff --git a/lib/cu_cp/du_processor/du_processor_repository.cpp b/lib/cu_cp/du_processor/du_processor_repository.cpp index 1a730a09cd..1d28dac811 100644 --- a/lib/cu_cp/du_processor/du_processor_repository.cpp +++ b/lib/cu_cp/du_processor/du_processor_repository.cpp @@ -31,7 +31,7 @@ using namespace srsran; using namespace srs_cu_cp; du_processor_repository::du_processor_repository(du_repository_config cfg_) : - cfg(cfg_), logger(cfg.logger), du_cfg_mng(cfg.cu_cp.rrc_config) + cfg(cfg_), logger(cfg.logger), du_cfg_mng(cfg.cu_cp.ngap_config, cfg.cu_cp.rrc_config) { } diff --git a/lib/cu_cp/log_format.h b/lib/cu_cp/log_format.h index dbc4871089..3557a537ae 100644 --- a/lib/cu_cp/log_format.h +++ b/lib/cu_cp/log_format.h @@ -199,13 +199,13 @@ namespace fmt { template <> struct formatter { template - auto parse(ParseContext& ctx) -> decltype(ctx.begin()) + auto parse(ParseContext& ctx) { return ctx.begin(); } + template auto format(const srsran::srs_cu_cp::ue_event_prefix& ue_prefix, FormatContext& ctx) - -> decltype(std::declval().out()) { using namespace srsran; auto ret = format_to(ctx.out(), "{:<4}", ue_prefix.direction); diff --git a/lib/cu_cp/routine_managers/cu_cp_routine_manager.cpp b/lib/cu_cp/routine_managers/cu_cp_routine_manager.cpp index e72c59e1c2..b95e10eba9 100644 --- a/lib/cu_cp/routine_managers/cu_cp_routine_manager.cpp +++ b/lib/cu_cp/routine_managers/cu_cp_routine_manager.cpp @@ -52,14 +52,16 @@ bool cu_cp_routine_manager::schedule_async_task(async_task task) } async_task> -cu_cp_routine_manager::start_initial_context_setup_routine(const ngap_init_context_setup_request& request, - rrc_ue_interface& rrc_ue, - ue_security_manager& security_mng, - f1ap_ue_context_manager& f1ap_ue_ctxt_mng, - cu_cp_ngap_handler& pdu_session_setup_handler) +cu_cp_routine_manager::start_initial_context_setup_routine( + const ngap_init_context_setup_request& request, + rrc_ue_interface& rrc_ue, + ngap_ue_radio_capability_management_handler& ngap_ue_radio_cap_handler, + ue_security_manager& security_mng, + f1ap_ue_context_manager& f1ap_ue_ctxt_mng, + cu_cp_ngap_handler& pdu_session_setup_handler) { return launch_async( - request, rrc_ue, security_mng, f1ap_ue_ctxt_mng, pdu_session_setup_handler, logger); + request, rrc_ue, ngap_ue_radio_cap_handler, security_mng, f1ap_ue_ctxt_mng, pdu_session_setup_handler, logger); } async_task cu_cp_routine_manager::start_pdu_session_resource_setup_routine( diff --git a/lib/cu_cp/routine_managers/cu_cp_routine_manager.h b/lib/cu_cp/routine_managers/cu_cp_routine_manager.h index a33ed05407..a467ad5591 100644 --- a/lib/cu_cp/routine_managers/cu_cp_routine_manager.h +++ b/lib/cu_cp/routine_managers/cu_cp_routine_manager.h @@ -43,11 +43,12 @@ class cu_cp_routine_manager : public common_task_scheduler bool schedule_async_task(async_task task) override; async_task> - start_initial_context_setup_routine(const ngap_init_context_setup_request& request, - rrc_ue_interface& rrc_ue, - ue_security_manager& security_mng, - f1ap_ue_context_manager& f1ap_ue_ctxt_mng, - cu_cp_ngap_handler& pdu_session_setup_handler); + start_initial_context_setup_routine(const ngap_init_context_setup_request& request, + rrc_ue_interface& rrc_ue, + ngap_ue_radio_capability_management_handler& ngap_ue_radio_cap_handler, + ue_security_manager& security_mng, + f1ap_ue_context_manager& f1ap_ue_ctxt_mng, + cu_cp_ngap_handler& pdu_session_setup_handler); async_task start_pdu_session_resource_setup_routine(const cu_cp_pdu_session_resource_setup_request& setup_msg, diff --git a/lib/cu_cp/routines/initial_context_setup_routine.cpp b/lib/cu_cp/routines/initial_context_setup_routine.cpp index de281c67d8..8af9260b80 100644 --- a/lib/cu_cp/routines/initial_context_setup_routine.cpp +++ b/lib/cu_cp/routines/initial_context_setup_routine.cpp @@ -30,14 +30,17 @@ using namespace srsran; using namespace srsran::srs_cu_cp; using namespace asn1::rrc_nr; -initial_context_setup_routine::initial_context_setup_routine(const ngap_init_context_setup_request& request_, - rrc_ue_interface& rrc_ue_, - ue_security_manager& security_mng_, - f1ap_ue_context_manager& f1ap_ue_ctxt_mng_, - cu_cp_ngap_handler& pdu_session_setup_handler_, - srslog::basic_logger& logger_) : +initial_context_setup_routine::initial_context_setup_routine( + const ngap_init_context_setup_request& request_, + rrc_ue_interface& rrc_ue_, + ngap_ue_radio_capability_management_handler& ngap_ue_radio_cap_handler_, + ue_security_manager& security_mng_, + f1ap_ue_context_manager& f1ap_ue_ctxt_mng_, + cu_cp_ngap_handler& pdu_session_setup_handler_, + srslog::basic_logger& logger_) : request(request_), rrc_ue(rrc_ue_), + ngap_ue_radio_cap_handler(ngap_ue_radio_cap_handler_), security_mng(security_mng_), f1ap_ue_ctxt_mng(f1ap_ue_ctxt_mng_), pdu_session_setup_handler(pdu_session_setup_handler_), @@ -88,10 +91,10 @@ void initial_context_setup_routine::operator()( } } - // Await Security Mode Command Complete from RRC UE + // Await Security Mode Complete from RRC UE { CORO_AWAIT_VALUE(security_mode_command_result, - rrc_ue.handle_security_mode_command_complete_expected(rrc_smc_ctxt.transaction_id)); + rrc_ue.handle_security_mode_complete_expected(rrc_smc_ctxt.transaction_id)); if (!security_mode_command_result) { handle_failure(); CORO_EARLY_RETURN(make_unexpected(fail_msg)); @@ -100,7 +103,15 @@ void initial_context_setup_routine::operator()( // Start UE Capability Enquiry Procedure { - /// TODO: Move UE Capability Enquiry Procedure here + CORO_AWAIT_VALUE(ue_capability_transfer_result, + rrc_ue.handle_rrc_ue_capability_transfer_request(ue_capability_transfer_request)); + + // Handle UE Capability Transfer result + if (not ue_capability_transfer_result) { + logger.warning("ue={}: \"{}\" UE capability transfer failed", request.ue_index, name()); + handle_failure(); + CORO_EARLY_RETURN(make_unexpected(fail_msg)); + } } // Handle optional IEs @@ -117,47 +128,27 @@ void initial_context_setup_routine::operator()( request.pdu_session_res_setup_list_cxt_req.value().ue_aggregate_maximum_bit_rate_dl = 0; } + // Handle NAS PDUs from Initial Context Setup Request + if (request.nas_pdu.has_value()) { + request.pdu_session_res_setup_list_cxt_req.value().nas_pdu = request.nas_pdu.value().copy(); + } + CORO_AWAIT_VALUE(pdu_session_setup_response, pdu_session_setup_handler.handle_new_pdu_session_resource_setup_request( request.pdu_session_res_setup_list_cxt_req.value())); resp_msg.pdu_session_res_setup_response_items = pdu_session_setup_response.pdu_session_res_setup_response_items; resp_msg.pdu_session_res_failed_to_setup_items = pdu_session_setup_response.pdu_session_res_failed_to_setup_items; - - // Handle NAS PDUs from PDU Session Resource Setup List Context Request - for (auto& session : request.pdu_session_res_setup_list_cxt_req.value().pdu_session_res_setup_items) { - if (!session.pdu_session_nas_pdu.empty()) { - handle_nas_pdu(session.pdu_session_nas_pdu.copy()); - } - } - + } else { // Handle NAS PDUs from Initial Context Setup Request if (request.nas_pdu.has_value()) { handle_nas_pdu(request.nas_pdu.value().copy()); } - - } else { - // prepare RRC Reconfiguration and call RRC UE notifier - if (!fill_rrc_reconfig_args(rrc_reconfig_args, - ue_context_setup_request.srbs_to_be_setup_list, - {} /* No DRB to setup */, - {} /* No extra DRB to be removed */, - ue_context_setup_response.du_to_cu_rrc_info, - request.nas_pdu.has_value() ? std::vector{request.nas_pdu.value().copy()} - : std::vector{}, - rrc_ue.generate_meas_config(std::nullopt), - false /* No SRBs to reestablish */, - false /* No DRBs to reestablish */, - false /* No keys to update */, - {} /* No SIB1 */, - logger)) { - logger.warning("ue={}: \"{}\" Failed to fill RRCReconfiguration", request.ue_index, name()); - CORO_EARLY_RETURN(make_unexpected(fail_msg)); - } - - CORO_AWAIT_VALUE(rrc_reconfig_result, rrc_ue.handle_rrc_reconfiguration_request(rrc_reconfig_args)); } + // Schedule transmission of UE Radio Capability Info Indication + send_ue_radio_capability_info_indication(); + logger.info("ue={}: \"{}\" finished successfully", request.ue_index, name()); CORO_RETURN(resp_msg); } @@ -184,4 +175,12 @@ void initial_context_setup_routine::handle_nas_pdu(byte_buffer nas_pdu) { logger.debug("ue={}: Forwarding NAS PDU to RRC", request.ue_index); rrc_ue.handle_dl_nas_transport_message(std::move(nas_pdu)); +} + +void initial_context_setup_routine::send_ue_radio_capability_info_indication() +{ + ue_radio_cap_info_indication.ue_index = request.ue_index; + ue_radio_cap_info_indication.ue_cap_rat_container_list = rrc_ue.get_packed_ue_radio_access_cap_info(); + + ngap_ue_radio_cap_handler.handle_tx_ue_radio_capability_info_indication_required(ue_radio_cap_info_indication); } \ No newline at end of file diff --git a/lib/cu_cp/routines/initial_context_setup_routine.h b/lib/cu_cp/routines/initial_context_setup_routine.h index c60ca1d5e5..3f3b71cc27 100644 --- a/lib/cu_cp/routines/initial_context_setup_routine.h +++ b/lib/cu_cp/routines/initial_context_setup_routine.h @@ -24,6 +24,7 @@ #include "../ue_manager/ue_manager_impl.h" #include "srsran/ngap/ngap_init_context_setup.h" +#include "srsran/ngap/ngap_ue_radio_capability_management.h" namespace srsran { namespace srs_cu_cp { @@ -32,12 +33,13 @@ namespace srs_cu_cp { class initial_context_setup_routine { public: - initial_context_setup_routine(const ngap_init_context_setup_request& request_, - rrc_ue_interface& rrc_ue_, - ue_security_manager& security_mng_, - f1ap_ue_context_manager& f1ap_ue_ctxt_mng_, - cu_cp_ngap_handler& pdu_session_setup_handler_, - srslog::basic_logger& logger_); + initial_context_setup_routine(const ngap_init_context_setup_request& request_, + rrc_ue_interface& rrc_ue_, + ngap_ue_radio_capability_management_handler& ngap_ue_radio_cap_handler_, + ue_security_manager& security_mng_, + f1ap_ue_context_manager& f1ap_ue_ctxt_mng_, + cu_cp_ngap_handler& pdu_session_setup_handler_, + srslog::basic_logger& logger_); void operator()( coro_context>>& ctx); @@ -46,26 +48,30 @@ class initial_context_setup_routine void handle_failure(); void handle_nas_pdu(byte_buffer nas_pdu); + void send_ue_radio_capability_info_indication(); private: ngap_init_context_setup_request request; - rrc_ue_interface& rrc_ue; - ue_security_manager& security_mng; - f1ap_ue_context_manager& f1ap_ue_ctxt_mng; // to trigger UE context setup at F1AP - cu_cp_ngap_handler& pdu_session_setup_handler; // to setup PDU sessions - srslog::basic_logger& logger; + rrc_ue_interface& rrc_ue; + ngap_ue_radio_capability_management_handler& ngap_ue_radio_cap_handler; + ue_security_manager& security_mng; + f1ap_ue_context_manager& f1ap_ue_ctxt_mng; // to trigger UE context setup at F1AP + cu_cp_ngap_handler& pdu_session_setup_handler; // to setup PDU sessions + srslog::basic_logger& logger; // (sub-)routine requests - rrc_ue_security_mode_command_context rrc_smc_ctxt; - f1ap_ue_context_setup_request ue_context_setup_request; - rrc_reconfiguration_procedure_request rrc_reconfig_args; + rrc_ue_security_mode_command_context rrc_smc_ctxt; + f1ap_ue_context_setup_request ue_context_setup_request; + rrc_ue_capability_transfer_request ue_capability_transfer_request; + ngap_ue_radio_capability_info_indication ue_radio_cap_info_indication; + rrc_reconfiguration_procedure_request rrc_reconfig_args; // (sub-)routine results f1ap_ue_context_setup_response ue_context_setup_response; + bool ue_capability_transfer_result = false; // to query the UE capabilities cu_cp_pdu_session_resource_setup_response pdu_session_setup_response; bool security_mode_command_result = false; - bool rrc_reconfig_result = false; // the final UE reconfiguration ngap_init_context_setup_failure fail_msg; ngap_init_context_setup_response resp_msg; }; 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 9acc3f4997..e1ceff94d2 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 @@ -122,6 +122,7 @@ void inter_cu_handover_target_routine::operator()( ue_context_setup_request.cu_to_du_rrc_info.ie_exts.emplace(); ue_context_setup_request.cu_to_du_rrc_info.ie_exts.value().ho_prep_info = request.source_to_target_transparent_container.rrc_container.copy(); + ue_context_setup_request.cu_to_du_rrc_info.ue_cap_rat_container_list = rrc_context.ue_cap_rat_container_list.copy(); // Call F1AP procedure CORO_AWAIT_VALUE(ue_context_setup_response, 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 42252d229d..39363c2651 100644 --- a/lib/cu_cp/routines/mobility/inter_du_handover_routine.cpp +++ b/lib/cu_cp/routines/mobility/inter_du_handover_routine.cpp @@ -145,7 +145,7 @@ void inter_du_handover_routine::operator()(coro_contextget_security_manager().is_security_context_initialized()) { logger.warning( "ue={}: \"{}\" failed. Cause: Security context not initialized", target_ue->get_ue_index(), name()); @@ -269,6 +269,7 @@ bool inter_du_handover_routine::generate_ue_context_setup_request(f1ap_ue_contex return false; } setup_request.cu_to_du_rrc_info.ie_exts.value().ho_prep_info = std::move(buffer_copy.value()); + setup_request.cu_to_du_rrc_info.ue_cap_rat_container_list = transfer_context.ue_cap_rat_container_list.copy(); for (const auto& srb_id : srbs) { f1ap_srbs_to_be_setup_mod_item srb_item; 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 7ffaf30c86..de0746f245 100644 --- a/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp +++ b/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp @@ -99,17 +99,6 @@ void pdu_session_resource_setup_routine::operator()( CORO_EARLY_RETURN(handle_pdu_session_resource_setup_result(false)); } - { - CORO_AWAIT_VALUE(ue_capability_transfer_result, - rrc_ue_notifier.on_ue_capability_transfer_request(ue_capability_transfer_request)); - - // Handle UE Capability Transfer result - if (not ue_capability_transfer_result) { - logger.warning("ue={}: \"{}\" UE capability transfer failed", setup_msg.ue_index, name()); - CORO_EARLY_RETURN(handle_pdu_session_resource_setup_result(false)); - } - } - { // Calculate next user-plane configuration based on incoming setup message. next_config = up_resource_mng.calculate_update(setup_msg.pdu_session_res_setup_items); @@ -167,6 +156,9 @@ void pdu_session_resource_setup_routine::operator()( { // prepare UE Context Modification Request and call F1 ue_context_mod_request.ue_index = setup_msg.ue_index; + ue_context_mod_request.cu_to_du_rrc_info.emplace(); + ue_context_mod_request.cu_to_du_rrc_info.value().ue_cap_rat_container_list = + rrc_ue_notifier.get_packed_ue_capability_rat_container_list(); // DRB setup have already added above. CORO_AWAIT_VALUE(ue_context_modification_response, @@ -214,6 +206,10 @@ void pdu_session_resource_setup_routine::operator()( { // get NAS PDUs as received by AMF std::vector nas_pdus; + if (!setup_msg.nas_pdu.empty()) { + nas_pdus.push_back(setup_msg.nas_pdu); + } + for (const auto& pdu_session : setup_msg.pdu_session_res_setup_items) { if (!pdu_session.pdu_session_nas_pdu.empty()) { nas_pdus.push_back(pdu_session.pdu_session_nas_pdu); diff --git a/lib/cu_cp/routines/pdu_session_resource_setup_routine.h b/lib/cu_cp/routines/pdu_session_resource_setup_routine.h index 3355103b90..bee23fbc9f 100644 --- a/lib/cu_cp/routines/pdu_session_resource_setup_routine.h +++ b/lib/cu_cp/routines/pdu_session_resource_setup_routine.h @@ -85,7 +85,6 @@ class pdu_session_resource_setup_routine srslog::basic_logger& logger; // (sub-)routine requests - rrc_ue_capability_transfer_request ue_capability_transfer_request; e1ap_bearer_context_setup_request bearer_context_setup_request; f1ap_ue_context_modification_request ue_context_mod_request; e1ap_bearer_context_modification_request bearer_context_modification_request; @@ -93,7 +92,6 @@ class pdu_session_resource_setup_routine // (sub-)routine results cu_cp_pdu_session_resource_setup_response response_msg; - bool ue_capability_transfer_result = false; // to query the UE capabilities e1ap_bearer_context_setup_response bearer_context_setup_response; // to initially setup the DRBs at the CU-UP f1ap_ue_context_modification_response ue_context_modification_response; // to inform DU about the new DRBs e1ap_bearer_context_modification_response diff --git a/lib/du/du_cell_config_validation.cpp b/lib/du/du_cell_config_validation.cpp index 413b193d97..dac48c9a18 100644 --- a/lib/du/du_cell_config_validation.cpp +++ b/lib/du/du_cell_config_validation.cpp @@ -717,9 +717,9 @@ check_outcome srsran::is_du_cell_config_valid(const du_cell_config& cell_cfg) HANDLE_ERROR(check_ssb_configuration(cell_cfg)); HANDLE_ERROR(check_tdd_ul_dl_config(cell_cfg)); const pucch_builder_params& pucch_cfg = cell_cfg.pucch_cfg; - HANDLE_ERROR(srs_du::pucch_parameters_validator(pucch_cfg.nof_ue_pucch_f1_res_harq.to_uint(), + HANDLE_ERROR(srs_du::pucch_parameters_validator(pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq.to_uint(), pucch_cfg.nof_ue_pucch_f2_res_harq.to_uint(), - pucch_cfg.f1_params, + pucch_cfg.f0_or_f1_params, pucch_cfg.f2_params, cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.crbs.length())); HANDLE_ERROR(config_validators::validate_csi_meas_cfg(cell_cfg.ue_ded_serv_cell_cfg, cell_cfg.tdd_ul_dl_cfg_common)); diff --git a/lib/du/du_update_config_helpers.cpp b/lib/du/du_update_config_helpers.cpp index 45402f1a1f..3f8acf214c 100644 --- a/lib/du/du_update_config_helpers.cpp +++ b/lib/du/du_update_config_helpers.cpp @@ -78,11 +78,11 @@ unsigned srsran::config_helpers::compute_prach_frequency_start(const pucch_build // Compute the cell PUCCH resource list, depending on which parameter that has been passed. const std::vector& res_list = srs_du::generate_cell_pucch_res_list( - user_params.nof_ue_pucch_f1_res_harq.to_uint() * user_params.nof_cell_harq_pucch_res_sets + + user_params.nof_ue_pucch_f0_or_f1_res_harq.to_uint() * user_params.nof_cell_harq_pucch_res_sets + user_params.nof_sr_resources, user_params.nof_ue_pucch_f2_res_harq.to_uint() * user_params.nof_cell_harq_pucch_res_sets + user_params.nof_csi_resources, - user_params.f1_params, + user_params.f0_or_f1_params, user_params.f2_params, bwp_size); diff --git a/lib/du_manager/procedures/initial_du_setup_procedure.cpp b/lib/du_manager/procedures/initial_du_setup_procedure.cpp index 544c80b82c..5e9dde87f1 100644 --- a/lib/du_manager/procedures/initial_du_setup_procedure.cpp +++ b/lib/du_manager/procedures/initial_du_setup_procedure.cpp @@ -101,9 +101,9 @@ void initial_du_setup_procedure::handle_f1_setup_response(const f1_setup_respons std::string cause; switch (resp.result) { case f1_setup_response_message::result_code::f1_setup_failure: - cause = "CU-CP responded with F1 Setup Failure"; + cause = "CU-CP responded with \"F1 Setup Failure\""; if (resp.f1_setup_failure_cause != "unspecified") { - cause += fmt::format(" with cause {}", resp.f1_setup_failure_cause); + cause += fmt::format(" with F1AP cause \"{}\"", resp.f1_setup_failure_cause); } break; case f1_setup_response_message::result_code::invalid_response: diff --git a/lib/du_manager/procedures/ue_configuration_procedure.cpp b/lib/du_manager/procedures/ue_configuration_procedure.cpp index 6892721c56..2c17154060 100644 --- a/lib/du_manager/procedures/ue_configuration_procedure.cpp +++ b/lib/du_manager/procedures/ue_configuration_procedure.cpp @@ -21,7 +21,6 @@ */ #include "ue_configuration_procedure.h" -#include "../../ran/gnb_format.h" #include "../converters/asn1_rrc_config_helpers.h" #include "../converters/scheduler_configuration_helpers.h" #include "srsran/mac/mac_ue_configurator.h" diff --git a/lib/du_manager/ran_resource_management/du_pucch_resource_manager.cpp b/lib/du_manager/ran_resource_management/du_pucch_resource_manager.cpp index 00bd917122..433fd10fce 100644 --- a/lib/du_manager/ran_resource_management/du_pucch_resource_manager.cpp +++ b/lib/du_manager/ran_resource_management/du_pucch_resource_manager.cpp @@ -25,7 +25,7 @@ #include "srsran/ran/csi_report/csi_report_on_pucch_helpers.h" #include "srsran/ran/pucch/pucch_info.h" #include "srsran/scheduler/scheduler_pucch_format.h" -#include "srsran/support/math/gcd.h" +#include using namespace srsran; using namespace srs_du; @@ -58,13 +58,13 @@ du_pucch_resource_manager::du_pucch_resource_manager(span unsigned max_pucch_grants_per_slot_) : user_defined_pucch_cfg(cell_cfg_list_[0].pucch_cfg), default_pucch_res_list( - srs_du::generate_cell_pucch_res_list(cell_cfg_list_[0].pucch_cfg.nof_ue_pucch_f1_res_harq.to_uint() * + srs_du::generate_cell_pucch_res_list(cell_cfg_list_[0].pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq.to_uint() * cell_cfg_list_[0].pucch_cfg.nof_cell_harq_pucch_res_sets + cell_cfg_list_[0].pucch_cfg.nof_sr_resources, cell_cfg_list_[0].pucch_cfg.nof_ue_pucch_f2_res_harq.to_uint() * cell_cfg_list_[0].pucch_cfg.nof_cell_harq_pucch_res_sets + cell_cfg_list_[0].pucch_cfg.nof_csi_resources, - cell_cfg_list_[0].pucch_cfg.f1_params, + cell_cfg_list_[0].pucch_cfg.f0_or_f1_params, cell_cfg_list_[0].pucch_cfg.f2_params, cell_cfg_list_[0].ul_cfg_common.init_ul_bwp.generic_params.crbs.length())), default_pucch_cfg( @@ -96,7 +96,7 @@ du_pucch_resource_manager::du_pucch_resource_manager(span } // As the SR and CSI period might not be one a multiple of each other, we compute the Least Common Multiple (LCM) of // the two periods. - lcm_csi_sr_period = lcm(sr_period_slots, csi_period_slots); + lcm_csi_sr_period = std::lcm(sr_period_slots, csi_period_slots); // Setup RAN resources per cell. for (auto& cell : cells) { @@ -160,18 +160,6 @@ du_pucch_resource_manager::find_optimal_csi_report_slot_offset( const unsigned csi_rs_period = csi_resource_periodicity_to_uint(*csi_res.csi_res_period); const unsigned csi_rs_offset = *csi_res.csi_res_offset; - const auto csi_offset_colliding_with_sr = [&](unsigned offset_candidate) -> bool { - for (unsigned csi_off = offset_candidate; csi_off < lcm_csi_sr_period; csi_off += csi_period_slots) { - for (unsigned sr_off = candidate_sr_offset; sr_off < lcm_csi_sr_period; sr_off += sr_period_slots) { - if (csi_off == sr_off) { - return true; - } - } - } - - return false; - }; - const auto weight_function = [&](unsigned offset_candidate) -> unsigned { // This weight formula prioritizes offsets equal or after the \c csi_rs_slot_offset + // MINIMUM_CSI_RS_REPORT_DISTANCE. @@ -179,7 +167,7 @@ du_pucch_resource_manager::find_optimal_csi_report_slot_offset( (csi_rs_period + offset_candidate - csi_rs_offset - MINIMUM_CSI_RS_REPORT_DISTANCE) % csi_rs_period; // We increase the weight if the CSI report offset collides with an SR slot offset. - if (csi_offset_colliding_with_sr(offset_candidate)) { + if (csi_offset_collides_with_sr(candidate_sr_offset, offset_candidate)) { weight += csi_rs_period; } @@ -241,7 +229,7 @@ bool du_pucch_resource_manager::alloc_resources(cell_group_config& cell_grp_cfg) } } - // If the PUCCH is exceeded, proceed with the next SR resource/offset pair. + // If the PUCCH count is exceeded, proceed with the next SR resource/offset pair. if (not pucch_cnt_exceeded) { if (not default_csi_report_cfg.has_value()) { sr_res_offset = *sr_res_offset_it; @@ -285,7 +273,7 @@ bool du_pucch_resource_manager::alloc_resources(cell_group_config& cell_grp_cfg) cells[0].ue_idx, sr_res_offset.value().first, csi_res_offset.has_value() ? csi_res_offset.value().first : 0, - user_defined_pucch_cfg.nof_ue_pucch_f1_res_harq, + user_defined_pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq, user_defined_pucch_cfg.nof_ue_pucch_f2_res_harq, user_defined_pucch_cfg.nof_cell_harq_pucch_res_sets, user_defined_pucch_cfg.nof_sr_resources, @@ -393,11 +381,24 @@ std::set du_pucch_resource_manager::compute_sr_csi_pucch_offsets(unsig return sr_csi_offsets; } +bool du_pucch_resource_manager::csi_offset_collides_with_sr(unsigned sr_offset, unsigned csi_offset) const +{ + for (unsigned csi_off = csi_offset; csi_off < lcm_csi_sr_period; csi_off += csi_period_slots) { + for (unsigned sr_off = sr_offset; sr_off < lcm_csi_sr_period; sr_off += sr_period_slots) { + if (csi_off == sr_off) { + return true; + } + } + } + + return false; +} + unsigned du_pucch_resource_manager::pucch_res_idx_to_sr_du_res_idx(unsigned pucch_res_idx) const { // The mapping from the UE's PUCCH-Config \ref res_id index to the DU index for PUCCH SR resource is the inverse of // what is defined in \ref srs_du::ue_pucch_config_builder. - return pucch_res_idx - user_defined_pucch_cfg.nof_ue_pucch_f1_res_harq.to_uint() * + return pucch_res_idx - user_defined_pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq.to_uint() * user_defined_pucch_cfg.nof_cell_harq_pucch_res_sets; } @@ -405,7 +406,7 @@ unsigned du_pucch_resource_manager::pucch_res_idx_to_csi_du_res_idx(unsigned puc { // The mapping from the UE's PUCCH-Config \ref res_id index to the DU index for PUCCH CSI resource is the inverse of // what is defined in \ref srs_du::ue_pucch_config_builder. - return pucch_res_idx - (user_defined_pucch_cfg.nof_ue_pucch_f1_res_harq.to_uint() * + return pucch_res_idx - (user_defined_pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq.to_uint() * user_defined_pucch_cfg.nof_cell_harq_pucch_res_sets + user_defined_pucch_cfg.nof_sr_resources + user_defined_pucch_cfg.nof_ue_pucch_f2_res_harq.to_uint() * diff --git a/lib/du_manager/ran_resource_management/du_pucch_resource_manager.h b/lib/du_manager/ran_resource_management/du_pucch_resource_manager.h index cb2aacbfa7..5a620760ab 100644 --- a/lib/du_manager/ran_resource_management/du_pucch_resource_manager.h +++ b/lib/du_manager/ran_resource_management/du_pucch_resource_manager.h @@ -82,6 +82,8 @@ class du_pucch_resource_manager /// Multiple of SR and CSI periods. If SR and CSI results in having common offsets, this will be counted only once. std::set compute_sr_csi_pucch_offsets(unsigned sr_offset, unsigned csi_offset = 0); + [[nodiscard]] bool csi_offset_collides_with_sr(unsigned sr_offset, unsigned csi_offset) const; + // Parameters for PUCCH configuration passed by the user. const pucch_builder_params user_defined_pucch_cfg; const std::vector default_pucch_res_list; 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 ba85cc5d00..1b42aaf89f 100644 --- a/lib/du_manager/ran_resource_management/pucch_resource_generator.cpp +++ b/lib/du_manager/ran_resource_management/pucch_resource_generator.cpp @@ -71,6 +71,90 @@ static unsigned occ_cs_index_to_occ(unsigned occ_cs_idx, unsigned nof_css) return occ_cs_idx / nof_css; } +static std::vector compute_f0_res(unsigned nof_res_f0, pucch_f0_params params, unsigned bwp_size_rbs) +{ + // Compute the number of symbols and RBs for F0. + std::vector res_list; + const unsigned nof_f0_symbols = params.nof_symbols.to_uint(); + + // With intraslot freq. hopping. + if (params.intraslot_freq_hopping) { + for (unsigned rb_idx = 0; rb_idx < bwp_size_rbs / 2 - 1U; ++rb_idx) { + // Generate resource for increasing RB index, until the num. of required resources is reached. + const prb_interval prbs{rb_idx, rb_idx + 1U}; + // Pre-compute the second RBs for the frequency hop specularly to the first RBs. + const prb_interval freq_hop_prbs{bwp_size_rbs - 1U - rb_idx, bwp_size_rbs - rb_idx}; + + // Generate resource for increasing Symbol index, until the num. of required resources is reached. + for (unsigned sym_idx = 0; sym_idx + nof_f0_symbols <= NOF_OFDM_SYM_PER_SLOT_NORMAL_CP; + sym_idx += nof_f0_symbols) { + const ofdm_symbol_range symbols{sym_idx, sym_idx + nof_f0_symbols}; + + // Allocate resources for first hop. + res_list.emplace_back(pucch_grant{.format = srsran::pucch_format::FORMAT_0, + .symbols = symbols, + .prbs = prbs, + .freq_hop_grant = freq_hop_prbs}); + if (res_list.size() == nof_res_f0) { + break; + } + + // Allocate resources for second hop. + res_list.emplace_back(pucch_grant{.format = srsran::pucch_format::FORMAT_2, + .symbols = symbols, + .prbs = freq_hop_prbs, + .freq_hop_grant = prbs}); + if (res_list.size() == nof_res_f0) { + break; + } + } + if (res_list.size() == nof_res_f0) { + break; + } + } + } + // With intraslot freq. hopping. + else { + for (unsigned rb_idx = 0; rb_idx < bwp_size_rbs / 2 - 1U; ++rb_idx) { + const prb_interval prbs_low_spectrum{rb_idx, rb_idx + 1U}; + // Pre-compute the RBs for the resources to be allocated on the upper part of the spectrum. This is to achieve + // balancing of the PUCCH resources on both sides of the BWP. + const prb_interval prbs_hi_spectrum{bwp_size_rbs - 1U - rb_idx, bwp_size_rbs - rb_idx}; + + // Generate resource for increasing Symbol index, until the num. of required resources is reached. + for (unsigned sym_idx = 0; sym_idx + nof_f0_symbols <= NOF_OFDM_SYM_PER_SLOT_NORMAL_CP; + sym_idx += nof_f0_symbols) { + const ofdm_symbol_range symbols{sym_idx, sym_idx + nof_f0_symbols}; + res_list.emplace_back( + pucch_grant{.format = srsran::pucch_format::FORMAT_0, .symbols = symbols, .prbs = prbs_low_spectrum}); + if (res_list.size() == nof_res_f0) { + break; + } + } + if (res_list.size() == nof_res_f0) { + break; + } + + // Repeat the resource allocation on the upper part of the spectrum, to spread the PUCCH resource on both sides of + // the BWP. + for (unsigned sym_idx = 0; sym_idx + nof_f0_symbols <= 14; sym_idx += nof_f0_symbols) { + const ofdm_symbol_range symbols{sym_idx, sym_idx + nof_f0_symbols}; + res_list.emplace_back( + pucch_grant{.format = srsran::pucch_format::FORMAT_0, .symbols = symbols, .prbs = prbs_hi_spectrum}); + if (res_list.size() == nof_res_f0) { + break; + } + } + + if (res_list.size() == nof_res_f0) { + break; + } + } + } + + return res_list; +} + static std::vector compute_f1_res(unsigned nof_res_f1, pucch_f1_params params, unsigned bwp_size_rbs, unsigned nof_occ_css) { @@ -281,72 +365,49 @@ static std::vector compute_f2_res(unsigned nof_res_f2, pucch_f2_par return res_list; } -static std::tuple compute_nof_f1_f2_resources(unsigned max_pucch_f1_rbs, - unsigned max_pucch_f2_rbs, - pucch_f1_params f1_params, - pucch_f2_params f2_params) +error_type +srsran::srs_du::pucch_parameters_validator(unsigned nof_res_f0_f1, + unsigned nof_res_f2, + std::variant f0_f1_params, + pucch_f2_params f2_params, + unsigned bwp_size_rbs) { - // Compute the resources until max number of RBs or Number of requested resource is reached. - const unsigned nof_occ_codes = f1_params.occ_supported ? format1_symb_to_spreading_factor(f1_params.nof_symbols) : 1; - const unsigned nof_cs = format1_cp_step_to_uint(f1_params.nof_cyc_shifts); - - // Compute the number of symbols and PRBs for F2. - const unsigned nof_f2_symbols = f2_params.nof_symbols.to_uint(); - const unsigned f2_max_rbs = f2_params.max_payload_bits.has_value() - ? get_pucch_format2_max_nof_prbs(f2_params.max_payload_bits.value(), - nof_f2_symbols, - to_max_code_rate_float(f2_params.max_code_rate)) - : f2_params.max_nof_rbs; + const bool has_f0 = std::holds_alternative(f0_f1_params); + unsigned nof_f0_f1_rbs = 0; + + if (has_f0) { + const auto& f0_params = std::get(f0_f1_params); + // > If intraslot_freq_hopping is enabled, check if PUCCH Format 0 has more than symbol. + if (has_f0 and f0_params.intraslot_freq_hopping and f0_params.nof_symbols == 1) { + return make_unexpected("Intra-slot frequency hopping for PUCCH Format 0 requires 2 symbols"); + } - if (f2_max_rbs > pucch_constants::FORMAT2_MAX_NPRB) { - return {}; + // We define a block as a set of Resources (either F0/F1 or F2) aligned over the same starting PRB. + const unsigned nof_f0_per_block = NOF_OFDM_SYM_PER_SLOT_NORMAL_CP / f0_params.nof_symbols.to_uint(); + nof_f0_f1_rbs = + static_cast(std::ceil(static_cast(nof_res_f0_f1) / static_cast(nof_f0_per_block))); + } else { + const auto& f1_params = std::get(f0_f1_params); + // > Compute the number of RBs required for the PUCCH Format 1 resources. + const unsigned nof_occ_codes = + f1_params.occ_supported ? format1_symb_to_spreading_factor(f1_params.nof_symbols) : 1; + + // We define a block as a set of Resources (either F0/F1 or F2) aligned over the same starting PRB. + const unsigned nof_f1_per_block = nof_occ_codes * format1_cp_step_to_uint(f1_params.nof_cyc_shifts) * + (NOF_OFDM_SYM_PER_SLOT_NORMAL_CP / f1_params.nof_symbols.to_uint()); + nof_f0_f1_rbs = + static_cast(std::ceil(static_cast(nof_res_f0_f1) / static_cast(nof_f1_per_block))); + // With intraslot_freq_hopping, the nof of RBs is an even number. + if (f1_params.intraslot_freq_hopping) { + nof_f0_f1_rbs = static_cast(std::ceil(static_cast(nof_f0_f1_rbs) / 2.0F)) * 2; + } } - // For intraslot_freq_hopping, double the RB occupancy for the resources. - const unsigned rbs_per_f1_res = f1_params.intraslot_freq_hopping ? 2 : 1; - // With interslot frequency hopping, we need to multiply the nof resources by 2 (to compensate the double RBs - // occupancy). This is because each F1 resource is spread over 2 RBs, but only occupies roughly half of the symbols - // over the same RB. - const unsigned f1_freq_hop_res_multiplier = f1_params.intraslot_freq_hopping ? 2 : 1; - const unsigned nof_f1_res = f1_freq_hop_res_multiplier * nof_occ_codes * nof_cs * - (NOF_OFDM_SYM_PER_SLOT_NORMAL_CP / f1_params.nof_symbols.to_uint()) * - (max_pucch_f1_rbs / rbs_per_f1_res); - - const unsigned rbs_per_f2_res = f2_params.intraslot_freq_hopping ? 2 * f2_max_rbs : f2_max_rbs; - // With interslot frequency hopping, we need to multiply the nof resources by 2 (to compensate the double RBs - // occupancy). This is because each F2 resource is spread over 2*f2_max_rbs RBs, but only occupies roughly half of the - // symbols over the same RBs. - const unsigned f2_freq_hop_res_multiplier = f2_params.intraslot_freq_hopping ? 2 : 1; - const unsigned nof_f2_res = f2_freq_hop_res_multiplier * (NOF_OFDM_SYM_PER_SLOT_NORMAL_CP / nof_f2_symbols) * - (max_pucch_f2_rbs / rbs_per_f2_res); - - return std::tuple{nof_f1_res, nof_f2_res}; -} - -error_type srsran::srs_du::pucch_parameters_validator(unsigned nof_res_f1, - unsigned nof_res_f2, - pucch_f1_params f1_params, - pucch_f2_params f2_params, - unsigned bwp_size_rbs) -{ // > 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 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. - const unsigned nof_occ_codes = f1_params.occ_supported ? format1_symb_to_spreading_factor(f1_params.nof_symbols) : 1; - - // We define a block as a set of Resources (either F1 or F2) aligned over the same starting PRB. - const unsigned nof_f1_per_block = nof_occ_codes * format1_cp_step_to_uint(f1_params.nof_cyc_shifts) * - (NOF_OFDM_SYM_PER_SLOT_NORMAL_CP / f1_params.nof_symbols.to_uint()); - auto nof_f1_rbs = - static_cast(std::ceil(static_cast(nof_res_f1) / static_cast(nof_f1_per_block))); - // With intraslot_freq_hopping, the nof of RBs is an even number. - if (f1_params.intraslot_freq_hopping) { - nof_f1_rbs = static_cast(std::ceil(static_cast(nof_f1_rbs) / 2.0F)) * 2; - } - const unsigned f2_max_rbs = f2_params.max_payload_bits.has_value() ? get_pucch_format2_max_nof_prbs(f2_params.max_payload_bits.value(), f2_params.nof_symbols.to_uint(), @@ -368,66 +429,108 @@ error_type srsran::srs_du::pucch_parameters_validator(unsigned // Verify the number of RBs for the PUCCH resources does not exceed the BWP size. // [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) { + if (static_cast(nof_f0_f1_rbs + nof_f2_rbs) / static_cast(bwp_size_rbs) >= max_allowed_prbs_usage) { return make_unexpected("With the given parameters, the number of PRBs for PUCCH exceeds the 60% of the BWP PRBs"); } return {}; } -static std::vector merge_f1_f2_resource_lists(const std::vector& pucch_f1_resource_list, - const std::vector& pucch_f2_resource_list, - unsigned nof_cs, - unsigned bwp_size_rbs) +static std::vector +merge_f0_f1_f2_resource_lists(const std::vector& pucch_f0_f1_resource_list, + const std::vector& pucch_f2_resource_list, + std::optional nof_cs, + unsigned bwp_size_rbs) { - // This function merges the lists of PUCCH F1 and F2 resource. It first allocates the F1 resources on the sides of the - // BWP; second, it allocates the F2 resources beside F1 ones. + // This function merges the lists of PUCCH F0/F1 and F2 resource. It first allocates the F0/F1 resources on the sides + // of the BWP; second, it allocates the F2 resources beside F0/F1 ones. std::vector resource_list; + const bool has_f0 = not nof_cs.has_value(); + + // NOTE: PUCCH F0/F1 resource are located at the sides of the BWP. PUCCH F2 are located beside the F0/F1 resources, + // specifically on F0/F1's right (on the frequency axis) for frequencies < BWP/2, and F0/F1's left (on the frequency + // axis) for frequencies > BWP/2 and < BWP. + unsigned f0_f1_rbs_occupancy_low_freq = 0; + unsigned f0_f1_rbs_occupancy_hi_freq = 0; + + if (has_f0) { + for (const auto& res_f0 : pucch_f0_f1_resource_list) { + auto res_id = static_cast(resource_list.size()); + // No need to set res_id.second, which is the PUCCH resource ID for the ASN1 message. This will be set by the DU + // before assigning the resources to the UE. + pucch_resource res{.res_id = {res_id, 0}, .starting_prb = res_f0.prbs.start()}; + if (res_f0.freq_hop_grant.has_value()) { + res.second_hop_prb.emplace(res_f0.freq_hop_grant.value().start()); + } + pucch_format_0_cfg format0{.initial_cyclic_shift = 0U, .starting_sym_idx = res_f0.symbols.start()}; + + // Update the frequency shift for PUCCH F2. + if (res_f0.prbs.start() < bwp_size_rbs / 2 - 1U) { + // f0_f1_rbs_occupancy_low_freq accounts for the PUCCH F0/F1 resource occupancy on the first half of the BWP; + // PUCCH F0/F1 resources are located on the lowest RBs indices. + f0_f1_rbs_occupancy_low_freq = std::max(f0_f1_rbs_occupancy_low_freq, res_f0.prbs.start() + 1); + if (res_f0.freq_hop_grant.has_value()) { + f0_f1_rbs_occupancy_hi_freq = + std::max(f0_f1_rbs_occupancy_hi_freq, bwp_size_rbs - res_f0.freq_hop_grant.value().start()); + } + } else if (res_f0.prbs.start() > bwp_size_rbs / 2) { + // f0_f1_rbs_occupancy_hi_freq accounts for the PUCCH F0/F1 resource occupancy on the second half of the BWP; + // PUCCH F0/F1 resources are located on the highest RBs indices. + f0_f1_rbs_occupancy_hi_freq = std::max(f0_f1_rbs_occupancy_hi_freq, bwp_size_rbs - res_f0.prbs.start()); + if (res_f0.freq_hop_grant.has_value()) { + f0_f1_rbs_occupancy_low_freq = std::max(f0_f1_rbs_occupancy_low_freq, res_f0.freq_hop_grant.value().start()); + } + } else { + srsran_assert(false, "PUCCH resources are not expected to be allocated at the centre of the BWP"); + return {}; + } - // NOTE: PUCCH F1 resource are located at the sides of the BWP. PUCCH F2 are located beside the F1 resources, - // specifically on F1's right (on the frequency axis) for frequencies < BWP/2, and F1's left (on the frequency axis) - // for frequencies > BWP/2 and < BWP. - unsigned f1_rbs_occupancy_low_freq = 0; - unsigned f1_rbs_occupancy_hi_freq = 0; - for (const auto& res_f1 : pucch_f1_resource_list) { - auto res_id = static_cast(resource_list.size()); - // No need to set res_id.second, which is the PUCCH resource ID for the ASN1 message. This will be set by the DU - // before assigning the resources to the UE. - pucch_resource res{.res_id = {res_id, 0}, .starting_prb = res_f1.prbs.start()}; - if (res_f1.freq_hop_grant.has_value()) { - res.second_hop_prb.emplace(res_f1.freq_hop_grant.value().start()); + format0.nof_symbols = res_f0.symbols.length(); + res.format_params.emplace(format0); + res.format = pucch_format::FORMAT_0; + resource_list.emplace_back(res); } - pucch_format_1_cfg format1{.starting_sym_idx = res_f1.symbols.start()}; - - // Update the frequency shift for PUCCH F2. - if (res_f1.prbs.start() < bwp_size_rbs / 2 - 1) { - // f1_rbs_occupancy_low_freq accounts for the PUCCH F1 resource occupancy on the first half of the BWP; PUCCH F1 - // resources are located on the lowest RBs indices. - f1_rbs_occupancy_low_freq = std::max(f1_rbs_occupancy_low_freq, res_f1.prbs.start() + 1); + } else { + for (const auto& res_f1 : pucch_f0_f1_resource_list) { + auto res_id = static_cast(resource_list.size()); + // No need to set res_id.second, which is the PUCCH resource ID for the ASN1 message. This will be set by the DU + // before assigning the resources to the UE. + pucch_resource res{.res_id = {res_id, 0}, .starting_prb = res_f1.prbs.start()}; if (res_f1.freq_hop_grant.has_value()) { - f1_rbs_occupancy_hi_freq = - std::max(f1_rbs_occupancy_hi_freq, bwp_size_rbs - res_f1.freq_hop_grant.value().start()); + res.second_hop_prb.emplace(res_f1.freq_hop_grant.value().start()); } - } else if (res_f1.prbs.start() > bwp_size_rbs / 2) { - // f1_rbs_occupancy_hi_freq accounts for the PUCCH F1 resource occupancy on the second half of the BWP; PUCCH F1 - // resources are located on the highest RBs indices. - f1_rbs_occupancy_hi_freq = std::max(f1_rbs_occupancy_hi_freq, bwp_size_rbs - res_f1.prbs.start()); - if (res_f1.freq_hop_grant.has_value()) { - f1_rbs_occupancy_low_freq = std::max(f1_rbs_occupancy_low_freq, res_f1.freq_hop_grant.value().start()); + pucch_format_1_cfg format1{.starting_sym_idx = res_f1.symbols.start()}; + + // Update the frequency shift for PUCCH F2. + if (res_f1.prbs.start() < bwp_size_rbs / 2 - 1) { + // f0_f1_rbs_occupancy_low_freq accounts for the PUCCH F0/F1 resource occupancy on the first half of the BWP; + // PUCCH F0/F1 resources are located on the lowest RBs indices. + f0_f1_rbs_occupancy_low_freq = std::max(f0_f1_rbs_occupancy_low_freq, res_f1.prbs.start() + 1); + if (res_f1.freq_hop_grant.has_value()) { + f0_f1_rbs_occupancy_hi_freq = + std::max(f0_f1_rbs_occupancy_hi_freq, bwp_size_rbs - res_f1.freq_hop_grant.value().start()); + } + } else if (res_f1.prbs.start() > bwp_size_rbs / 2) { + // f0_f1_rbs_occupancy_hi_freq accounts for the PUCCH F0/F1 resource occupancy on the second half of the BWP; + // PUCCH F0/F1 resources are located on the highest RBs indices. + f0_f1_rbs_occupancy_hi_freq = std::max(f0_f1_rbs_occupancy_hi_freq, bwp_size_rbs - res_f1.prbs.start()); + if (res_f1.freq_hop_grant.has_value()) { + f0_f1_rbs_occupancy_low_freq = std::max(f0_f1_rbs_occupancy_low_freq, res_f1.freq_hop_grant.value().start()); + } + } else { + srsran_assert(false, "PUCCH resources are not expected to be allocated at the centre of the BWP"); + return {}; } - } else { - srsran_assert(false, "PUCCH resources are not expected to be allocated at the centre of the BWP"); - return {}; - } - format1.nof_symbols = res_f1.symbols.length(); - srsran_assert(res_f1.occ_cs_idx.has_value(), - "The index needed to compute OCC code and cyclic shift have not been found"); - format1.initial_cyclic_shift = occ_cs_index_to_cyclic_shift(res_f1.occ_cs_idx.value(), nof_cs); - format1.time_domain_occ = occ_cs_index_to_occ(res_f1.occ_cs_idx.value(), nof_cs); - res.format_params.emplace(format1); - res.format = pucch_format::FORMAT_1; - resource_list.emplace_back(res); + format1.nof_symbols = res_f1.symbols.length(); + srsran_assert(res_f1.occ_cs_idx.has_value(), + "The index needed to compute OCC code and cyclic shift have not been found"); + format1.initial_cyclic_shift = occ_cs_index_to_cyclic_shift(res_f1.occ_cs_idx.value(), nof_cs.value()); + format1.time_domain_occ = occ_cs_index_to_occ(res_f1.occ_cs_idx.value(), nof_cs.value()); + res.format_params.emplace(format1); + res.format = pucch_format::FORMAT_1; + resource_list.emplace_back(res); + } } for (const auto& res_f2 : pucch_f2_resource_list) { @@ -435,16 +538,16 @@ static std::vector merge_f1_f2_resource_lists(const std::vector< // No need to set res_id.second, which is the PUCCH resource ID for the ASN1 message. This will be set by the DU // before assigning the resources to the UE. pucch_resource res{.res_id = {res_id, 0}}; - // Shift F2 RBs depending on previously allocated F1 resources. + // Shift F2 RBs depending on previously allocated F0/F1 resources. if (res_f2.prbs.start() < bwp_size_rbs / 2 - res_f2.prbs.length()) { - res.starting_prb = res_f2.prbs.start() + f1_rbs_occupancy_low_freq; + res.starting_prb = res_f2.prbs.start() + f0_f1_rbs_occupancy_low_freq; if (res_f2.freq_hop_grant.has_value()) { - res.second_hop_prb.emplace(res_f2.freq_hop_grant.value().start() - f1_rbs_occupancy_hi_freq); + res.second_hop_prb.emplace(res_f2.freq_hop_grant.value().start() - f0_f1_rbs_occupancy_hi_freq); } } else if (res_f2.prbs.start() > bwp_size_rbs / 2) { - res.starting_prb = res_f2.prbs.start() - f1_rbs_occupancy_hi_freq; + res.starting_prb = res_f2.prbs.start() - f0_f1_rbs_occupancy_hi_freq; if (res_f2.freq_hop_grant.has_value()) { - res.second_hop_prb.emplace(res_f2.freq_hop_grant.value().start() + f1_rbs_occupancy_low_freq); + res.second_hop_prb.emplace(res_f2.freq_hop_grant.value().start() + f0_f1_rbs_occupancy_low_freq); } } else { srsran_assert(false, "PUCCH resources are not expected to be allocated at the centre of the BWP"); @@ -461,57 +564,50 @@ static std::vector merge_f1_f2_resource_lists(const std::vector< return resource_list; } -std::vector srsran::srs_du::generate_cell_pucch_res_list_given_rbs(unsigned max_pucch_f1_rbs, - unsigned max_pucch_f2_rbs, - pucch_f1_params f1_params, - pucch_f2_params f2_params, - unsigned bwp_size_rbs) +std::vector +srsran::srs_du::generate_cell_pucch_res_list(unsigned nof_res_f0_f1, + unsigned nof_res_f2, + std::variant f0_f1_params, + pucch_f2_params f2_params, + unsigned bwp_size_rbs) { - // Compute the number of resources that can be allocated with the given RBs. - const std::tuple nof_pucch_res = - compute_nof_f1_f2_resources(max_pucch_f1_rbs, max_pucch_f2_rbs, f1_params, f2_params); - // If the available RBs are not enough to allocate any resource, the function will return an empty list. - if (max_pucch_f1_rbs > 0 and std::get<0>(nof_pucch_res) == 0) { - return {}; - } - if (max_pucch_f2_rbs > 0 and std::get<1>(nof_pucch_res) == 0) { - return {}; - } - return generate_cell_pucch_res_list( - std::get<0>(nof_pucch_res), std::get<1>(nof_pucch_res), f1_params, f2_params, bwp_size_rbs); -} - -std::vector srsran::srs_du::generate_cell_pucch_res_list(unsigned nof_res_f1, - unsigned nof_res_f2, - pucch_f1_params f1_params, - pucch_f2_params f2_params, - unsigned bwp_size_rbs) -{ - auto outcome = pucch_parameters_validator(nof_res_f1, nof_res_f2, f1_params, f2_params, bwp_size_rbs); + auto outcome = pucch_parameters_validator(nof_res_f0_f1, nof_res_f2, f0_f1_params, f2_params, bwp_size_rbs); if (not outcome.has_value()) { srsran_assertion_failure("The cell list could not be generated due to: {}", outcome.error()); return {}; } - const unsigned nof_occ_codes = f1_params.occ_supported ? format1_symb_to_spreading_factor(f1_params.nof_symbols) : 1; - const unsigned nof_css = format1_cp_step_to_uint(f1_params.nof_cyc_shifts); + const bool has_f0 = std::holds_alternative(f0_f1_params); + + // Compute the PUCCH F0/F1 and F2 resources separately. + std::vector pucch_f0_f1_resource_list; + unsigned nof_css = 0; + if (has_f0 and nof_res_f0_f1 > 0) { + const pucch_f0_params f0_params = std::get(f0_f1_params); + pucch_f0_f1_resource_list = compute_f0_res(nof_res_f0_f1, f0_params, bwp_size_rbs); + } else if (nof_res_f0_f1 > 0) { + const pucch_f1_params f1_params = std::get(f0_f1_params); + const unsigned nof_occ_codes = + f1_params.occ_supported ? format1_symb_to_spreading_factor(f1_params.nof_symbols) : 1; + nof_css = format1_cp_step_to_uint(f1_params.nof_cyc_shifts); + pucch_f0_f1_resource_list = compute_f1_res(nof_res_f0_f1, f1_params, bwp_size_rbs, nof_css * nof_occ_codes); + } - // Compute the PUCCH F1 and F2 resources separately. - const std::vector pucch_f1_resource_list = - nof_res_f1 > 0 ? compute_f1_res(nof_res_f1, f1_params, bwp_size_rbs, nof_css * nof_occ_codes) - : std::vector{}; const std::vector pucch_f2_resource_list = nof_res_f2 > 0 ? compute_f2_res(nof_res_f2, f2_params, bwp_size_rbs) : std::vector{}; - return merge_f1_f2_resource_lists(pucch_f1_resource_list, pucch_f2_resource_list, nof_css, bwp_size_rbs); + return merge_f0_f1_f2_resource_lists(pucch_f0_f1_resource_list, + pucch_f2_resource_list, + has_f0 ? std::nullopt : std::optional{nof_css}, + bwp_size_rbs); } -static unsigned cell_res_list_validator(const std::vector& res_list, - bounded_integer nof_ue_pucch_f1_res_harq, - bounded_integer nof_ue_pucch_f2_res_harq, - unsigned nof_harq_pucch_cfgs, - unsigned nof_cell_pucch_f1_res_sr, - unsigned nof_cell_pucch_f2_res_csi) +static unsigned cell_res_list_validator(const std::vector& res_list, + bounded_integer nof_ue_pucch_f1_res_harq, + bounded_integer nof_ue_pucch_f2_res_harq, + unsigned nof_harq_pucch_cfgs, + unsigned nof_cell_pucch_f1_res_sr, + unsigned nof_cell_pucch_f2_res_csi) { const unsigned FAILURE_CASE = 0U; @@ -525,63 +621,72 @@ static unsigned cell_res_list_validator(const std::vector& return cnt; }; + const unsigned tot_nof_f0_res = count_resources(pucch_format::FORMAT_0); const unsigned tot_nof_f1_res = count_resources(pucch_format::FORMAT_1); const unsigned tot_nof_f2_res = count_resources(pucch_format::FORMAT_2); - if (tot_nof_f1_res + tot_nof_f2_res != res_list.size()) { + if (tot_nof_f0_res != 0 and tot_nof_f1_res != 0) { + srsran_assertion_failure("The cell PUCCH resource list can contain either F0 or F1 PUCCH resources, but not both."); + return FAILURE_CASE; + } + + const unsigned tot_nof_f0_f1_res = tot_nof_f0_res + tot_nof_f1_res; + + if (tot_nof_f0_f1_res + tot_nof_f2_res != res_list.size()) { srsran_assertion_failure( - "The sum of F1 and F2 PUCCH resources must be equal to the cell PUCCH resource list size."); + "The sum of F0/F1 and F2 PUCCH resources must be equal to the cell PUCCH resource list size."); return FAILURE_CASE; } - if (tot_nof_f1_res < 2 or tot_nof_f2_res < 2) { - srsran_assertion_failure("The cell PUCCH resource list must contain at least 2 F1 and 2 F2 PUCCH resources."); + if (tot_nof_f0_f1_res < 2 or tot_nof_f2_res < 2) { + srsran_assertion_failure("The cell PUCCH resource list must contain at least 2 F0/F1 and 2 F2 PUCCH resources."); return FAILURE_CASE; } - if (nof_ue_pucch_f1_res_harq.to_uint() > tot_nof_f1_res - nof_cell_pucch_f1_res_sr or + if (nof_ue_pucch_f1_res_harq.to_uint() > tot_nof_f0_f1_res - nof_cell_pucch_f1_res_sr or nof_ue_pucch_f2_res_harq.to_uint() > tot_nof_f2_res - nof_cell_pucch_f2_res_csi) { srsran_assertion_failure( "The nof requested UE PUCCH resources is greater than the nof of resources available in the cell."); return FAILURE_CASE; } - if ((nof_ue_pucch_f1_res_harq.to_uint() * nof_harq_pucch_cfgs > tot_nof_f1_res - nof_cell_pucch_f1_res_sr) or + if ((nof_ue_pucch_f1_res_harq.to_uint() * nof_harq_pucch_cfgs > tot_nof_f0_f1_res - nof_cell_pucch_f1_res_sr) or (nof_ue_pucch_f2_res_harq.to_uint() * nof_harq_pucch_cfgs > tot_nof_f2_res - nof_cell_pucch_f2_res_csi)) { srsran_assertion_failure( "The cell PUCCH resource list doesn't contain enough resources to allocate all requested UEs."); return FAILURE_CASE; } - for (unsigned res_idx = 0; res_idx != tot_nof_f1_res; ++res_idx) { + for (unsigned res_idx = 0; res_idx != tot_nof_f0_f1_res; ++res_idx) { if (res_list[res_idx].format == pucch_format::FORMAT_2) { - srsran_assertion_failure("The F1 resources in the cell PUCCH resource list must precede all F2 resources."); + srsran_assertion_failure("The F0/F1 resources in the cell PUCCH resource list must precede all F2 resources."); return FAILURE_CASE; } } - return tot_nof_f1_res; + return tot_nof_f0_res != 0 ? tot_nof_f0_res : tot_nof_f1_res; } -bool srsran::srs_du::ue_pucch_config_builder(serving_cell_config& serv_cell_cfg, - const std::vector& res_list, - unsigned du_harq_set_idx, - unsigned du_sr_res_idx, - unsigned du_csi_res_idx, - bounded_integer nof_ue_pucch_f1_res_harq, - bounded_integer nof_ue_pucch_f2_res_harq, - unsigned nof_harq_pucch_sets, - unsigned nof_cell_pucch_f1_res_sr, - unsigned nof_cell_pucch_f2_res_csi) +bool srsran::srs_du::ue_pucch_config_builder( + serving_cell_config& serv_cell_cfg, + const std::vector& res_list, + unsigned du_harq_set_idx, + unsigned du_sr_res_idx, + unsigned du_csi_res_idx, + bounded_integer nof_ue_pucch_f0_f1_res_harq, + bounded_integer nof_ue_pucch_f2_res_harq, + unsigned nof_harq_pucch_sets, + unsigned nof_cell_pucch_f0_f1_res_sr, + unsigned nof_cell_pucch_f2_res_csi) { - const unsigned tot_nof_cell_f1_res = cell_res_list_validator(res_list, - nof_ue_pucch_f1_res_harq, - nof_ue_pucch_f2_res_harq, - nof_harq_pucch_sets, - nof_cell_pucch_f1_res_sr, - nof_cell_pucch_f2_res_csi); - - if (tot_nof_cell_f1_res == 0U) { + const unsigned tot_nof_cell_f0_f1_res = cell_res_list_validator(res_list, + nof_ue_pucch_f0_f1_res_harq, + nof_ue_pucch_f2_res_harq, + nof_harq_pucch_sets, + nof_cell_pucch_f0_f1_res_sr, + nof_cell_pucch_f2_res_csi); + + if (tot_nof_cell_f0_f1_res == 0U) { return false; } @@ -602,8 +707,8 @@ bool srsran::srs_du::ue_pucch_config_builder(serving_cell_config& pucch_res_set_idx::set_1; // Add F1 for HARQ. - const unsigned f1_idx_offset = (du_harq_set_idx % nof_harq_pucch_sets) * nof_ue_pucch_f1_res_harq.to_uint(); - for (unsigned ue_f1_cnt = 0; ue_f1_cnt < nof_ue_pucch_f1_res_harq.to_uint(); ++ue_f1_cnt) { + const unsigned f1_idx_offset = (du_harq_set_idx % nof_harq_pucch_sets) * nof_ue_pucch_f0_f1_res_harq.to_uint(); + for (unsigned ue_f1_cnt = 0; ue_f1_cnt < nof_ue_pucch_f0_f1_res_harq.to_uint(); ++ue_f1_cnt) { const auto& cell_res = res_list[ue_f1_cnt + f1_idx_offset]; // Add PUCCH resource to pucch_res_list. @@ -623,7 +728,7 @@ bool srsran::srs_du::ue_pucch_config_builder(serving_cell_config& // Add SR resource. const unsigned sr_res_idx = - nof_ue_pucch_f1_res_harq.to_uint() * nof_harq_pucch_sets + (du_sr_res_idx % nof_cell_pucch_f1_res_sr); + nof_ue_pucch_f0_f1_res_harq.to_uint() * nof_harq_pucch_sets + (du_sr_res_idx % nof_cell_pucch_f0_f1_res_sr); const auto& sr_cell_res = res_list[sr_res_idx]; pucch_cfg.pucch_res_list.emplace_back(pucch_resource{.res_id = {sr_cell_res.res_id.cell_res_id, ue_pucch_res_id}, .starting_prb = sr_cell_res.starting_prb, @@ -636,7 +741,7 @@ bool srsran::srs_du::ue_pucch_config_builder(serving_cell_config& // Add F2 for HARQ. const unsigned f2_idx_offset = - tot_nof_cell_f1_res + (du_harq_set_idx % nof_harq_pucch_sets) * nof_ue_pucch_f2_res_harq.to_uint(); + tot_nof_cell_f0_f1_res + (du_harq_set_idx % nof_harq_pucch_sets) * nof_ue_pucch_f2_res_harq.to_uint(); for (unsigned ue_f2_cnt = 0; ue_f2_cnt < nof_ue_pucch_f2_res_harq.to_uint(); ++ue_f2_cnt) { const auto& cell_res = res_list[f2_idx_offset + ue_f2_cnt]; pucch_cfg.pucch_res_list.emplace_back(pucch_resource{.res_id = {cell_res.res_id.cell_res_id, ue_pucch_res_id}, @@ -654,7 +759,7 @@ bool srsran::srs_du::ue_pucch_config_builder(serving_cell_config& if (serv_cell_cfg.csi_meas_cfg.has_value()) { // Add CSI resource. - const unsigned csi_res_idx = tot_nof_cell_f1_res + nof_ue_pucch_f2_res_harq.to_uint() * nof_harq_pucch_sets + + const unsigned csi_res_idx = tot_nof_cell_f0_f1_res + nof_ue_pucch_f2_res_harq.to_uint() * nof_harq_pucch_sets + (du_csi_res_idx % nof_cell_pucch_f2_res_csi); const auto& csi_cell_res = res_list[csi_res_idx]; pucch_cfg.pucch_res_list.emplace_back(pucch_resource{.res_id = {csi_cell_res.res_id.cell_res_id, ue_pucch_res_id}, diff --git a/lib/du_manager/ran_resource_management/pucch_resource_generator.h b/lib/du_manager/ran_resource_management/pucch_resource_generator.h index 59433d608d..dcf1708753 100644 --- a/lib/du_manager/ran_resource_management/pucch_resource_generator.h +++ b/lib/du_manager/ran_resource_management/pucch_resource_generator.h @@ -30,99 +30,77 @@ namespace srsran { namespace srs_du { /// The following values have to be set according to the \ref pucch_resource_manager capabilities. -/// Maximum number of PUCCH F1 resources per UE for HARQ-ACK reporting. -constexpr unsigned max_ue_f1_res_harq = 8; +/// Maximum number of PUCCH F0/F1 resources per UE for HARQ-ACK reporting. +constexpr unsigned max_ue_f0_f1_res_harq = 8; /// Maximum number of PUCCH F2 resources per UE for HARQ-ACK reporting. constexpr unsigned max_ue_f2_res_harq = 8; /// \brief Validates the user-defined parameters for building the PUCCH resource list. -/// \param[in] nof_res_f1 number of PUCCH F1 resources to be generated. +/// \param[in] nof_res_f1 number of PUCCH F0/F1 resources to be generated. /// \param[in] nof_res_f2 number of PUCCH F2 resources to be generated. -/// \param[in] f1_params PUCCH F1 resource parameters. +/// \param[in] f1_params PUCCH F0/F1 resource parameters. /// \param[in] f1_params PUCCH F2 resource parameters. /// \param[in] bwp_size_rbs Size of the BWP in RBs. /// \return In case an invalid parameter is detected, returns a string containing an error message. -error_type pucch_parameters_validator(unsigned nof_res_f1, - unsigned nof_res_f2, - pucch_f1_params f1_params, - pucch_f2_params f2_params, - unsigned bwp_size_rbs); +error_type pucch_parameters_validator(unsigned nof_res_f0_f1, + unsigned nof_res_f2, + std::variant f0_f1_params, + pucch_f2_params f2_params, + unsigned bwp_size_rbs); -/// \brief Generates the list of cell PUCCH resources (Format 1 and 2) given the available PRBs. +/// \brief Generates the list of cell PUCCH resources (Format 0/1 and 2) given the number of requested resources. /// -/// The function attempts to allocate the maximum number of resources given the number of PRBs. PUCCH resources F1 and -/// F2 are allocated on different RBs. The function attempts to spread the resources on both -///// sides of the BWP. -/// -/// \param[in] max_pucch_f1_rbs number of available RBs where to allocate PUCCH F1 resources. -/// \param[in] max_pucch_f2_rbs number of available RBs where to allocate PUCCH F2 resources. -/// \param[in] f1_params PUCCH F1 resource parameters. -/// \param[in] f1_params PUCCH F2 resource parameters. -/// \param[in] bwp_size_rbs Size of the BWP in RBs. -/// \return The list of PUCCH resources for a cell. The list has the PUCCH Format 1 resources in front of the list, and -/// the PUCCH Format 2 in the back of the list. -/// \remark The function returns an empty list in the following cases: (i) If the given number of RBs is not enough to -/// allocate at least 1 resource. (ii) If the RBs occupancy is larger than the BWP size. (iii) If F2 intra-slot -/// frequency hopping is enabled with only 1 symbol. -std::vector generate_cell_pucch_res_list_given_rbs(unsigned max_pucch_f1_rbs, - unsigned max_pucch_f2_rbs, - pucch_f1_params f1_params, - pucch_f2_params f2_params, - unsigned bwp_size_rbs); - -/// \brief Generates the list of cell PUCCH resources (Format 1 and 2) given the number of requested resources. -/// -/// PUCCH resources F1 and F2 are allocated on different RBs. The function attempts to spread the resources on both +/// PUCCH resources F0/F1 and F2 are allocated on different RBs. The function attempts to spread the resources on both /// sides of the BWP. /// -/// \param[in] nof_res_f1 number of PUCCH F1 resources to be generated. +/// \param[in] nof_res_f0_f1 number of PUCCH F0/F1 resources to be generated. /// \param[in] nof_res_f2 number of PUCCH F2 resources to be generated. -/// \param[in] f1_params PUCCH F1 resource parameters. +/// \param[in] f1_params PUCCH F0/F1 resource parameters. /// \param[in] f1_params PUCCH F2 resource parameters. /// \param[in] bwp_size_rbs Size of the BWP in RBs. -/// \return The list of PUCCH resources for a cell. The list has the PUCCH Format 1 resources in front of the list, and -/// the PUCCH Format 2 in the back of the list. +/// \return The list of PUCCH resources for a cell. The list has the PUCCH Format 0/1 resources in front of the list, +/// and the PUCCH Format 2 in the back of the list. /// \remark The function returns an empty list in the following cases: (i) If overall the RBs occupancy is larger than /// the BWP size. (ii) If F2 intra-slot frequency hopping is enabled with only 1 symbol. -std::vector generate_cell_pucch_res_list(unsigned nof_res_f1, - unsigned nof_res_f2, - pucch_f1_params f1_params, - pucch_f2_params f2_params, - unsigned bwp_size_rbs); +std::vector generate_cell_pucch_res_list(unsigned nof_res_f0_f1, + unsigned nof_res_f2, + std::variant f0_f1_params, + pucch_f2_params f2_params, + unsigned bwp_size_rbs); /// \brief Generates the list of PUCCH resources for a given UE. /// -/// This function generates the list of PUCCH F1 and F2 resources for a given UE, including the resources for HARQ-ACK -/// reporting, SR and CSI. It also updates the PUCCH resource sets accordingly, as well as the pointers to the PUCCH F1 -/// resource for SR and to the PUCCH F2 resource for CSI. -/// This function overwrites the default \c ServingCellConfig passed as a function input. +/// This function generates the list of PUCCH F0/F1 and F2 resources for a given UE, including the resources for +/// HARQ-ACK reporting, SR and CSI. It also updates the PUCCH resource sets accordingly, as well as the pointers to the +/// PUCCH F0/F1 resource for SR and to the PUCCH F2 resource for CSI. This function overwrites the default \c +/// ServingCellConfig passed as a function input. /// /// The UE's PUCCH resource list composed of: -/// - \ref nof_ue_pucch_f1_res_harq PUCCH Format 1 resources for HARQ-ACK reporting, chosen from -/// \ref nof_harq_pucch_sets possible sets of PUCCH Format 1 cell resources. -/// - 1 PUCCH Format 1 resource for SR chosen from \ref nof_cell_pucch_f1_res_sr possible sets of PUCCH Format 1 +/// - \ref nof_ue_pucch_f0_or_f1_res_harq PUCCH Format 0/1 resources for HARQ-ACK reporting, chosen from +/// \ref nof_harq_pucch_sets possible sets of PUCCH Format 0/1 cell resources. +/// - 1 PUCCH Format 0/1 resource for SR chosen from \ref nof_cell_pucch_f0_f1_res_sr possible sets of PUCCH Format 0/1 /// cell resources. -/// - \ref nof_ue_pucch_f1_res_harq PUCCH Format 2 resources for HARQ-ACK reporting, chosen from +/// - \ref nof_ue_pucch_f0_or_f1_res_harq PUCCH Format 2 resources for HARQ-ACK reporting, chosen from /// \ref nof_harq_pucch_sets possible sets of PUCCH Format 2 cell resources. -/// - 1 PUCCH Format 2 resource for CSI chosen from \ref nof_cell_pucch_f2_res_csi possible sets of PUCCH Format 1 +/// - 1 PUCCH Format 2 resource for CSI chosen from \ref nof_cell_pucch_f2_res_csi possible sets of PUCCH Format 0/1 /// cell resources. /// /// The returned UE PUCCH resource list \ref pucch_res_list contains the following resources, sorted as follows: -/// [ F1-HARQ_0 ... F1-HARQ_N-1 F1-SR F2-HARQ_0 ... F2-HARQ_M-1 F2-CSI ] -/// where N = nof_ue_pucch_f1_res_harq and M = nof_ue_pucch_f2_res_harq, +/// [ F0/F1-HARQ_0 ... F0/F1-HARQ_N-1 F0/F1-SR F2-HARQ_0 ... F2-HARQ_M-1 F2-CSI ] +/// where N = nof_ue_pucch_f0_or_f1_res_harq and M = nof_ue_pucch_f2_res_harq, /// and with the following indices \ref res_id: -/// - The first \ref nof_ue_pucch_f1_res_harq are the PUCCH F1 resources for HARQ-ACK and have index -/// [ (du_harq_set_idx % nof_harq_pucch_sets) * nof_ue_pucch_f1_res_harq, -/// (du_harq_set_idx % nof_harq_pucch_sets) * nof_ue_pucch_f1_res_harq + nof_ue_pucch_f1_res_harq ). -/// - The next resource in the list is the PUCCH F1 resource for SR, which have index: -/// nof_harq_pucch_sets * nof_ue_pucch_f1_res_harq + du_sr_res_idx % nof_cell_pucch_f1_res_sr. +/// - The first \ref nof_ue_pucch_f0_or_f1_res_harq are the PUCCH F0/F1 resources for HARQ-ACK and have index +/// [ (du_harq_set_idx % nof_harq_pucch_sets) * nof_ue_pucch_f0_or_f1_res_harq, +/// (du_harq_set_idx % nof_harq_pucch_sets) * nof_ue_pucch_f0_or_f1_res_harq + nof_ue_pucch_f0_or_f1_res_harq ). +/// - The next resource in the list is the PUCCH F0/F1 resource for SR, which have index: +/// nof_harq_pucch_sets * nof_ue_pucch_f0_or_f1_res_harq + du_sr_res_idx % nof_cell_pucch_f0_f1_res_sr. /// - The next \ref nof_ue_pucch_f2_res_harq are the PUCCH F2 resources for HARQ-ACK and have index -/// [ nof_harq_pucch_sets * nof_ue_pucch_f1_res_harq + nof_cell_pucch_f1_res_sr + +/// [ nof_harq_pucch_sets * nof_ue_pucch_f0_or_f1_res_harq + nof_cell_pucch_f0_f1_res_sr + /// (du_harq_set_idx % nof_harq_pucch_sets) * nof_ue_pucch_f2_res_harq, -/// nof_harq_pucch_sets * nof_ue_pucch_f1_res_harq + nof_cell_pucch_f1_res_sr + +/// nof_harq_pucch_sets * nof_ue_pucch_f0_or_f1_res_harq + nof_cell_pucch_f0_f1_res_sr + /// (du_harq_set_idx % nof_harq_pucch_sets) * nof_ue_pucch_f2_res_harq + nof_ue_pucch_f2_res_harq). /// - The last resource in the list is the PUCCH F2 resource for CSI, which has index: -//// nof_harq_pucch_sets * nof_ue_pucch_f1_res_harq + nof_cell_pucch_f1_res_sr + +//// nof_harq_pucch_sets * nof_ue_pucch_f0_or_f1_res_harq + nof_cell_pucch_f0_f1_res_sr + /// nof_ue_pucch_f2_res_harq * nof_harq_pucch_sets + du_csi_res_idx % nof_cell_pucch_f2_res_csi. /// /// \param[in,out] serv_cell_cfg default \c ServingCellConfig that will be overwritten by this function. @@ -131,23 +109,23 @@ std::vector generate_cell_pucch_res_list(unsigned nof_res /// \ref nof_harq_pucch_sets possible ones; the chosen set for this UE has index /// du_harq_set_idx % nof_harq_pucch_sets. /// \param[in] du_sr_res_idx defines which PUCCH resource for SR to be assigned to this UE among -/// \ref nof_cell_pucch_f1_res_sr possible ones. Values: {0, ..., nof_cell_pucch_f1_res_sr-1}. +/// \ref nof_cell_pucch_f0_f1_res_sr possible ones. Values: {0, ..., nof_cell_pucch_f0_f1_res_sr-1}. /// \param[in] du_csi_res_idx defines which PUCCH resource for CSI to be assigned to this UE among /// \ref nof_cell_pucch_f2_res_csi possible ones. Values: {0, ..., nof_cell_pucch_f2_res_csi-1}. -/// \param[in] nof_ue_pucch_f1_res_harq desired number of UE PUCCH F1 resources (HARQ-ACK) in UE configuration. +/// \param[in] nof_ue_pucch_f0_f1_res_harq desired number of UE PUCCH F0/F1 resources (HARQ-ACK) in UE configuration. /// \param[in] nof_ue_pucch_f2_res_harq desired number of UE PUCCH F2 resources (HARQ-ACK) in UE configuration. -/// \param[in] nof_cell_pucch_f1_res_sr number of PUCCH F1 resources for SR available in the cell. +/// \param[in] nof_cell_pucch_f0_f1_res_sr number of PUCCH F0/F1 resources for SR available in the cell. /// \param[in] nof_cell_pucch_f2_res_csi number of PUCCH F2 resources for CSI available in the cell. /// \return true if the building is successful, false otherwise. -bool ue_pucch_config_builder(serving_cell_config& serv_cell_cfg, - const std::vector& res_list, - unsigned du_harq_set_idx, - unsigned du_sr_res_idx, - unsigned du_csi_res_idx, - bounded_integer nof_ue_pucch_f1_res_harq, - bounded_integer nof_ue_pucch_f2_res_harq, - unsigned nof_harq_pucch_sets, - unsigned nof_cell_pucch_f1_res_sr, - unsigned nof_cell_pucch_f2_res_csi = 1); +bool ue_pucch_config_builder(serving_cell_config& serv_cell_cfg, + const std::vector& res_list, + unsigned du_harq_set_idx, + unsigned du_sr_res_idx, + unsigned du_csi_res_idx, + bounded_integer nof_ue_pucch_f0_f1_res_harq, + bounded_integer nof_ue_pucch_f2_res_harq, + unsigned nof_harq_pucch_sets, + unsigned nof_cell_pucch_f0_f1_res_sr, + unsigned nof_cell_pucch_f2_res_csi = 1); } // namespace srs_du } // namespace srsran diff --git a/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp b/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp index c3c0c3490d..686cda4546 100644 --- a/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp +++ b/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp @@ -69,32 +69,23 @@ du_setup_request srsran::srs_cu_cp::create_du_setup_request(const asn1::f1ap::f1 cu_cp_du_served_cells_item 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; - - // 5GS TAC if (asn1_served_cell.served_cell_info.five_gs_tac_present) { served_cell.served_cell_info.five_gs_tac = asn1_served_cell.served_cell_info.five_gs_tac.to_number(); } - - // cfg EPS TAC if (asn1_served_cell.served_cell_info.cfg_eps_tac_present) { served_cell.served_cell_info.cfg_eps_tac = asn1_served_cell.served_cell_info.cfg_eps_tac.to_number(); } - - // served PLMNs for (const auto& asn1_plmn : asn1_served_cell.served_cell_info.served_plmns) { - served_cell.served_cell_info.served_plmns.push_back(asn1_plmn.plmn_id.to_string()); + auto result = plmn_identity::from_bytes(asn1_plmn.plmn_id.to_bytes()); + // Note: If the ASN.1 PLMN ID is not valid, it is not considered in the response back to the DU. + if (result.has_value()) { + served_cell.served_cell_info.served_plmns.push_back(result.value()); + } } - - // NR mode info served_cell.served_cell_info.nr_mode_info = f1ap_asn1_to_nr_mode_info(asn1_served_cell.served_cell_info.nr_mode_info); - - // meas timing cfg served_cell.served_cell_info.meas_timing_cfg = asn1_served_cell.served_cell_info.meas_timing_cfg.copy(); // GNB DU sys info @@ -186,7 +177,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 (not msgerr.has_value()) { - logger.info("Rejecting F1 Setup Request. Cause: {}", msgerr.error().second); + logger.warning("Rejecting F1 Setup Request. Cause: {}", msgerr.error().second); pdu_notifier.on_new_message(create_f1_setup_reject(request, msgerr.error().first)); return; } @@ -204,7 +195,7 @@ void srsran::srs_cu_cp::handle_f1_setup_procedure(const asn1::f1ap::f1_setup_req if (not request_outcome.is_accepted()) { // Failed to setup DU case. auto& fail_resp = std::get(request_outcome.result); - logger.info("Rejecting F1 Setup Request. Cause: {}", fail_resp.cause_str); + logger.warning("Rejecting F1 Setup Request. Cause: {}", fail_resp.cause_str); f1ap_msg = create_f1_setup_reject(request, cause_to_asn1(fail_resp.cause)); } else { // DU has been accepted. diff --git a/lib/mac/CMakeLists.txt b/lib/mac/CMakeLists.txt index 8130390ed7..c95e2dbb63 100644 --- a/lib/mac/CMakeLists.txt +++ b/lib/mac/CMakeLists.txt @@ -38,6 +38,7 @@ set(SOURCES mac_factory.cpp mac_ul/mac_ul_sch_pdu.cpp mac_ul/ul_bsr.cpp mac_ul/pdu_rx_handler.cpp + mac_ul/mac_ul_processor.cpp mac_ul/mac_ul_ue_manager.cpp mac_sched/srsran_scheduler_adapter.cpp mac_sched/uci_cell_decoder.cpp) diff --git a/lib/mac/mac_ul/mac_ul_processor.cpp b/lib/mac/mac_ul/mac_ul_processor.cpp new file mode 100644 index 0000000000..29fd6a516f --- /dev/null +++ b/lib/mac/mac_ul/mac_ul_processor.cpp @@ -0,0 +1,109 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "mac_ul_processor.h" +#include "srsran/srslog/srslog.h" + +using namespace srsran; + +mac_ul_processor::mac_ul_processor(const mac_ul_config& cfg_) : + cfg(cfg_), + logger(srslog::fetch_basic_logger("MAC")), + ue_manager(cfg.rnti_table), + pdu_handler(cfg.ul_ccch_notifier, cfg.ue_exec_mapper, cfg.sched, ue_manager, cfg.rnti_table, cfg.pcap) +{ +} + +async_task mac_ul_processor::add_ue(const mac_ue_create_request& request) +{ + // Update UE executor to match new PCell. + cfg.ue_exec_mapper.rebind_executors(request.ue_index, request.cell_index); + task_executor& ul_exec = cfg.ue_exec_mapper.ctrl_executor(request.ue_index); + + // Dispatch UE creation task to new UL executor. + return execute_and_continue_on_blocking( + ul_exec, cfg.ctrl_exec, [this, request]() { return ue_manager.add_ue(request); }); +} + +async_task mac_ul_processor::addmod_bearers(du_ue_index_t ue_index, + const std::vector& ul_logical_channels) +{ + return execute_and_continue_on_blocking( + cfg.ue_exec_mapper.ctrl_executor(ue_index), cfg.ctrl_exec, [this, ue_index, ul_logical_channels]() { + return ue_manager.addmod_bearers(ue_index, ul_logical_channels); + }); +} + +async_task mac_ul_processor::remove_bearers(du_ue_index_t ue_index, span lcids_to_rem) +{ + std::vector lcids(lcids_to_rem.begin(), lcids_to_rem.end()); + return execute_and_continue_on_blocking( + cfg.ue_exec_mapper.ctrl_executor(ue_index), cfg.ctrl_exec, [this, ue_index, lcids = std::move(lcids)]() { + return ue_manager.remove_bearers(ue_index, lcids); + }); +} + +async_task mac_ul_processor::remove_ue(const mac_ue_delete_request& msg) +{ + return execute_and_continue_on_blocking(cfg.ue_exec_mapper.ctrl_executor(msg.ue_index), + cfg.ctrl_exec, + [this, ue_index = msg.ue_index]() { ue_manager.remove_ue(ue_index); }); +} + +bool mac_ul_processor::flush_ul_ccch_msg(du_ue_index_t ue_index, byte_buffer ccch_pdu) +{ + if (not cfg.ue_exec_mapper.ctrl_executor(ue_index).execute([this, ue_index, pdu = std::move(ccch_pdu)]() mutable { + pdu_handler.push_ul_ccch_msg(ue_index, std::move(pdu)); + })) { + logger.warning("ue={}: Unable to forward UL-CCCH message to upper layers. Cause: task queue is full.", ue_index); + // Note: The UE is not yet created in the CU, so there in no inactivity timer. + return false; + } + return true; +} + +void mac_ul_processor::handle_rx_data_indication(mac_rx_data_indication msg) +{ + for (mac_rx_pdu& pdu : msg.pdus) { + if (pdu.pdu.empty()) { + logger.error("cell={} slot_rx={} rnti={}: Received empty MAC RX PDU from lower layers", + msg.cell_index, + msg.sl_rx, + pdu.rnti); + continue; + } + + // > Convert C-RNTI to DU-specific UE index. + // Note: for Msg3, the UE context is not yet created, and ue_index will be an invalid index. This situation is + // handled inside the pdu_handler. + du_ue_index_t ue_index = cfg.rnti_table[pdu.rnti]; + + // > Fork each PDU handling to different executors based on the PDU RNTI. + if (not cfg.ue_exec_mapper.mac_ul_pdu_executor(ue_index).execute( + [this, slot_rx = msg.sl_rx, cell_idx = msg.cell_index, pdu = std::move(pdu)]() mutable { + // > Decode Rx PDU and handle respective subPDUs. + pdu_handler.handle_rx_pdu(slot_rx, cell_idx, std::move(pdu)); + })) { + logger.warning("cell={} slot_rx={}: Discarding Rx PDU. Cause: Rx task queue is full.", msg.cell_index, msg.sl_rx); + } + } +} diff --git a/lib/mac/mac_ul/mac_ul_processor.h b/lib/mac/mac_ul/mac_ul_processor.h index 90c4116f5c..6b7fa2a963 100644 --- a/lib/mac/mac_ul/mac_ul_processor.h +++ b/lib/mac/mac_ul/mac_ul_processor.h @@ -45,91 +45,22 @@ struct mac_ul_config { class mac_ul_processor final : public mac_ul_configurator, public mac_pdu_handler { public: - mac_ul_processor(const mac_ul_config& cfg_) : - cfg(cfg_), - logger(srslog::fetch_basic_logger("MAC")), - ue_manager(cfg.rnti_table), - pdu_handler(cfg.ul_ccch_notifier, cfg.ue_exec_mapper, cfg.sched, ue_manager, cfg.rnti_table, cfg.pcap) - { - } + mac_ul_processor(const mac_ul_config& cfg_); - async_task add_ue(const mac_ue_create_request& request) override - { - // Update UE executor to match new PCell. - cfg.ue_exec_mapper.rebind_executors(request.ue_index, request.cell_index); - task_executor& ul_exec = cfg.ue_exec_mapper.ctrl_executor(request.ue_index); + async_task add_ue(const mac_ue_create_request& request) override; - // Dispatch UE creation task to new UL executor. - return execute_and_continue_on_blocking( - ul_exec, cfg.ctrl_exec, [this, request]() { return ue_manager.add_ue(request); }); - } + async_task addmod_bearers(du_ue_index_t ue_index, + const std::vector& ul_logical_channels) override; - virtual async_task addmod_bearers(du_ue_index_t ue_index, - const std::vector& ul_logical_channels) override - { - return execute_and_continue_on_blocking( - cfg.ue_exec_mapper.ctrl_executor(ue_index), cfg.ctrl_exec, [this, ue_index, ul_logical_channels]() { - return ue_manager.addmod_bearers(ue_index, ul_logical_channels); - }); - } + async_task remove_bearers(du_ue_index_t ue_index, span lcids_to_rem) override; - async_task remove_bearers(du_ue_index_t ue_index, span lcids_to_rem) override - { - std::vector lcids(lcids_to_rem.begin(), lcids_to_rem.end()); - return execute_and_continue_on_blocking( - cfg.ue_exec_mapper.ctrl_executor(ue_index), cfg.ctrl_exec, [this, ue_index, lcids = std::move(lcids)]() { - return ue_manager.remove_bearers(ue_index, lcids); - }); - } + async_task remove_ue(const mac_ue_delete_request& msg) override; - async_task remove_ue(const mac_ue_delete_request& msg) override - { - return execute_and_continue_on_blocking(cfg.ue_exec_mapper.ctrl_executor(msg.ue_index), - cfg.ctrl_exec, - [this, ue_index = msg.ue_index]() { ue_manager.remove_ue(ue_index); }); - } - - bool flush_ul_ccch_msg(du_ue_index_t ue_index, byte_buffer ccch_pdu) override - { - if (not cfg.ue_exec_mapper.ctrl_executor(ue_index).execute([this, ue_index, pdu = std::move(ccch_pdu)]() mutable { - pdu_handler.push_ul_ccch_msg(ue_index, std::move(pdu)); - })) { - logger.warning("ue={}: Unable to forward UL-CCCH message to upper layers. Cause: task queue is full.", ue_index); - // Note: The UE is not yet created in the CU, so there in no inactivity timer. - return false; - } - return true; - } + bool flush_ul_ccch_msg(du_ue_index_t ue_index, byte_buffer ccch_pdu) override; /// Handles FAPI Rx_Data.Indication. /// The PDUs contained in the Rx_Data.Indication are dispatched to different executors, depending on their RNTI. - void handle_rx_data_indication(mac_rx_data_indication msg) override - { - for (mac_rx_pdu& pdu : msg.pdus) { - if (pdu.pdu.empty()) { - logger.error("cell={} slot_rx={} rnti={}: Received empty MAC RX PDU from lower layers", - msg.cell_index, - msg.sl_rx, - pdu.rnti); - continue; - } - - // > Convert C-RNTI to DU-specific UE index. - // Note: for Msg3, the UE context is not yet created, and ue_index will be an invalid index. This situation is - // handled inside the pdu_handler. - du_ue_index_t ue_index = cfg.rnti_table[pdu.rnti]; - - // > Fork each PDU handling to different executors based on the PDU RNTI. - if (not cfg.ue_exec_mapper.mac_ul_pdu_executor(ue_index).execute( - [this, slot_rx = msg.sl_rx, cell_idx = msg.cell_index, pdu = std::move(pdu)]() mutable { - // > Decode Rx PDU and handle respective subPDUs. - pdu_handler.handle_rx_pdu(slot_rx, cell_idx, std::move(pdu)); - })) { - logger.warning( - "cell={} slot_rx={}: Discarding Rx PDU. Cause: Rx task queue is full.", msg.cell_index, msg.sl_rx); - } - } - } + void handle_rx_data_indication(mac_rx_data_indication msg) override; private: const mac_ul_config cfg; diff --git a/lib/mac/mac_ul/mac_ul_ue_manager.cpp b/lib/mac/mac_ul/mac_ul_ue_manager.cpp index 1cfd28923a..0d6855bb30 100644 --- a/lib/mac/mac_ul/mac_ul_ue_manager.cpp +++ b/lib/mac/mac_ul/mac_ul_ue_manager.cpp @@ -21,6 +21,7 @@ */ #include "mac_ul_ue_manager.h" +#include "srsran/srslog/srslog.h" using namespace srsran; @@ -42,7 +43,7 @@ bool mac_ul_ue_manager::add_ue(const mac_ue_create_request& request) // > Add UE Bearers if (not addmod_bearers(request.ue_index, request.bearers)) { - log_proc_failure(logger, request.ue_index, request.crnti, "UE Create Request", "Failed to add/mod UE bearers"); + logger.warning("ue={}: \"UE Creation\" failed. Cause: Failed to add/mod UE bearers", request.ue_index); return false; } @@ -52,7 +53,7 @@ bool mac_ul_ue_manager::add_ue(const mac_ue_create_request& request) void mac_ul_ue_manager::remove_ue(du_ue_index_t ue_index) { if (not ue_db.contains(ue_index)) { - log_proc_failure(logger, ue_index, "UE Remove Request", "Invalid RNTI"); + logger.warning("ue={}: \"UE Removal\" failed. Cause: UE with provided ID does not exist", ue_index); return; } ue_db.erase(ue_index); @@ -62,7 +63,7 @@ bool mac_ul_ue_manager::addmod_bearers(du_ue_index_t const std::vector& ul_logical_channels) { if (not ue_db.contains(ue_index)) { - logger.error("ue={} Interrupting DEMUX update. Cause: The provided index does not exist", ue_index); + logger.error("ue={}: Interrupting DEMUX update. Cause: The provided index does not exist", ue_index); return false; } mac_ul_ue_context& u = ue_db[ue_index]; diff --git a/lib/mac/mac_ul/mac_ul_ue_manager.h b/lib/mac/mac_ul/mac_ul_ue_manager.h index ce6a2897ca..877ac690ab 100644 --- a/lib/mac/mac_ul/mac_ul_ue_manager.h +++ b/lib/mac/mac_ul/mac_ul_ue_manager.h @@ -22,12 +22,12 @@ #pragma once -#include "../../ran/gnb_format.h" #include "srsran/adt/slotted_array.h" #include "srsran/du_high/rnti_value_table.h" #include "srsran/mac/mac.h" #include "srsran/ran/du_types.h" #include "srsran/ran/du_ue_list.h" +#include "srsran/srslog/logger.h" namespace srsran { diff --git a/lib/mac/mac_ul/pdu_rx_handler.cpp b/lib/mac/mac_ul/pdu_rx_handler.cpp index ee10d31dd0..326c3c3618 100644 --- a/lib/mac/mac_ul/pdu_rx_handler.cpp +++ b/lib/mac/mac_ul/pdu_rx_handler.cpp @@ -22,6 +22,8 @@ #include "pdu_rx_handler.h" #include "srsran/instrumentation/traces/up_traces.h" +#include "srsran/srslog/srslog.h" +#include "srsran/support/format_utils.h" using namespace srsran; diff --git a/lib/mac/mac_ul/pdu_rx_handler.h b/lib/mac/mac_ul/pdu_rx_handler.h index 9a1d2a7325..029c1a2892 100644 --- a/lib/mac/mac_ul/pdu_rx_handler.h +++ b/lib/mac/mac_ul/pdu_rx_handler.h @@ -33,6 +33,7 @@ #include "srsran/pcap/mac_pcap.h" #include "srsran/ran/du_types.h" #include "srsran/ran/slot_point.h" +#include "srsran/srslog/logger.h" namespace srsran { diff --git a/lib/ngap/ngap_asn1_helpers.h b/lib/ngap/ngap_asn1_helpers.h index 88aa668c3f..635af4f3ed 100644 --- a/lib/ngap/ngap_asn1_helpers.h +++ b/lib/ngap/ngap_asn1_helpers.h @@ -32,6 +32,7 @@ #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/ngap/ngap_handover.h" #include "srsran/ngap/ngap_init_context_setup.h" +#include "srsran/ngap/ngap_nas.h" #include "srsran/ngap/ngap_reset.h" #include "srsran/ngap/ngap_setup.h" #include "srsran/ngap/ngap_types.h" @@ -176,6 +177,22 @@ inline void fill_asn1_ng_reset(asn1::ngap::ng_reset_s& asn1_reset, const ngap_ng } } +/// \brief Fills the common type \c ngap_dl_nas_transport_message struct. +/// \param[out] msg The common type \c ngap_dl_nas_transport_message struct to fill. +/// \param[in] ue_index The index of the UE. +/// \param[in] asn1_msg The ASN.1 type DLNASTransport. +inline void fill_ngap_dl_nas_transport_message(ngap_dl_nas_transport_message& msg, + ue_index_t ue_index, + const asn1::ngap::dl_nas_transport_s& asn1_msg) +{ + msg.ue_index = ue_index; + msg.nas_pdu = asn1_msg->nas_pdu.copy(); + if (asn1_msg->ue_cap_info_request_present && + asn1_msg->ue_cap_info_request == asn1::ngap::ue_cap_info_request_opts::options::requested) { + msg.ue_cap_info_request = true; + } +} + /// \brief Convert common type Initial UE Message to NGAP Initial UE Message. /// \param[out] asn1_msg The ASN1 NGAP Initial UE Message. /// \param[in] msg The CU-CP Initial UE Message. diff --git a/lib/ngap/ngap_impl.cpp b/lib/ngap/ngap_impl.cpp index 3638b24b09..e0f967d208 100644 --- a/lib/ngap/ngap_impl.cpp +++ b/lib/ngap/ngap_impl.cpp @@ -255,6 +255,48 @@ void ngap_impl::handle_ul_nas_transport_message(const cu_cp_ul_nas_transport& ms })); } +void ngap_impl::handle_tx_ue_radio_capability_info_indication_required( + const ngap_ue_radio_capability_info_indication& msg) +{ + if (!ue_ctxt_list.contains(msg.ue_index)) { + logger.warning("ue={}: Dropping UE Radio Capability Info Indication. UE context does not exist", msg.ue_index); + return; + } + + ngap_ue_context& ue_ctxt = ue_ctxt_list[msg.ue_index]; + + auto* ue = ue_ctxt.get_cu_cp_ue(); + srsran_assert(ue != nullptr, + "ue={} ran_ue={} amf_ue={}: UE for UE context doesn't exist", + ue_ctxt.ue_ids.ue_index, + ue_ctxt.ue_ids.ran_ue_id, + ue_ctxt.ue_ids.amf_ue_id); + + ngap_message ngap_msg = {}; + ngap_msg.pdu.set_init_msg(); + ngap_msg.pdu.init_msg().load_info_obj(ASN1_NGAP_ID_UE_RADIO_CAP_INFO_IND); + auto& ue_radio_cap_info_ind_msg = ngap_msg.pdu.init_msg().value.ue_radio_cap_info_ind(); + + ue_radio_cap_info_ind_msg->ran_ue_ngap_id = ran_ue_id_to_uint(ue_ctxt.ue_ids.ran_ue_id); + + amf_ue_id_t amf_ue_id = ue_ctxt.ue_ids.amf_ue_id; + if (amf_ue_id == amf_ue_id_t::invalid) { + logger.warning("ue={}: Dropping UE Radio Capability Info message. UE does not have an AMF UE ID", msg.ue_index); + return; + } + ue_radio_cap_info_ind_msg->amf_ue_ngap_id = amf_ue_id_to_uint(amf_ue_id); + ue_radio_cap_info_ind_msg->ue_radio_cap = msg.ue_cap_rat_container_list.copy(); + + ue_ctxt.logger.log_info("Scheduling UE Radio Capability Info Indication"); + + // Schedule transmission of UE Radio Capability Info Indication to AMF + ue->schedule_async_task(launch_async([this, ngap_msg](coro_context>& ctx) { + CORO_BEGIN(ctx); + tx_pdu_notifier->on_new_message(ngap_msg); + CORO_RETURN(); + })); +} + void ngap_impl::handle_message(const ngap_message& msg) { // Run NGAP protocols in Control executor. @@ -353,9 +395,16 @@ void ngap_impl::handle_dl_nas_transport_message(const asn1::ngap::dl_nas_transpo ue_ctxt_list.update_amf_ue_id(ue_ctxt.ue_ids.ran_ue_id, uint_to_amf_ue_id(msg->amf_ue_ngap_id)); } + ngap_dl_nas_transport_message dl_nas_msg; + fill_ngap_dl_nas_transport_message(dl_nas_msg, ue->get_ue_index(), msg); + // start routine - ue->schedule_async_task(launch_async( - msg->nas_pdu.copy(), ue->get_rrc_ue_pdu_notifier(), ue_ctxt.logger)); + ue->schedule_async_task( + launch_async(dl_nas_msg, + ue->get_rrc_ue_pdu_notifier(), + ue->get_rrc_ue_control_notifier(), + get_ngap_ue_radio_cap_management_handler(), + ue_ctxt.logger)); } void ngap_impl::handle_initial_context_setup_request(const asn1::ngap::init_context_setup_request_s& request) @@ -477,7 +526,7 @@ void ngap_impl::handle_pdu_session_resource_setup_request(const asn1::ngap::pdu_ // start routine ue->schedule_async_task(launch_async( - msg, request, ue_ctxt.ue_ids, ue->get_rrc_ue_pdu_notifier(), cu_cp_notifier, *tx_pdu_notifier, ue_ctxt.logger)); + msg, request, ue_ctxt.ue_ids, cu_cp_notifier, *tx_pdu_notifier, ue_ctxt.logger)); } void ngap_impl::handle_pdu_session_resource_modify_request(const asn1::ngap::pdu_session_res_modify_request_s& request) diff --git a/lib/ngap/ngap_impl.h b/lib/ngap/ngap_impl.h index 1c8370d083..ab4ac8893c 100644 --- a/lib/ngap/ngap_impl.h +++ b/lib/ngap/ngap_impl.h @@ -31,6 +31,7 @@ #include "srsran/ngap/gateways/n2_connection_client.h" #include "srsran/ngap/ngap.h" #include "srsran/ngap/ngap_configuration.h" +#include "srsran/ngap/ngap_ue_radio_capability_management.h" #include "srsran/support/executors/task_executor.h" #include @@ -56,18 +57,21 @@ class ngap_impl final : public ngap_interface bool handle_amf_tnl_connection_request() override; async_task handle_amf_disconnection_request() override; async_task handle_ng_setup_request(const ngap_ng_setup_request& request) override; + async_task handle_ng_reset_message(const cu_cp_ng_reset& msg) override; - async_task handle_ng_reset_message(const cu_cp_ng_reset& msg) override; - + // ngap_nas_message_handler void handle_initial_ue_message(const cu_cp_initial_ue_message& msg) override; - void handle_ul_nas_transport_message(const cu_cp_ul_nas_transport& msg) override; + // ngap_ue_radio_capability_management_handler + void + handle_tx_ue_radio_capability_info_indication_required(const ngap_ue_radio_capability_info_indication& msg) override; + // ngap message handler functions void handle_message(const ngap_message& msg) override; void handle_connection_loss() override {} - // ngap control message handler functions + // ngap_control_message_handler async_task handle_ue_context_release_request(const cu_cp_ue_context_release_request& msg) override; async_task handle_handover_preparation_request(const ngap_handover_preparation_request& msg) override; @@ -81,14 +85,15 @@ class ngap_impl final : public ngap_interface // ngap_ue_context_removal_handler void remove_ue_context(ue_index_t ue_index) override; - ngap_message_handler& get_ngap_message_handler() override { return *this; } - ngap_event_handler& get_ngap_event_handler() override { return *this; } - ngap_connection_manager& get_ngap_connection_manager() override { return *this; } - ngap_nas_message_handler& get_ngap_nas_message_handler() override { return *this; } - ngap_control_message_handler& get_ngap_control_message_handler() override { return *this; } - ngap_ue_control_manager& get_ngap_ue_control_manager() override { return *this; } - ngap_statistics_handler& get_ngap_statistics_handler() override { return *this; } - ngap_ue_context_removal_handler& get_ngap_ue_context_removal_handler() override { return *this; } + ngap_message_handler& get_ngap_message_handler() override { return *this; } + ngap_event_handler& get_ngap_event_handler() override { return *this; } + ngap_connection_manager& get_ngap_connection_manager() override { return *this; } + ngap_nas_message_handler& get_ngap_nas_message_handler() override { return *this; } + ngap_ue_radio_capability_management_handler& get_ngap_ue_radio_cap_management_handler() override { return *this; } + ngap_control_message_handler& get_ngap_control_message_handler() override { return *this; } + ngap_ue_control_manager& get_ngap_ue_control_manager() override { return *this; } + ngap_statistics_handler& get_ngap_statistics_handler() override { return *this; } + ngap_ue_context_removal_handler& get_ngap_ue_context_removal_handler() override { return *this; } private: class tx_pdu_notifier_with_logging final : public ngap_message_notifier diff --git a/lib/ngap/procedures/ngap_dl_nas_message_transfer_procedure.cpp b/lib/ngap/procedures/ngap_dl_nas_message_transfer_procedure.cpp index 8c1655e944..1264cbe6f0 100644 --- a/lib/ngap/procedures/ngap_dl_nas_message_transfer_procedure.cpp +++ b/lib/ngap/procedures/ngap_dl_nas_message_transfer_procedure.cpp @@ -28,10 +28,16 @@ using namespace srsran::srs_cu_cp; using namespace asn1::ngap; ngap_dl_nas_message_transfer_procedure::ngap_dl_nas_message_transfer_procedure( - byte_buffer nas_pdu_, - ngap_rrc_ue_pdu_notifier& rrc_ue_pdu_notifier_, - ngap_ue_logger& logger_) : - nas_pdu(nas_pdu_), rrc_ue_pdu_notifier(rrc_ue_pdu_notifier_), logger(logger_) + const ngap_dl_nas_transport_message& msg_, + ngap_rrc_ue_pdu_notifier& rrc_ue_pdu_notifier_, + ngap_rrc_ue_control_notifier& rrc_ue_ctrl_notifier_, + ngap_ue_radio_capability_management_handler& ngap_handler_, + ngap_ue_logger& logger_) : + msg(msg_), + rrc_ue_pdu_notifier(rrc_ue_pdu_notifier_), + rrc_ue_ctrl_notifier(rrc_ue_ctrl_notifier_), + ngap_handler(ngap_handler_), + logger(logger_) { } @@ -43,6 +49,12 @@ void ngap_dl_nas_message_transfer_procedure::operator()(coro_context>& ctx); @@ -43,11 +46,13 @@ class ngap_dl_nas_message_transfer_procedure private: // results senders void send_pdu_to_rrc_ue(); + void send_ue_radio_capability_info_indication(); - byte_buffer nas_pdu; - const ngap_ue_ids ue_ids; - ngap_rrc_ue_pdu_notifier& rrc_ue_pdu_notifier; - ngap_ue_logger& logger; + ngap_dl_nas_transport_message msg; + ngap_rrc_ue_pdu_notifier& rrc_ue_pdu_notifier; + ngap_rrc_ue_control_notifier& rrc_ue_ctrl_notifier; + ngap_ue_radio_capability_management_handler& ngap_handler; + ngap_ue_logger& logger; }; } // namespace srs_cu_cp diff --git a/lib/ngap/procedures/ngap_pdu_session_resource_setup_procedure.cpp b/lib/ngap/procedures/ngap_pdu_session_resource_setup_procedure.cpp index f45e5ca3eb..fe3d278ab0 100644 --- a/lib/ngap/procedures/ngap_pdu_session_resource_setup_procedure.cpp +++ b/lib/ngap/procedures/ngap_pdu_session_resource_setup_procedure.cpp @@ -22,7 +22,6 @@ #include "ngap_pdu_session_resource_setup_procedure.h" #include "../ngap/ngap_asn1_helpers.h" -#include "ngap_procedure_helpers.h" #include "srsran/asn1/ngap/common.h" #include "srsran/ngap/ngap.h" #include "srsran/ngap/ngap_message.h" @@ -35,14 +34,12 @@ ngap_pdu_session_resource_setup_procedure::ngap_pdu_session_resource_setup_proce const cu_cp_pdu_session_resource_setup_request& request_, const asn1::ngap::pdu_session_res_setup_request_s& asn1_request_, const ngap_ue_ids& ue_ids_, - ngap_rrc_ue_pdu_notifier& rrc_ue_pdu_notifier_, ngap_cu_cp_notifier& cu_cp_notifier_, ngap_message_notifier& amf_notif_, ngap_ue_logger& logger_) : request(request_), asn1_request(asn1_request_), ue_ids(ue_ids_), - rrc_ue_pdu_notifier(rrc_ue_pdu_notifier_), cu_cp_notifier(cu_cp_notifier_), amf_notifier(amf_notif_), logger(logger_) @@ -62,14 +59,14 @@ void ngap_pdu_session_resource_setup_procedure::operator()(coro_contextnas_pdu_present) { - handle_nas_pdu(logger, asn1_request->nas_pdu.copy(), rrc_ue_pdu_notifier); + verification_outcome.request.nas_pdu = asn1_request->nas_pdu.copy(); } + // Handle mandatory IEs + CORO_AWAIT_VALUE(response, cu_cp_notifier.on_new_pdu_session_resource_setup_request(verification_outcome.request)); + // Combine validation response with DU processor response combine_pdu_session_resource_setup_response(); } diff --git a/lib/ngap/procedures/ngap_pdu_session_resource_setup_procedure.h b/lib/ngap/procedures/ngap_pdu_session_resource_setup_procedure.h index 77a081a0f9..89f7a48399 100644 --- a/lib/ngap/procedures/ngap_pdu_session_resource_setup_procedure.h +++ b/lib/ngap/procedures/ngap_pdu_session_resource_setup_procedure.h @@ -36,7 +36,6 @@ class ngap_pdu_session_resource_setup_procedure ngap_pdu_session_resource_setup_procedure(const cu_cp_pdu_session_resource_setup_request& request_, const asn1::ngap::pdu_session_res_setup_request_s& asn1_request_, const ngap_ue_ids& ue_ids_, - ngap_rrc_ue_pdu_notifier& rrc_ue_pdu_notifier_, ngap_cu_cp_notifier& cu_cp_notifier_, ngap_message_notifier& amf_notif_, ngap_ue_logger& logger_); @@ -56,7 +55,6 @@ class ngap_pdu_session_resource_setup_procedure cu_cp_pdu_session_resource_setup_response validation_response; byte_buffer nas_pdu; const ngap_ue_ids ue_ids; - ngap_rrc_ue_pdu_notifier& rrc_ue_pdu_notifier; cu_cp_pdu_session_resource_setup_response response; ngap_cu_cp_notifier& cu_cp_notifier; ngap_message_notifier& amf_notifier; diff --git a/lib/ofh/ethernet/ethernet_receiver_impl.cpp b/lib/ofh/ethernet/ethernet_receiver_impl.cpp index 0cc6b59abf..67fcb4b2c4 100644 --- a/lib/ofh/ethernet/ethernet_receiver_impl.cpp +++ b/lib/ofh/ethernet/ethernet_receiver_impl.cpp @@ -64,7 +64,7 @@ receiver_impl::receiver_impl(const std::string& interface, } if (interface.size() > (IFNAMSIZ - 1)) { - report_error("The Ethernet receiver interface name '{}' exceeds the maximum allowed length"); + report_error("The Ethernet receiver interface name '{}' exceeds the maximum allowed length", interface); } if (is_promiscuous_mode_enabled) { diff --git a/lib/phy/generic_functions/precoding/channel_precoder_avx2.cpp b/lib/phy/generic_functions/precoding/channel_precoder_avx2.cpp index a045b3df31..b05814949c 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_avx2.cpp +++ b/lib/phy/generic_functions/precoding/channel_precoder_avx2.cpp @@ -57,9 +57,9 @@ simd_cf_interleaved operator*(const simd_cf_interleaved& re, const simd_cf_t& we } // namespace -void channel_precoder_avx2::apply_precoding_port(span port_re, - const re_buffer_reader& input_re, - span port_weights) const +void channel_precoder_avx2::apply_precoding_port(span port_re, + const re_buffer_reader<>& input_re, + span port_weights) const { unsigned nof_re = input_re.get_nof_re(); unsigned nof_layers = input_re.get_nof_slices(); @@ -193,7 +193,7 @@ static inline void layer4_map_and_ci8_to_cf(simd_cf_interleaved& out_l0, from_ci8_to_cf(out_l0, out_l1, out_l2, out_l3, tmp); } -void channel_precoder_avx2::apply_layer_map_and_precoding(re_buffer_writer& output, +void channel_precoder_avx2::apply_layer_map_and_precoding(re_buffer_writer<>& output, span input, const precoding_weight_matrix& precoding) const { diff --git a/lib/phy/generic_functions/precoding/channel_precoder_avx2.h b/lib/phy/generic_functions/precoding/channel_precoder_avx2.h index 7db753d952..69780fa46c 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_avx2.h +++ b/lib/phy/generic_functions/precoding/channel_precoder_avx2.h @@ -34,12 +34,12 @@ class channel_precoder_avx2 : public channel_precoder_impl { public: // See interface for documentation. - void apply_precoding_port(span port_re, - const re_buffer_reader& input_re, - span port_weights) const override; + void apply_precoding_port(span port_re, + const re_buffer_reader<>& input_re, + span port_weights) const override; // See interface for documentation. - void apply_layer_map_and_precoding(re_buffer_writer& output, + void apply_layer_map_and_precoding(re_buffer_writer<>& output, span input, const precoding_weight_matrix& precoding) const override; }; diff --git a/lib/phy/generic_functions/precoding/channel_precoder_avx512.cpp b/lib/phy/generic_functions/precoding/channel_precoder_avx512.cpp index 24c63f32e1..cc7eea2f3c 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_avx512.cpp +++ b/lib/phy/generic_functions/precoding/channel_precoder_avx512.cpp @@ -215,9 +215,9 @@ static inline void layer4_map_and_ci8_to_cf(simd_cf_interleaved& out0, from_ci8_to_cf(out0, out1, out2, out3, tmp); } -void channel_precoder_avx512::apply_precoding_port(span port_re, - const re_buffer_reader& input_re, - span port_weights) const +void channel_precoder_avx512::apply_precoding_port(span port_re, + const re_buffer_reader<>& input_re, + span port_weights) const { unsigned nof_re = input_re.get_nof_re(); unsigned nof_layers = input_re.get_nof_slices(); @@ -266,7 +266,7 @@ void channel_precoder_avx512::apply_precoding_port(span port_ } } -void channel_precoder_avx512::apply_layer_map_and_precoding(re_buffer_writer& output, +void channel_precoder_avx512::apply_layer_map_and_precoding(re_buffer_writer<>& output, span input, const precoding_weight_matrix& precoding) const { diff --git a/lib/phy/generic_functions/precoding/channel_precoder_avx512.h b/lib/phy/generic_functions/precoding/channel_precoder_avx512.h index b7fa140a83..5b28ab3145 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_avx512.h +++ b/lib/phy/generic_functions/precoding/channel_precoder_avx512.h @@ -33,13 +33,13 @@ namespace srsran { class channel_precoder_avx512 : public channel_precoder_impl { // See interface for documentation. - void apply_precoding_port(span port_re, - const re_buffer_reader& input_re, - span port_weights) const override; + void apply_precoding_port(span port_re, + const re_buffer_reader<>& input_re, + span port_weights) const override; public: // See interface for documentation. - void apply_layer_map_and_precoding(re_buffer_writer& output, + void apply_layer_map_and_precoding(re_buffer_writer<>& output, span input, const precoding_weight_matrix& precoding) const override; }; diff --git a/lib/phy/generic_functions/precoding/channel_precoder_generic.cpp b/lib/phy/generic_functions/precoding/channel_precoder_generic.cpp index 530aaf7d32..0a9ac66dd9 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_generic.cpp +++ b/lib/phy/generic_functions/precoding/channel_precoder_generic.cpp @@ -24,9 +24,9 @@ using namespace srsran; -void channel_precoder_generic::apply_precoding_port(span port_re, - const srsran::re_buffer_reader& input_re, - span port_weights) const +void channel_precoder_generic::apply_precoding_port(span port_re, + const re_buffer_reader<>& input_re, + span port_weights) const { unsigned nof_re = input_re.get_nof_re(); unsigned nof_layers = input_re.get_nof_slices(); @@ -47,7 +47,7 @@ void channel_precoder_generic::apply_precoding_port(span } } -void channel_precoder_generic::apply_layer_map_and_precoding(re_buffer_writer& output, +void channel_precoder_generic::apply_layer_map_and_precoding(re_buffer_writer<>& output, span input, const precoding_weight_matrix& precoding) const { diff --git a/lib/phy/generic_functions/precoding/channel_precoder_generic.h b/lib/phy/generic_functions/precoding/channel_precoder_generic.h index 1e8a1bdcd7..85a119342a 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_generic.h +++ b/lib/phy/generic_functions/precoding/channel_precoder_generic.h @@ -33,13 +33,13 @@ namespace srsran { class channel_precoder_generic : public channel_precoder_impl { // See interface for documentation. - void apply_precoding_port(span port_re, - const re_buffer_reader& input_re, - span port_weights) const override; + void apply_precoding_port(span port_re, + const re_buffer_reader<>& input_re, + span port_weights) const override; public: // See interface for documentation. - void apply_layer_map_and_precoding(re_buffer_writer& output, + void apply_layer_map_and_precoding(re_buffer_writer<>& output, span input, const precoding_weight_matrix& precoding) const override; }; diff --git a/lib/phy/generic_functions/precoding/channel_precoder_impl.cpp b/lib/phy/generic_functions/precoding/channel_precoder_impl.cpp index 783e986ead..762a228d9b 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_impl.cpp +++ b/lib/phy/generic_functions/precoding/channel_precoder_impl.cpp @@ -24,8 +24,8 @@ using namespace srsran; -void channel_precoder_impl::apply_precoding(re_buffer_writer& output, - const re_buffer_reader& input, +void channel_precoder_impl::apply_precoding(re_buffer_writer<>& output, + const re_buffer_reader<>& input, const precoding_weight_matrix& precoding) const { // Number of RE in a single slice, i.e., port or layer. diff --git a/lib/phy/generic_functions/precoding/channel_precoder_impl.h b/lib/phy/generic_functions/precoding/channel_precoder_impl.h index 6f447f03f9..7ad6f14391 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_impl.h +++ b/lib/phy/generic_functions/precoding/channel_precoder_impl.h @@ -37,8 +37,8 @@ class channel_precoder_impl : public channel_precoder explicit channel_precoder_impl() = default; // See interface for documentation. - void apply_precoding(re_buffer_writer& output, - const re_buffer_reader& input, + void apply_precoding(re_buffer_writer<>& output, + const re_buffer_reader<>& input, const precoding_weight_matrix& precoding) const override; protected: @@ -48,7 +48,7 @@ class channel_precoder_impl : public channel_precoder /// \param[in] input Input symbols, indexed by RE and transmit layer. /// \param[in] precoding Precoding coefficients, indexed by layer. virtual void - apply_precoding_port(span port_re, const re_buffer_reader& input_re, span port_weights) const = 0; + apply_precoding_port(span port_re, const re_buffer_reader<>& input_re, span port_weights) const = 0; }; } // namespace srsran diff --git a/lib/phy/generic_functions/precoding/channel_precoder_neon.cpp b/lib/phy/generic_functions/precoding/channel_precoder_neon.cpp index 39ebe64372..d24e7beda2 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_neon.cpp +++ b/lib/phy/generic_functions/precoding/channel_precoder_neon.cpp @@ -76,9 +76,9 @@ simd_cf_interleaved add_mul(const simd_cf_interleaved& sum, const simd_cf_interl } // namespace -void channel_precoder_neon::apply_precoding_port(span port_re, - const re_buffer_reader& input_re, - span port_weights) const +void channel_precoder_neon::apply_precoding_port(span port_re, + const re_buffer_reader<>& input_re, + span port_weights) const { unsigned nof_re = input_re.get_nof_re(); unsigned nof_layers = input_re.get_nof_slices(); @@ -251,7 +251,7 @@ static inline void layer4_map_and_ci8_to_cf(simd_cf_interleaved& out_l0, } template -void template_apply_layer_map_and_precoding(re_buffer_writer& output, +void template_apply_layer_map_and_precoding(re_buffer_writer<>& output, span input, const precoding_weight_matrix& precoding) { @@ -398,7 +398,7 @@ void template_apply_layer_map_and_precoding(re_buffer_writer& outpu } template <> -void template_apply_layer_map_and_precoding<1>(re_buffer_writer& output, +void template_apply_layer_map_and_precoding<1>(re_buffer_writer<>& output, span input, const precoding_weight_matrix& precoding) { @@ -446,7 +446,7 @@ void template_apply_layer_map_and_precoding<1>(re_buffer_writer& ou } } -void channel_precoder_neon::apply_layer_map_and_precoding(re_buffer_writer& output, +void channel_precoder_neon::apply_layer_map_and_precoding(re_buffer_writer<>& output, span input, const precoding_weight_matrix& precoding) const { diff --git a/lib/phy/generic_functions/precoding/channel_precoder_neon.h b/lib/phy/generic_functions/precoding/channel_precoder_neon.h index 4d560a7a0c..6a3681d66c 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_neon.h +++ b/lib/phy/generic_functions/precoding/channel_precoder_neon.h @@ -34,12 +34,12 @@ class channel_precoder_neon : public channel_precoder_impl { public: // See interface for documentation. - void apply_precoding_port(span port_re, - const re_buffer_reader& input_re, - span port_weights) const override; + void apply_precoding_port(span port_re, + const re_buffer_reader<>& input_re, + span port_weights) const override; // See interface for documentation. - void apply_layer_map_and_precoding(re_buffer_writer& output, + void apply_layer_map_and_precoding(re_buffer_writer<>& output, span input, const precoding_weight_matrix& precoding) const override; }; diff --git a/lib/phy/support/resource_grid_mapper_impl.cpp b/lib/phy/support/resource_grid_mapper_impl.cpp index e3423eda45..8128327a2b 100644 --- a/lib/phy/support/resource_grid_mapper_impl.cpp +++ b/lib/phy/support/resource_grid_mapper_impl.cpp @@ -46,7 +46,7 @@ static const re_prb_mask& get_re_mask_type_1(unsigned cdm_group_id) // from the RE allocation pattern. static void map_dmrs_type1_contiguous(resource_grid_writer& writer, precoding_buffer_type& precoding_buffer, - const re_buffer_reader& input, + const re_buffer_reader<>& input, const re_pattern& pattern, const precoding_configuration& precoding, const channel_precoder& precoder) @@ -162,7 +162,7 @@ resource_grid_mapper_impl::resource_grid_mapper_impl(unsigned static_cast(max_nof_subcarriers)); } -void resource_grid_mapper_impl::map(const re_buffer_reader& input, +void resource_grid_mapper_impl::map(const re_buffer_reader<>& input, const re_pattern& pattern, const precoding_configuration& precoding) { diff --git a/lib/phy/support/resource_grid_mapper_impl.h b/lib/phy/support/resource_grid_mapper_impl.h index aa03497aa4..4833aabec0 100644 --- a/lib/phy/support/resource_grid_mapper_impl.h +++ b/lib/phy/support/resource_grid_mapper_impl.h @@ -41,7 +41,8 @@ class resource_grid_mapper_impl : public resource_grid_mapper ~resource_grid_mapper_impl() = default; // See interface for documentation. - void map(const re_buffer_reader& input, const re_pattern& pattern, const precoding_configuration& precoding) override; + void + map(const re_buffer_reader<>& input, const re_pattern& pattern, const precoding_configuration& precoding) override; // See interface for documentation. void map(symbol_buffer& buffer, diff --git a/lib/phy/support/support_factories.cpp b/lib/phy/support/support_factories.cpp index 124f15c628..b7bee9e116 100644 --- a/lib/phy/support/support_factories.cpp +++ b/lib/phy/support/support_factories.cpp @@ -145,8 +145,8 @@ class channel_precoder_dummy : public channel_precoder { public: // See interface for documentation. - void apply_precoding(re_buffer_writer& output, - const re_buffer_reader& input, + void apply_precoding(re_buffer_writer<>& output, + const re_buffer_reader<>& input, const precoding_weight_matrix& precoding) const override { unsigned nof_ports = precoding.get_nof_ports(); @@ -180,7 +180,7 @@ class channel_precoder_dummy : public channel_precoder } // See interface for documentation. - void apply_layer_map_and_precoding(re_buffer_writer& output, + void apply_layer_map_and_precoding(re_buffer_writer<>& output, span input, const precoding_weight_matrix& precoding) const override { diff --git a/lib/phy/upper/channel_coding/ldpc/ldpc_decoder_impl.cpp b/lib/phy/upper/channel_coding/ldpc/ldpc_decoder_impl.cpp index 2b6c4ed9a6..28a2656586 100644 --- a/lib/phy/upper/channel_coding/ldpc/ldpc_decoder_impl.cpp +++ b/lib/phy/upper/channel_coding/ldpc/ldpc_decoder_impl.cpp @@ -160,8 +160,15 @@ void ldpc_decoder_impl::load_soft_bits(span llrs) soft_bits_view = soft_bits_view.last(soft_bits_view.size() - 2 * node_size_byte); for (unsigned i_node = 2 * node_size_byte, max_node = nof_full_nodes * node_size_byte; i_node != max_node; i_node += node_size_byte) { - srsvec::copy(soft_bits_view.first(lifting_size), llr_view.first(lifting_size)); + // Copy input LLR in the soft bits. + clamp(soft_bits_view.first(lifting_size), llr_view.first(lifting_size), soft_bits_clamp_low, soft_bits_clamp_high); + + // Advance input LLR. llr_view = llr_view.last(llr_view.size() - lifting_size); + + // Zero node tail soft bits. + srsvec::zero(soft_bits_view.subspan(lifting_size, node_size_byte - lifting_size)); + // Recall that soft bits may have zero padding in SIMD implementations (i.e., when node_size_byte != lifting_size). soft_bits_view = soft_bits_view.last(soft_bits_view.size() - node_size_byte); } @@ -169,7 +176,10 @@ void ldpc_decoder_impl::load_soft_bits(span llrs) // The length of llrs may not be an exact multiple of the lifting size. unsigned tail_positions = llr_view.size(); if (tail_positions != 0) { + // Copy last LLRs. srsvec::copy(soft_bits_view.first(tail_positions), llr_view); + // Zero the remaining soft bits. + srsvec::zero(soft_bits_view.subspan(tail_positions, node_size_byte - lifting_size)); } } diff --git a/lib/phy/upper/channel_coding/ldpc/ldpc_decoder_impl.h b/lib/phy/upper/channel_coding/ldpc/ldpc_decoder_impl.h index 4de0db32ca..b15db41c27 100644 --- a/lib/phy/upper/channel_coding/ldpc/ldpc_decoder_impl.h +++ b/lib/phy/upper/channel_coding/ldpc/ldpc_decoder_impl.h @@ -189,6 +189,10 @@ class ldpc_decoder_impl : public ldpc_decoder std::array(ldpc::MAX_BG_N_FULL* ldpc::MAX_LIFTING_SIZE)> soft_bits; private: + /// Soft bits clamp lower bound. + static constexpr log_likelihood_ratio soft_bits_clamp_low = -32; + /// Soft bits clamp higher bound. + static constexpr log_likelihood_ratio soft_bits_clamp_high = 32; /// Pointer to the Tanner graph (~ parity check matrix) used by the encoding algorithm. const ldpc_graph_impl* current_graph = nullptr; /// Total number of base graph variable nodes in the current graph. diff --git a/lib/phy/upper/channel_processors/pdcch_modulator_impl.cpp b/lib/phy/upper/channel_processors/pdcch_modulator_impl.cpp index b88131ad12..d82abd93ce 100644 --- a/lib/phy/upper/channel_processors/pdcch_modulator_impl.cpp +++ b/lib/phy/upper/channel_processors/pdcch_modulator_impl.cpp @@ -56,7 +56,7 @@ void pdcch_modulator_impl::modulate(span d_pdcch, span b_ha } } -void pdcch_modulator_impl::map(resource_grid_mapper& mapper, const re_buffer_reader& d_pdcch, const config_t& config) +void pdcch_modulator_impl::map(resource_grid_mapper& mapper, const re_buffer_reader<>& d_pdcch, const config_t& config) { // Resource element allocation within a resource block for PDCCH. static const re_prb_mask re_mask = {true, false, true, true, true, false, true, true, true, false, true, true}; diff --git a/lib/phy/upper/channel_processors/pdcch_modulator_impl.h b/lib/phy/upper/channel_processors/pdcch_modulator_impl.h index f13105a4f8..ba9e2d0c96 100644 --- a/lib/phy/upper/channel_processors/pdcch_modulator_impl.h +++ b/lib/phy/upper/channel_processors/pdcch_modulator_impl.h @@ -73,7 +73,7 @@ class pdcch_modulator_impl : public pdcch_modulator /// \param[out] mapper Provides the destination resource grid. /// \param[in] d_pdcch Provides the block of complex-valued symbols to map. /// \param[in] config Provides the mapper configuration. - void map(resource_grid_mapper& mapper, const re_buffer_reader& d_pdcch, const config_t& config); + void map(resource_grid_mapper& mapper, const re_buffer_reader<>& d_pdcch, const config_t& config); public: // See interface for the documentation. diff --git a/lib/phy/upper/channel_processors/pucch_demodulator_impl.cpp b/lib/phy/upper/channel_processors/pucch_demodulator_impl.cpp index a133d1c814..5f9abebf23 100644 --- a/lib/phy/upper/channel_processors/pucch_demodulator_impl.cpp +++ b/lib/phy/upper/channel_processors/pucch_demodulator_impl.cpp @@ -52,8 +52,8 @@ void pucch_demodulator_impl::demodulate(span pucch_constants::FORMAT2_MAX_NSYMB); // Resize data and channel estimation buffers. - ch_re.resize({nof_re_port, nof_rx_ports}); - ch_estimates.resize({nof_re_port, nof_rx_ports, SINGLE_TX_LAYER}); + ch_re.resize(nof_rx_ports, nof_re_port); + ch_estimates.resize(nof_re_port, nof_rx_ports, SINGLE_TX_LAYER); // Resize equalized data and post equalization noise variance buffers. eq_re.resize(nof_re_port); @@ -118,10 +118,10 @@ void pucch_demodulator_impl::get_data_re_ests(const resource_grid_reader& for (unsigned i_port = 0, i_port_end = config.rx_ports.size(); i_port != i_port_end; ++i_port) { // Get a view of the data RE destination buffer for a single Rx port. - span re_port_buffer = ch_re.get_view({i_port}); + span re_port_buffer = ch_re.get_slice(i_port); // Get a view of the channel estimates destination buffer for a single Rx port and Tx layer. - span ests_port_buffer = ch_estimates.get_view({i_port, 0}); + span ests_port_buffer = ch_estimates.get_channel(i_port, 0); for (unsigned i_symbol = config.start_symbol_index, i_symbol_end = config.start_symbol_index + config.nof_symbols; i_symbol != i_symbol_end; diff --git a/lib/phy/upper/channel_processors/pucch_demodulator_impl.h b/lib/phy/upper/channel_processors/pucch_demodulator_impl.h index 6b0ee2072b..a1eab5b34d 100644 --- a/lib/phy/upper/channel_processors/pucch_demodulator_impl.h +++ b/lib/phy/upper/channel_processors/pucch_demodulator_impl.h @@ -29,6 +29,7 @@ #include "srsran/phy/upper/channel_modulation/demodulation_mapper.h" #include "srsran/phy/upper/channel_processors/pucch_demodulator.h" #include "srsran/phy/upper/equalization/channel_equalizer.h" +#include "srsran/phy/upper/equalization/dynamic_ch_est_list.h" #include "srsran/phy/upper/sequence_generators/pseudo_random_generator.h" #include "srsran/ran/pucch/pucch_constants.h" @@ -42,7 +43,10 @@ class pucch_demodulator_impl : public pucch_demodulator pucch_demodulator_impl(std::unique_ptr equalizer_, std::unique_ptr demapper_, std::unique_ptr descrambler_) : - equalizer(std::move(equalizer_)), demapper(std::move(demapper_)), descrambler(std::move(descrambler_)) + equalizer(std::move(equalizer_)), + demapper(std::move(demapper_)), + descrambler(std::move(descrambler_)), + ch_estimates(pucch_constants::MAX_NOF_RE, MAX_PORTS, 1) { srsran_assert(equalizer, "Invalid pointer to channel_equalizer object."); srsran_assert(demapper, "Invalid pointer to demodulation_mapper object."); @@ -92,11 +96,7 @@ class pucch_demodulator_impl : public pucch_demodulator /// \brief Buffer used to transfer channel modulation symbols from the resource grid to the equalizer. /// \remark The symbols are arranged in two dimensions, i.e., resource element and receive port. - static_tensor(channel_equalizer::re_list::dims::nof_dims), - cbf16_t, - pucch_constants::MAX_NOF_RE * MAX_PORTS, - channel_equalizer::re_list::dims> - ch_re; + static_re_buffer ch_re; /// \brief Buffer used to store channel modulation resource elements at the equalizer output. /// \remark The symbols are arranged in two dimensions, i.e., resource element and transmit layer. @@ -109,12 +109,7 @@ class pucch_demodulator_impl : public pucch_demodulator /// \brief Buffer used to transfer channel estimation coefficients from the channel estimate to the equalizer. /// \remark The channel estimation coefficients are arranged in three dimensions, i.e., resource element, receive port /// and transmit layer. - static_tensor( - channel_equalizer::ch_est_list::dims::nof_dims), - cbf16_t, - pucch_constants::MAX_NOF_RE * MAX_PORTS, - channel_equalizer::ch_est_list::dims> - ch_estimates; + dynamic_ch_est_list ch_estimates; /// Buffer used to transfer noise variance estimates from the channel estimate to the equalizer. std::array noise_var_estimates; diff --git a/lib/phy/upper/channel_processors/pucch_detector_impl.cpp b/lib/phy/upper/channel_processors/pucch_detector_impl.cpp index a73da4536f..d8a7aa6b22 100644 --- a/lib/phy/upper/channel_processors/pucch_detector_impl.cpp +++ b/lib/phy/upper/channel_processors/pucch_detector_impl.cpp @@ -26,6 +26,7 @@ #include "pucch_detector_impl.h" #include "srsran/phy/support/resource_grid_reader.h" #include "srsran/srsvec/conversion.h" +#include "srsran/srsvec/copy.h" #include "srsran/srsvec/mean.h" using namespace srsran; @@ -223,8 +224,8 @@ pucch_detector::pucch_detection_result pucch_detector_impl::detect(const resourc // Total number of REs used for PUCCH data (recall that positive integer division implies taking the floor). unsigned nof_res = (config.nof_symbols / 2) * NRE; unsigned nof_ports = config.ports.size(); - time_spread_sequence.resize({nof_res, nof_ports}); - ch_estimates.resize({nof_res, nof_ports, 1}); + time_spread_sequence.resize(nof_ports, nof_res); + ch_estimates.resize(nof_res, nof_ports, 1); eq_time_spread_sequence.resize(nof_res); eq_time_spread_noise_var.resize(nof_res); @@ -318,8 +319,8 @@ void pucch_detector_impl::extract_data_and_estimates(const resource_grid_reader& unsigned i_symbol = 0; unsigned skip = 0; unsigned symbol_index = first_symbol + 1; - span sequence_slice = time_spread_sequence.get_view({port}); - span estimate_slice = ch_estimates.get_view({port}); + span sequence_slice = time_spread_sequence.get_slice(port); + span estimate_slice = ch_estimates.get_channel(port, 0); for (; i_symbol != nof_data_symbols_pre_hop; ++i_symbol, skip += NRE, symbol_index += 2) { // Index of the first subcarrier assigned to PUCCH, before hopping. unsigned k_init = NRE * first_prb; diff --git a/lib/phy/upper/channel_processors/pucch_detector_impl.h b/lib/phy/upper/channel_processors/pucch_detector_impl.h index 0ea8dcaaae..15739a7596 100644 --- a/lib/phy/upper/channel_processors/pucch_detector_impl.h +++ b/lib/phy/upper/channel_processors/pucch_detector_impl.h @@ -27,9 +27,11 @@ #include "../signal_processors/pucch/pucch_helper.h" #include "pucch_detector_format0.h" +#include "srsran/phy/support/re_buffer.h" #include "srsran/phy/support/resource_grid_reader.h" #include "srsran/phy/upper/channel_processors/pucch_detector.h" #include "srsran/phy/upper/equalization/channel_equalizer.h" +#include "srsran/phy/upper/equalization/dynamic_ch_est_list.h" #include "srsran/phy/upper/sequence_generators/low_papr_sequence_collection.h" #include "srsran/phy/upper/sequence_generators/pseudo_random_generator.h" @@ -62,7 +64,8 @@ class pucch_detector_impl : public pucch_detector low_papr(std::move(low_papr_)), helper(std::move(pseudo_random_)), equalizer(std::move(equalizer_)), - detector_format0(std::move(detector_format0_)) + detector_format0(std::move(detector_format0_)), + ch_estimates(MAX_ALLOCATED_RE_F1, MAX_PORTS / 2, 1) { srsran_assert(low_papr, "Invalid Low PAPR sequence generator."); srsran_assert(equalizer, "Invalid equalizer."); @@ -105,21 +108,12 @@ class pucch_detector_impl : public pucch_detector std::unique_ptr equalizer; /// PUCCH Format 0 detector. std::unique_ptr detector_format0; - /// \brief Tensor for storing the spread data sequence. + /// \brief Buffer for storing the spread data sequence. /// \remark Only half of the allocated symbols contain data, the other half being used for DM-RS. - static_tensor(channel_equalizer::re_list::dims::nof_dims), - cbf16_t, - MAX_ALLOCATED_RE_F1 * MAX_PORTS / 2, - channel_equalizer::re_list::dims> - time_spread_sequence; + static_re_buffer time_spread_sequence; /// \brief Tensor for storing the channel estimates corresponding to the spread data sequence. /// \remark Only half of the allocated symbols contain data, the other half being used for DM-RS. - static_tensor( - channel_equalizer::ch_est_list::dims::nof_dims), - cbf16_t, - MAX_ALLOCATED_RE_F1 * MAX_PORTS / 2, - channel_equalizer::ch_est_list::dims> - ch_estimates; + dynamic_ch_est_list ch_estimates; /// \brief Buffer for storing the spread data sequence after equalization. /// \remark Only half of the allocated symbols contain data, the other half being used for DM-RS. static_vector eq_time_spread_sequence; diff --git a/lib/phy/upper/channel_processors/pusch/pusch_demodulator_impl.cpp b/lib/phy/upper/channel_processors/pusch/pusch_demodulator_impl.cpp index 512dff1bfa..46fa68caee 100644 --- a/lib/phy/upper/channel_processors/pusch/pusch_demodulator_impl.cpp +++ b/lib/phy/upper/channel_processors/pusch/pusch_demodulator_impl.cpp @@ -218,17 +218,14 @@ void pusch_demodulator_impl::demodulate(pusch_codeword_buffer& codeword_buf continue; } - // Resize equalizer input buffers. - ch_re.resize({nof_re_port, nof_rx_ports}); - ch_estimates.resize({nof_re_port, nof_rx_ports, config.nof_tx_layers}); - // Resize equalizer output buffers. span eq_re = span(temp_eq_re).first(nof_re_port * config.nof_tx_layers); span eq_noise_vars = span(temp_eq_noise_vars).first(nof_re_port * config.nof_tx_layers); // Extract the data symbols and channel estimates from the resource grid. - get_ch_data_re(ch_re, grid, i_symbol, i_subc, block_re_mask, config.rx_ports); - get_ch_data_estimates(ch_estimates, estimates, i_symbol, i_subc, block_re_mask, config.rx_ports); + const re_buffer_reader& ch_re = get_ch_data_re(grid, i_symbol, i_subc, block_re_mask, config.rx_ports); + const channel_equalizer::ch_est_list& ch_estimates = + get_ch_data_estimates(estimates, i_symbol, i_subc, config.nof_tx_layers, block_re_mask, config.rx_ports); // Increment subcarrier count. i_subc += nof_block_subc; diff --git a/lib/phy/upper/channel_processors/pusch/pusch_demodulator_impl.h b/lib/phy/upper/channel_processors/pusch/pusch_demodulator_impl.h index 8e0767c21a..c94571aa16 100644 --- a/lib/phy/upper/channel_processors/pusch/pusch_demodulator_impl.h +++ b/lib/phy/upper/channel_processors/pusch/pusch_demodulator_impl.h @@ -25,13 +25,16 @@ #pragma once +#include "srsran/phy/support/re_buffer.h" #include "srsran/phy/support/resource_grid_reader.h" #include "srsran/phy/upper/channel_modulation/demodulation_mapper.h" #include "srsran/phy/upper/channel_modulation/evm_calculator.h" #include "srsran/phy/upper/channel_processors/pusch/pusch_demodulator.h" #include "srsran/phy/upper/equalization/channel_equalizer.h" +#include "srsran/phy/upper/equalization/dynamic_ch_est_list.h" +#include "srsran/phy/upper/equalization/modular_ch_est_list.h" #include "srsran/phy/upper/sequence_generators/pseudo_random_generator.h" -#include "srsran/srsvec/conversion.h" +#include "srsran/srsvec/copy.h" namespace srsran { @@ -50,15 +53,15 @@ class pusch_demodulator_impl : public pusch_demodulator demapper(std::move(demapper_)), evm_calc(std::move(evm_calc_)), descrambler(std::move(descrambler_)), - ch_re({MAX_BLOCK_SIZE, 1}), - ch_estimates({MAX_BLOCK_SIZE, 1, 1}), + ch_re_view(pusch_constants::MAX_NOF_RX_PORTS), + ch_estimates_copy(MAX_BLOCK_SIZE, pusch_constants::MAX_NOF_RX_PORTS, pusch_constants::MAX_NOF_LAYERS), + ch_estimates_view(pusch_constants::MAX_NOF_RX_PORTS, pusch_constants::MAX_NOF_LAYERS), compute_post_eq_sinr(compute_post_eq_sinr_) { srsran_assert(equalizer, "Invalid pointer to channel_equalizer object."); srsran_assert(demapper, "Invalid pointer to demodulation_mapper object."); srsran_assert(descrambler, "Invalid pointer to pseudo_random_generator object."); } - // See interface for the documentation. void demodulate(pusch_codeword_buffer& data, pusch_demodulator_notifier& notifier, @@ -77,23 +80,47 @@ class pusch_demodulator_impl : public pusch_demodulator /// Extracts the PUSCH data RE's from the provided resource grid. The DM-RS symbols are skipped. The extracted RE's /// are arranged in two dimensions, i.e., resource element and receive antenna port, as the channel equalizer expects. /// - /// \param[out] data_re PUSCH channel data symbols, organized by receive antenna port. /// \param[in] grid Resource grid for the current slot. /// \param[in] i_symbol OFDM symbol index relative to the beginning of the slot. /// \param[in] init_subc Initial subcarrier index relative to Point A. /// \param[in] re_mask Resource element mask, it selects the RE elements to extract. /// \param[in] rx_ports Receive ports. - static void get_ch_data_re(channel_equalizer::re_list& data_re, - const resource_grid_reader& grid, - unsigned i_symbol, - unsigned init_subc, - const re_symbol_mask_type& re_mask, - const static_vector& rx_ports) + /// \return A reference to the PUSCH channel data symbols. + const re_buffer_reader& get_ch_data_re(const resource_grid_reader& grid, + unsigned i_symbol, + unsigned init_subc, + const re_symbol_mask_type& re_mask, + const static_vector& rx_ports) { + // Extract RE boundaries. + unsigned nof_re = re_mask.count(); + int begin = re_mask.find_lowest(); + int end = re_mask.find_highest(); + srsran_assert(begin <= end, "Invalid mask."); + + // Check if the mask is contiguous. + if (nof_re == static_cast(end + 1 - begin)) { + // Prepare channel estimates view. + ch_re_view.resize(rx_ports.size(), nof_re); + + // Iterate over all layers and ports. + for (unsigned i_port = 0, i_port_end = rx_ports.size(); i_port != i_port_end; ++i_port) { + // View of the channel estimation for an OFDM symbol. + span ch_data_re = grid.get_view(i_port, i_symbol); + + // Set the view in the channel estimates. + ch_re_view.set_slice(i_port, ch_data_re.subspan(init_subc + begin, nof_re)); + } + return ch_re_view; + } + + // Prepare channel estimates copy destination. + ch_re_copy.resize(rx_ports.size(), nof_re); + // Extract RE for each port and symbol. for (unsigned i_port = 0, i_port_end = rx_ports.size(); i_port != i_port_end; ++i_port) { // Get a view of the port data RE. - span re_port_buffer = data_re.get_view<>({i_port}); + span re_port_buffer = ch_re_copy.get_slice(i_port); // Copy grid data resource elements into the buffer. re_port_buffer = grid.get(re_port_buffer, rx_ports[i_port], i_symbol, init_subc, re_mask); @@ -102,6 +129,8 @@ class pusch_demodulator_impl : public pusch_demodulator srsran_assert( re_port_buffer.empty(), "Invalid number of RE read from the grid. {} RE are missing.", re_port_buffer.size()); } + + return ch_re_copy; } /// \brief Gets channel data estimates. @@ -110,24 +139,53 @@ class pusch_demodulator_impl : public pusch_demodulator /// estimate. The DM-RS symbols are skipped. The extracted channel coefficients are arranged in three dimensions, /// i.e., resource element, receive port and transmit layer, as the channel equalizer expects. /// - /// \param[out] data_estimates Channel estimates of the data symbols, organized by receive port and transmit layer. /// \param[in] channel_estimate Channel estimation object. - /// \param[in] i_symbol OFDM symbol index relative to the beginning of the slot. - /// \param[in] init_subc Initial subcarrier index relative to Point A. - /// \param[in] re_mask Resource element mask, it selects the RE elements to extract. - /// \param[in] rx_ports Receive ports. - static void get_ch_data_estimates(channel_equalizer::ch_est_list& data_estimates, - const channel_estimate& channel_estimate, - unsigned i_symbol, - unsigned init_subc, - const re_symbol_mask_type& re_mask, - const static_vector& rx_ports) + /// \param[in] i_symbol OFDM symbol index relative to the beginning of the slot. + /// \param[in] init_subc Initial subcarrier index relative to Point A. + /// \param[in] nof_tx_layers Number of layers. + /// \param[in] re_mask Resource element mask, it selects the RE elements to extract. + /// \param[in] rx_ports Receive ports list. + /// \return A reference to the PUSCH channel data estimates. + const channel_equalizer::ch_est_list& get_ch_data_estimates(const channel_estimate& channel_estimate, + unsigned i_symbol, + unsigned init_subc, + unsigned nof_tx_layers, + const re_symbol_mask_type& re_mask, + const static_vector& rx_ports) { + // Extract RE boundaries. + unsigned nof_re = re_mask.count(); + int begin = re_mask.find_lowest(); + int end = re_mask.find_highest(); + srsran_assert(begin <= end, "Invalid mask."); + + // Check if the mask is contiguous. + if (nof_re == static_cast(end + 1 - begin)) { + // Prepare channel estimates view. + ch_estimates_view.resize(nof_re, rx_ports.size(), nof_tx_layers); + + // Iterate over all layers and ports. + for (unsigned i_layer = 0, i_layer_end = nof_tx_layers; i_layer != i_layer_end; ++i_layer) { + for (unsigned i_port = 0, i_port_end = rx_ports.size(); i_port != i_port_end; ++i_port) { + // View of the channel estimation for an OFDM symbol. + span symbol_estimates = channel_estimate.get_symbol_ch_estimate(i_symbol, i_port, i_layer); + + // Set the view in the channel estimates. + ch_estimates_view.set_channel(symbol_estimates.subspan(init_subc + begin, nof_re), i_port, i_layer); + } + } + + // Return the reference to the view buffer. + return ch_estimates_view; + } + + ch_estimates_copy.resize(nof_re, rx_ports.size(), nof_tx_layers); + // Extract data RE coefficients from the channel estimation. - for (unsigned i_layer = 0, i_layer_end = channel_estimate.size().nof_tx_layers; i_layer != i_layer_end; ++i_layer) { + for (unsigned i_layer = 0, i_layer_end = nof_tx_layers; i_layer != i_layer_end; ++i_layer) { for (unsigned i_port = 0, i_port_end = rx_ports.size(); i_port != i_port_end; ++i_port) { // Get a view of the channel estimates buffer for a single Rx port. - span ch_port_buffer = data_estimates.get_view({i_port, i_layer}); + span ch_port_buffer = ch_estimates_copy.get_channel(i_port, i_layer); // View of the channel estimation for an OFDM symbol. span symbol_estimates = channel_estimate.get_symbol_ch_estimate(i_symbol, i_port, i_layer); @@ -135,19 +193,6 @@ class pusch_demodulator_impl : public pusch_demodulator // Get view of the selected area of the grid. symbol_estimates = symbol_estimates.subspan(init_subc, re_mask.size()); - if (re_mask.is_contiguous()) { - int begin = re_mask.find_lowest(); - int end = re_mask.find_highest(); - - srsran_assert(begin >= 0, "Invalid mask."); - srsran_assert(begin <= end, "Invalid mask."); - - symbol_estimates = symbol_estimates.subspan(begin, end - begin + 1); - - srsvec::copy(ch_port_buffer, symbol_estimates); - continue; - } - // Skip DM-RS estimates. re_mask.for_each(0, re_mask.size(), [&symbol_estimates, &ch_port_buffer](unsigned i_re) { // Copy RE. @@ -163,6 +208,8 @@ class pusch_demodulator_impl : public pusch_demodulator ch_port_buffer.size()); } } + + return ch_estimates_copy; } /// Channel equalization component, also in charge of combining contributions of all receive antenna ports. @@ -173,26 +220,18 @@ class pusch_demodulator_impl : public pusch_demodulator std::unique_ptr evm_calc; /// Descrambler component. std::unique_ptr descrambler; - - /// Buffer used to transfer channel modulation symbols from the resource grid to the equalizer. - dynamic_tensor(channel_equalizer::re_list::dims::nof_dims), - cbf16_t, - channel_equalizer::re_list::dims> - ch_re; - + /// Copy buffer used to transfer channel modulation symbols from the resource grid to the equalizer. + static_re_buffer ch_re_copy; + /// View buffer used to transfer channel modulation symbols from the resource grid to the equalizer. + modular_re_buffer_reader ch_re_view; /// Buffer used to store channel modulation resource elements at the equalizer output. std::array temp_eq_re; - /// Buffer used to transfer symbol noise variances at the equalizer output. std::array temp_eq_noise_vars; - - /// Buffer used to transfer channel estimation coefficients from the channel estimate to the equalizer. - dynamic_tensor( - channel_equalizer::ch_est_list::dims::nof_dims), - cbf16_t, - channel_equalizer::ch_est_list::dims> - ch_estimates; - + /// Copy buffer used to transfer channel estimation coefficients from the channel estimate to the equalizer. + dynamic_ch_est_list ch_estimates_copy; + /// View buffer used to transfer channel estimation coefficients from the channel estimate to the equalizer. + modular_ch_est_list ch_estimates_view; /// Buffer used to transfer noise variance estimates from the channel estimate to the equalizer. std::array noise_var_estimates; diff --git a/lib/phy/upper/equalization/channel_equalizer_generic_impl.cpp b/lib/phy/upper/equalization/channel_equalizer_generic_impl.cpp index 4d4380e56f..588d2254a3 100644 --- a/lib/phy/upper/equalization/channel_equalizer_generic_impl.cpp +++ b/lib/phy/upper/equalization/channel_equalizer_generic_impl.cpp @@ -34,13 +34,13 @@ using namespace srsran; /// Assert that the dimensions of the equalizer input and output data structures match. static inline void assert_sizes(span eq_symbols, span eq_noise_vars, - const channel_equalizer::re_list& ch_symbols, + const re_buffer_reader& ch_symbols, const channel_equalizer::ch_est_list& ch_estimates, span noise_var_estimates) { // Rx symbols dimensions. - unsigned ch_symb_nof_re = ch_symbols.get_dimension_size(channel_equalizer::re_list::dims::re); - unsigned ch_symb_nof_rx_ports = ch_symbols.get_dimension_size(channel_equalizer::re_list::dims::slice); + unsigned ch_symb_nof_re = ch_symbols.get_nof_re(); + unsigned ch_symb_nof_rx_ports = ch_symbols.get_nof_slices(); // Output symbols dimensions. unsigned eq_symb_nof_re = eq_symbols.size(); @@ -52,9 +52,9 @@ static inline void assert_sizes(span eq_symbols unsigned eq_nvars_nof_re = eq_noise_vars.size(); // Channel estimates dimensions. - unsigned ch_ests_nof_re = ch_estimates.get_dimension_size(channel_equalizer::ch_est_list::dims::re); - unsigned ch_ests_nof_rx_ports = ch_estimates.get_dimension_size(channel_equalizer::ch_est_list::dims::rx_port); - unsigned ch_ests_nof_tx_layers = ch_estimates.get_dimension_size(channel_equalizer::ch_est_list::dims::tx_layer); + unsigned ch_ests_nof_re = ch_estimates.get_nof_re(); + unsigned ch_ests_nof_rx_ports = ch_estimates.get_nof_rx_ports(); + unsigned ch_ests_nof_tx_layers = ch_estimates.get_nof_tx_layers(); // Assert that the number of Resource Elements is the same for both inputs. srsran_assert(ch_symb_nof_re == ch_ests_nof_re, @@ -99,7 +99,7 @@ template void equalize_zf_single_tx_layer(unsigned nof_ports, span eq_symbols, span eq_noise_vars, - const channel_equalizer::re_list& ch_symbols, + const re_buffer_reader& ch_symbols, const channel_equalizer::ch_est_list& ch_estimates, span noise_var, float tx_scaling) @@ -119,7 +119,7 @@ template <> void equalize_zf_single_tx_layer<1>(unsigned /**/, span eq_symbols, span eq_noise_vars, - const channel_equalizer::re_list& ch_symbols, + const re_buffer_reader& ch_symbols, const channel_equalizer::ch_est_list& ch_estimates, span noise_var, float tx_scaling) @@ -133,7 +133,7 @@ template void equalize_mmse_single_tx_layer(unsigned nof_ports, span eq_symbols, span eq_noise_vars, - const channel_equalizer::re_list& ch_symbols, + const re_buffer_reader& ch_symbols, const channel_equalizer::ch_est_list& ch_estimates, span noise_var, float tx_scaling) @@ -153,7 +153,7 @@ template <> void equalize_mmse_single_tx_layer<1>(unsigned /**/, span eq_symbols, span eq_noise_vars, - const channel_equalizer::re_list& ch_symbols, + const re_buffer_reader& ch_symbols, const channel_equalizer::ch_est_list& ch_estimates, span noise_var, float tx_scaling) @@ -162,12 +162,12 @@ void equalize_mmse_single_tx_layer<1>(unsigned /**/, equalize_mmse_1xn<1>(eq_symbols, eq_noise_vars, ch_symbols, ch_estimates, noise_var, tx_scaling); } -void channel_equalizer_generic_impl::equalize(span eq_symbols, - span eq_noise_vars, - const re_list& ch_symbols, - const ch_est_list& ch_estimates, - span noise_var_estimates, - float tx_scaling) +void channel_equalizer_generic_impl::equalize(span eq_symbols, + span eq_noise_vars, + const re_buffer_reader& ch_symbols, + const ch_est_list& ch_estimates, + span noise_var_estimates, + float tx_scaling) { // Make sure that the input and output symbol lists and channel estimate dimensions are valid. assert_sizes(eq_symbols, eq_noise_vars, ch_symbols, ch_estimates, noise_var_estimates); @@ -175,8 +175,8 @@ void channel_equalizer_generic_impl::equalize(span eq_symbols, srsran_assert(tx_scaling > 0, "Tx scaling factor must be positive."); // Channel dimensions. - unsigned nof_rx_ports = ch_estimates.get_dimension_size(ch_est_list::dims::rx_port); - unsigned nof_tx_layers = ch_estimates.get_dimension_size(ch_est_list::dims::tx_layer); + unsigned nof_rx_ports = ch_estimates.get_nof_rx_ports(); + unsigned nof_tx_layers = ch_estimates.get_nof_tx_layers(); // Select the most pessimistic noise variance. float noise_var = *std::max_element(noise_var_estimates.begin(), noise_var_estimates.end()); @@ -215,7 +215,7 @@ void channel_equalizer_generic_impl::equalize(span eq_symbols, srsran_assertion_failure( "Invalid combination of channel spatial topology (i.e., {} Rx ports, {} Tx layers) and algorithm (i.e., {}).", - ch_estimates.get_dimension_size(ch_est_list::dims::rx_port), - ch_estimates.get_dimension_size(ch_est_list::dims::tx_layer), + ch_estimates.get_nof_rx_ports(), + ch_estimates.get_nof_tx_layers(), to_string(type)); } diff --git a/lib/phy/upper/equalization/channel_equalizer_generic_impl.h b/lib/phy/upper/equalization/channel_equalizer_generic_impl.h index c750ad3948..a791886a9c 100644 --- a/lib/phy/upper/equalization/channel_equalizer_generic_impl.h +++ b/lib/phy/upper/equalization/channel_equalizer_generic_impl.h @@ -38,12 +38,12 @@ class channel_equalizer_generic_impl : public channel_equalizer explicit channel_equalizer_generic_impl(channel_equalizer_algorithm_type type_) : type(type_) {} // See interface for documentation. - void equalize(span eq_symbols, - span eq_noise_vars, - const re_list& ch_symbols, - const ch_est_list& ch_estimates, - span noise_var_estimates, - float tx_scaling) override; + void equalize(span eq_symbols, + span eq_noise_vars, + const re_buffer_reader& ch_symbols, + const ch_est_list& ch_estimates, + span noise_var_estimates, + float tx_scaling) override; private: channel_equalizer_algorithm_type type; diff --git a/lib/phy/upper/equalization/equalize_mmse_1xn.h b/lib/phy/upper/equalization/equalize_mmse_1xn.h index e9a6df8240..2ba2c00ba1 100644 --- a/lib/phy/upper/equalization/equalize_mmse_1xn.h +++ b/lib/phy/upper/equalization/equalize_mmse_1xn.h @@ -26,8 +26,6 @@ #pragma once #include "../../../srsvec/simd.h" -#include "srsran/srsvec/fill.h" -#include "srsran/srsvec/mean.h" #include "srsran/srsvec/zero.h" namespace srsran { @@ -43,13 +41,13 @@ namespace srsran { template void equalize_mmse_1xn(span symbols_out, span nvars_out, - const channel_equalizer::re_list& ch_symbols, + const re_buffer_reader& ch_symbols, const channel_equalizer::ch_est_list& ch_estimates, span noise_var_est, float tx_scaling) { // Number of RE to process. - unsigned nof_re = ch_symbols.get_dimension_size(channel_equalizer::re_list::dims::re); + unsigned nof_re = ch_symbols.get_nof_re(); unsigned i_re = 0; @@ -60,8 +58,8 @@ void equalize_mmse_1xn(span symbols_out, for (unsigned i_port = 0; i_port != RX_PORTS; ++i_port) { // Get the input RE and channel estimate coefficient. - cf_t re_in = to_cf(ch_symbols[{i_re, i_port}]); - cf_t ch_est = to_cf(ch_estimates[{i_re, i_port}]) * tx_scaling; + cf_t re_in = to_cf(ch_symbols.get_slice(i_port)[i_re]); + cf_t ch_est = to_cf(ch_estimates.get_channel(i_port, 0)[i_re]) * tx_scaling; // Compute the channel square norm. float ch_est_norm = std::norm(ch_est); diff --git a/lib/phy/upper/equalization/equalize_zf_1xn.h b/lib/phy/upper/equalization/equalize_zf_1xn.h index cce293fd5a..c816c36ebc 100644 --- a/lib/phy/upper/equalization/equalize_zf_1xn.h +++ b/lib/phy/upper/equalization/equalize_zf_1xn.h @@ -26,6 +26,7 @@ #pragma once #include "../../../srsvec/simd.h" +#include "srsran/phy/constants.h" #include "srsran/srsvec/fill.h" #include "srsran/srsvec/zero.h" @@ -42,26 +43,27 @@ namespace srsran { template void equalize_zf_1xn(span symbols_out, span nvars_out, - const channel_equalizer::re_list& ch_symbols, + const re_buffer_reader& ch_symbols, const channel_equalizer::ch_est_list& ch_estimates, span noise_var_est, float tx_scaling) { // Number of RE to process. - unsigned nof_re = ch_symbols.get_dimension_size(channel_equalizer::re_list::dims::re); + unsigned nof_re = ch_symbols.get_nof_re(); unsigned i_re = 0; -#if defined(__AVX2__) || defined(__ARM_NEON) // Views over the input data. std::array, MAX_PORTS> port_symbols; std::array, MAX_PORTS> port_ests; for (unsigned i_port = 0; i_port != RX_PORTS; ++i_port) { - port_symbols[i_port] = ch_symbols.get_view({i_port}); - port_ests[i_port] = ch_estimates.get_view({i_port, 0}); + port_symbols[i_port] = ch_symbols.get_slice(i_port); + port_ests[i_port] = ch_estimates.get_channel(i_port, 0); } +#if defined(__AVX2__) || defined(__ARM_NEON) + // Create registers with zero and infinity values. simd_cf_t cf_zero = srsran_simd_cf_zero(); simd_f_t zero = srsran_simd_f_zero(); @@ -140,8 +142,8 @@ void equalize_zf_1xn(span symbols_out, for (unsigned i_port = 0; i_port != RX_PORTS; ++i_port) { // Get the input RE and channel estimate coefficient. - cf_t re_in = to_cf(ch_symbols[{i_re, i_port}]); - cf_t ch_est = to_cf(ch_estimates[{i_re, i_port}]); + cf_t re_in = to_cf(port_symbols[i_port][i_re]); + cf_t ch_est = to_cf(port_ests[i_port][i_re]); // Compute the channel square norm. float ch_est_norm = std::norm(ch_est); diff --git a/lib/phy/upper/equalization/equalize_zf_2xn.h b/lib/phy/upper/equalization/equalize_zf_2xn.h index df0deefb94..c62e84278c 100644 --- a/lib/phy/upper/equalization/equalize_zf_2xn.h +++ b/lib/phy/upper/equalization/equalize_zf_2xn.h @@ -43,7 +43,7 @@ namespace srsran { template void equalize_zf_2xn(span eq_symbols, span noise_vars, - const channel_equalizer::re_list& ch_symbols, + const re_buffer_reader& ch_symbols, const channel_equalizer::ch_est_list& ch_estimates_, float noise_var_est, float tx_scaling) @@ -53,7 +53,7 @@ void equalize_zf_2xn(span eq_symbols, static_assert(nof_ports >= nof_layers, "The number of ports must be greater than or equal to the number of layers."); unsigned i_re = 0; - const unsigned nof_re = ch_symbols.get_dimension_size(channel_equalizer::re_list::dims::re); + const unsigned nof_re = ch_symbols.get_nof_re(); // Skip processing if the noise variance is NaN, infinity or negative. if (!std::isnormal(noise_var_est) || (noise_var_est < 0.0F)) { @@ -65,14 +65,14 @@ void equalize_zf_2xn(span eq_symbols, // Views over input channel symbols, organized by receive port. std::array, nof_ports> symbols_in; for (unsigned i_port = 0; i_port != nof_ports; ++i_port) { - symbols_in[i_port] = ch_symbols.get_view<>({i_port}); + symbols_in[i_port] = ch_symbols.get_slice(i_port); } // Views over channel estimates, organized by receive port and transmit layer. std::array, nof_layers>, nof_ports> ch_estimates; for (unsigned i_layer = 0; i_layer != nof_layers; ++i_layer) { for (unsigned i_port = 0; i_port != nof_ports; ++i_port) { - ch_estimates[i_port][i_layer] = ch_estimates_.get_view<>({i_port, i_layer}); + ch_estimates[i_port][i_layer] = ch_estimates_.get_channel(i_port, i_layer); } } diff --git a/lib/phy/upper/log_likelihood_ratio.cpp b/lib/phy/upper/log_likelihood_ratio.cpp index bc2b84349f..13ed8bcced 100644 --- a/lib/phy/upper/log_likelihood_ratio.cpp +++ b/lib/phy/upper/log_likelihood_ratio.cpp @@ -21,6 +21,7 @@ */ #include "srsran/phy/upper/log_likelihood_ratio.h" +#include "../lib/srsvec/simd.h" #include "srsran/adt/optional.h" #include "srsran/srsvec/compare.h" #include @@ -223,6 +224,92 @@ static void hard_decision_simd(bit_buffer& hard_bits, const int8_t* soft_bits, u } #endif // __ARM_NEON +void srsran::clamp(span out, + span in, + log_likelihood_ratio low, + log_likelihood_ratio high) +{ + srsran_assert(out.size() == in.size(), + "Input size (i.e., {}) is not equal to the output size (i.e., {}).", + in.size(), + out.size()); + unsigned len = in.size(); + unsigned i = 0; + +#if defined(__AVX512F__) && defined(__AVX512BW__) + const __m512i* in_ptr = reinterpret_cast(in.data()); + __m512i* out_ptr = reinterpret_cast<__m512i*>(out.data()); + + __m512i low_epi8 = _mm512_set1_epi8(low.to_int()); + __m512i high_epi8 = _mm512_set1_epi8(high.to_int()); + + // Clamping function. + const auto clamp_func = [&low_epi8, &high_epi8](__m512i in_epi8) { + __m512i temp = in_epi8; + + __mmask64 high_mask = _mm512_cmp_epi8_mask(temp, high_epi8, _MM_CMPINT_GT); + temp = _mm512_mask_blend_epi8(high_mask, temp, high_epi8); + + __mmask64 low_mask = _mm512_cmp_epi8_mask(low_epi8, temp, _MM_CMPINT_GT); + temp = _mm512_mask_blend_epi8(low_mask, temp, low_epi8); + + return temp; + }; + + // Clamps 64 LLR at a time. + for (unsigned len_simd = (len / 64) * 64; i != len_simd; i += 64) { + _mm512_storeu_si512(out_ptr++, clamp_func(_mm512_loadu_si512(in_ptr++))); + } + + // Clamps the rest. + __mmask64 remainder_mask = (1UL << (len % 64UL)) - 1UL; + _mm512_mask_storeu_epi8(out_ptr, remainder_mask, clamp_func(_mm512_maskz_loadu_epi8(remainder_mask, in_ptr))); +#else // defined(__AVX512F__) && defined(__AVX512BW__) +#ifdef __AVX2__ + const __m256i* in_ptr = reinterpret_cast(in.data()); + __m256i* out_ptr = reinterpret_cast<__m256i*>(out.data()); + + __m256i low_epi8 = _mm256_set1_epi8(low.to_int()); + __m256i high_epi8 = _mm256_set1_epi8(high.to_int()); + + for (unsigned len_simd = (len / 32) * 32; i != len_simd; i += 32) { + __m256i temp = _mm256_loadu_si256(in_ptr++); + + __m256i high_mask = _mm256_cmpgt_epi8(temp, high_epi8); + temp = _mm256_blendv_epi8(temp, high_epi8, high_mask); + + __m256i low_mask = _mm256_cmpgt_epi8(low_epi8, temp); + temp = _mm256_blendv_epi8(temp, low_epi8, low_mask); + + _mm256_storeu_si256(out_ptr++, temp); + } +#endif // __AVX2__ +#ifdef __ARM_NEON + int8x16_t low_s8 = vdupq_n_s8(static_cast(low.to_int())); + int8x16_t high_s8 = vdupq_n_s8(static_cast(high.to_int())); + + const int8_t* in_ptr = reinterpret_cast(in.data()); + int8_t* out_ptr = reinterpret_cast(out.data()); + + for (unsigned len_simd = (len / 16) * 16; i != len_simd; i += 16) { + int8x16_t temp = vld1q_s8(in_ptr + i); + + uint8x16_t high_mask = vcgtq_s8(temp, high_s8); + temp = vbslq_s8(high_mask, high_s8, temp); + + uint8x16_t low_mask = vcltq_s8(temp, low_s8); + temp = vbslq_s8(low_mask, low_s8, temp); + + vst1q_s8(out_ptr + i, temp); + } +#endif // __ARM_NEON + + for (; i != len; ++i) { + out[i] = std::clamp(in[i], low, high); + } +#endif // defined(__AVX512F__) && defined(__AVX512BW__) +} + bool srsran::hard_decision(bit_buffer& hard_bits, span soft_bits) { // Make sure that there is enough space in the output to accommodate the hard bits. diff --git a/lib/phy/upper/signal_processors/dmrs_pdcch_processor_impl.cpp b/lib/phy/upper/signal_processors/dmrs_pdcch_processor_impl.cpp index 80d20d57f5..6941ad8d16 100644 --- a/lib/phy/upper/signal_processors/dmrs_pdcch_processor_impl.cpp +++ b/lib/phy/upper/signal_processors/dmrs_pdcch_processor_impl.cpp @@ -49,9 +49,9 @@ void dmrs_pdcch_processor_impl::sequence_generation(span sequence, *prg, M_SQRT1_2 * config.amplitude, config.reference_point_k_rb, NOF_DMRS_PER_RB, config.rb_mask); } -void dmrs_pdcch_processor_impl::mapping(resource_grid_mapper& mapper, - const re_buffer_reader& d_pdcch, - const config_t& config) +void dmrs_pdcch_processor_impl::mapping(resource_grid_mapper& mapper, + const re_buffer_reader<>& d_pdcch, + const config_t& config) { // Resource element allocation within a resource block for PDCCH. static const re_prb_mask re_mask = {false, true, false, false, false, true, false, false, false, true, false, false}; diff --git a/lib/phy/upper/signal_processors/dmrs_pdcch_processor_impl.h b/lib/phy/upper/signal_processors/dmrs_pdcch_processor_impl.h index e768a04391..23ad87afad 100644 --- a/lib/phy/upper/signal_processors/dmrs_pdcch_processor_impl.h +++ b/lib/phy/upper/signal_processors/dmrs_pdcch_processor_impl.h @@ -75,7 +75,7 @@ class dmrs_pdcch_processor_impl : public dmrs_pdcch_processor /// \param[out] mapper Resource grid mapper interface. /// \param[in] d_pdcch PDCCH resource elements to map in the resource grid. /// \param[in] config PDCCH modulator parameters. - void mapping(resource_grid_mapper& mapper, const re_buffer_reader& d_pdcch, const config_t& config); + void mapping(resource_grid_mapper& mapper, const re_buffer_reader<>& d_pdcch, const config_t& config); public: explicit dmrs_pdcch_processor_impl(std::unique_ptr prg_) : prg(std::move(prg_)) diff --git a/lib/phy/upper/upper_phy_factories.cpp b/lib/phy/upper/upper_phy_factories.cpp index 89fdd705dd..214862695d 100644 --- a/lib/phy/upper/upper_phy_factories.cpp +++ b/lib/phy/upper/upper_phy_factories.cpp @@ -795,15 +795,15 @@ srsran::create_downlink_processor_factory_sw(const downlink_processor_factory_sw // 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."); + report_fatal_error_if_not(pdsch_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."); + report_fatal_error_if_not(ssb_proc_factory, "Invalid SSB processor pool factory."); nzp_csi_rs_factory = create_nzp_csi_rs_generator_pool_factory(std::move(nzp_csi_rs_factory), config.nof_concurrent_threads); - report_fatal_error_if_not(pdcch_proc_factory, "Invalid NZP-CSI-RS generator pool factory."); + report_fatal_error_if_not(nzp_csi_rs_factory, "Invalid NZP-CSI-RS generator pool factory."); } return std::make_shared( diff --git a/lib/ran/gnb_format.h b/lib/ran/gnb_format.h deleted file mode 100644 index 7f9529b1d6..0000000000 --- a/lib/ran/gnb_format.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#pragma once - -#include "srsran/ran/du_types.h" -#include "srsran/ran/lcid.h" -#include "srsran/ran/rnti.h" -#include "srsran/srslog/srslog.h" -#include "srsran/support/format_utils.h" - -namespace srsran { - -struct ue_event_prefix { - const char* direction; - du_ue_index_t ue_index; - rnti_t rnti; - du_cell_index_t cell_index; - const char* channel = nullptr; - lcid_t lcid; - - ue_event_prefix(const char* dir_ = "CTRL", - du_ue_index_t ue_index_ = MAX_NOF_DU_UES, - rnti_t rnti_ = rnti_t::INVALID_RNTI, - du_cell_index_t cell_idx_ = MAX_NOF_DU_CELLS, - const char* channel_ = nullptr, - lcid_t lcid_ = INVALID_LCID) : - direction(dir_), ue_index(ue_index_), rnti(rnti_), cell_index(cell_idx_), channel(channel_), lcid(lcid_) - { - } - - ue_event_prefix& set_type(const char* t) - { - direction = t; - return *this; - } - ue_event_prefix& set_channel(const char* ch_) - { - channel = ch_; - return *this; - } - - ue_event_prefix& operator|(rnti_t rnti_) - { - rnti = rnti_; - return *this; - } - - ue_event_prefix& operator|(du_ue_index_t ue_index_) - { - ue_index = ue_index_; - return *this; - } - - ue_event_prefix& operator|(du_cell_index_t cell_index_) - { - cell_index = cell_index_; - return *this; - } - - ue_event_prefix& operator|(lcid_t lcid_) - { - lcid = lcid_; - return *this; - } -}; - -inline void log_proc_started(srslog::basic_logger& logger, du_ue_index_t ue_index, rnti_t rnti, const char* proc_name) -{ - logger.debug("{}: \"{}\" started.", ue_event_prefix{"CTRL", ue_index, rnti}, proc_name); -} - -inline void log_proc_started(srslog::basic_logger& logger, du_ue_index_t ue_index, const char* proc_name) -{ - logger.debug("{}: \"{}\" started.", ue_event_prefix{"CTRL", ue_index}, proc_name); -} - -inline void log_proc_completed(srslog::basic_logger& logger, du_ue_index_t ue_index, rnti_t rnti, const char* proc_name) -{ - logger.debug("{}: \"{}\" completed.", ue_event_prefix{"CTRL", ue_index, rnti}, proc_name); -} - -template -void log_proc_failure(srslog::basic_logger& logger, - du_ue_index_t ue_index, - const char* proc_name, - const char* cause_fmt = "", - Args&&... args) -{ - fmt::memory_buffer fmtbuf; - if (strcmp(cause_fmt, "") != 0) { - fmt::format_to(fmtbuf, "Cause: "); - fmt::format_to(fmtbuf, cause_fmt, std::forward(args)...); - } - logger.warning("{}: \"{}\" failed. {}", ue_event_prefix{"CTRL", ue_index}, proc_name, to_c_str(fmtbuf)); -} - -template -void log_proc_failure(srslog::basic_logger& logger, - du_ue_index_t ue_index, - rnti_t rnti, - const char* proc_name, - const char* cause_fmt = "", - Args&&... args) -{ - fmt::memory_buffer fmtbuf; - if (strcmp(cause_fmt, "") != 0) { - fmt::format_to(fmtbuf, "Cause: "); - fmt::format_to(fmtbuf, cause_fmt, std::forward(args)...); - } - logger.warning("{}: \"{}\" failed. {}", ue_event_prefix{"CTRL", ue_index, rnti}, proc_name, to_c_str(fmtbuf)); -} - -} // namespace srsran - -namespace fmt { - -/// FMT formatter of slot_point type. -template <> -struct formatter { - template - auto parse(ParseContext& ctx) -> decltype(ctx.begin()) - { - return ctx.begin(); - } - template - auto format(const srsran::ue_event_prefix& ue_prefix, FormatContext& ctx) - -> decltype(std::declval().out()) - { - using namespace srsran; - auto ret = format_to(ctx.out(), "{:<4}", ue_prefix.direction); - if (ue_prefix.ue_index != srsran::MAX_NOF_DU_UES) { - ret = format_to(ctx.out(), " ueId={}", ue_prefix.ue_index); - } else { - ret = format_to(ctx.out(), "{: <7}", ""); - } - if (ue_prefix.rnti != srsran::rnti_t::INVALID_RNTI) { - ret = format_to(ctx.out(), " {}", ue_prefix.rnti); - } else { - ret = format_to(ctx.out(), " {: <6}", ""); - } - if (ue_prefix.cell_index != srsran::MAX_NOF_DU_CELLS) { - ret = format_to(ctx.out(), " cell={}", ue_prefix.cell_index); - } - if (ue_prefix.channel != nullptr) { - ret = format_to(ctx.out(), " {}", ue_prefix.channel); - } - if (ue_prefix.lcid <= MAX_LCID) { - ret = format_to(ctx.out(), " {}", ue_prefix.lcid); - } - return ret; - } -}; - -} // namespace fmt diff --git a/lib/rlc/rlc_base_entity.h b/lib/rlc/rlc_base_entity.h index bfe91637be..d089fae34e 100644 --- a/lib/rlc/rlc_base_entity.h +++ b/lib/rlc/rlc_base_entity.h @@ -103,8 +103,8 @@ class rlc_base_entity : public rlc_entity rlc_bearer_logger logger; du_ue_index_t ue_index; rb_id_t rb_id; - std::unique_ptr tx = {}; - std::unique_ptr rx = {}; + std::unique_ptr tx; + std::unique_ptr rx; timer_duration metrics_period; private: @@ -113,38 +113,11 @@ class rlc_base_entity : public rlc_entity void push_metrics() { - rlc_metrics m = get_metrics(); + rlc_metrics m = get_metrics(); + m.metrics_period = metrics_period; if (rlc_metrics_notif) { rlc_metrics_notif->report_metrics(m); } - if (m.tx.mode == rlc_mode::am) { - logger.log_info( - "TX metrics period={}ms num_sdus={} sdu_rate={}kbps, dropped_sdus={} discarded_sdus={} " - "num_pdus_no_segm={} pdu_rate_no_segm={}kbps num_pdus_with_segm={} pdu_rate_with_segm={}kbps num_retx={} " - "retx_rate={}kbps ctrl_pdus={} ctrl_rate={}kbps", - metrics_period.count(), - m.tx.num_sdus, - (double)m.tx.num_sdu_bytes * 8 / (double)metrics_period.count(), - m.tx.num_dropped_sdus, - m.tx.num_discarded_sdus, - m.tx.num_pdus_no_segmentation, - (double)m.tx.num_pdu_bytes_no_segmentation * 8 / (double)metrics_period.count(), - m.tx.mode_specific.am.num_pdus_with_segmentation, - (double)m.tx.mode_specific.am.num_pdu_bytes_with_segmentation * 8 / (double)metrics_period.count(), - m.tx.mode_specific.am.num_retx_pdus, - (double)m.tx.mode_specific.am.num_retx_pdu_bytes * 8 / (double)metrics_period.count(), - m.tx.mode_specific.am.num_ctrl_pdus, - (double)m.tx.mode_specific.am.num_ctrl_pdu_bytes * 8 / (double)metrics_period.count()); - logger.log_info("RX metrics period={}ms num_sdus={} sdu_rate={}kbps num_pdus={} pdu_rate={}kbps " - "ctrl_pdus={}, ctrl_rate={}kbps", - metrics_period.count(), - m.rx.num_sdus, - (double)m.rx.num_sdu_bytes * 8 / (double)metrics_period.count(), - m.rx.num_pdus, - (double)m.rx.num_pdu_bytes * 8 / (double)metrics_period.count(), - m.tx.mode_specific.am.num_ctrl_pdus, - (double)m.rx.mode_specific.am.num_ctrl_pdu_bytes * 8 / (double)metrics_period.count()); - } reset_metrics(); metrics_timer.run(); } diff --git a/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.cpp b/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.cpp index 16a908730e..ee543fe2d2 100644 --- a/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.cpp +++ b/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.cpp @@ -32,6 +32,7 @@ rrc_reconfiguration_procedure::rrc_reconfiguration_procedure(rrc_ue_context_t& const rrc_reconfiguration_procedure_request& args_, rrc_ue_reconfiguration_proc_notifier& rrc_ue_notifier_, rrc_ue_context_update_notifier& cu_cp_notifier_, + rrc_ue_cu_cp_ue_notifier& cu_cp_ue_notifier_, rrc_ue_event_manager& event_mng_, rrc_ue_srb_handler& srb_notifier_, rrc_ue_logger& logger_) : @@ -39,6 +40,7 @@ rrc_reconfiguration_procedure::rrc_reconfiguration_procedure(rrc_ue_context_t& args(args_), rrc_ue(rrc_ue_notifier_), cu_cp_notifier(cu_cp_notifier_), + cu_cp_ue_notifier(cu_cp_ue_notifier_), event_mng(event_mng_), srb_notifier(srb_notifier_), logger(logger_) @@ -77,11 +79,10 @@ void rrc_reconfiguration_procedure::operator()(coro_context>& c } else { logger.log_warning("\"{}\" timed out after {}ms", name(), context.cfg.rrc_procedure_timeout_ms); // Notify NGAP to request UE context release from AMF - CORO_AWAIT(cu_cp_notifier.on_ue_release_required( + cu_cp_ue_notifier.schedule_async_task(cu_cp_notifier.on_ue_release_required( {context.ue_index, {}, ngap_cause_radio_network_t::release_due_to_ngran_generated_reason})); } - logger.log_debug("\"{}\" finalized", name()); CORO_RETURN(procedure_result); } diff --git a/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.h b/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.h index d9eb3c04e4..30ea718969 100644 --- a/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.h +++ b/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.h @@ -41,7 +41,8 @@ class rrc_reconfiguration_procedure const rrc_reconfiguration_procedure_request& args_, rrc_ue_reconfiguration_proc_notifier& rrc_ue_notifier_, rrc_ue_context_update_notifier& cu_cp_notifier_, - rrc_ue_event_manager& ev_mng_, + rrc_ue_cu_cp_ue_notifier& cu_cp_ue_notifier_, + rrc_ue_event_manager& event_mng_, rrc_ue_srb_handler& srb_notifier_, rrc_ue_logger& logger_); @@ -56,10 +57,11 @@ class rrc_reconfiguration_procedure rrc_ue_context_t& context; const rrc_reconfiguration_procedure_request args; - rrc_ue_reconfiguration_proc_notifier& rrc_ue; // handler to the parent RRC UE object - rrc_ue_context_update_notifier& cu_cp_notifier; // to release the UE if the reconfiguration fails - rrc_ue_event_manager& event_mng; // event manager for the RRC UE entity - rrc_ue_srb_handler& srb_notifier; // For creating SRBs + rrc_ue_reconfiguration_proc_notifier& rrc_ue; // handler to the parent RRC UE object + rrc_ue_context_update_notifier& cu_cp_notifier; // to release the UE if the reconfiguration fails + rrc_ue_cu_cp_ue_notifier& cu_cp_ue_notifier; // to schedule the UE release + rrc_ue_event_manager& event_mng; // event manager for the RRC UE entity + rrc_ue_srb_handler& srb_notifier; // For creating SRBs rrc_ue_logger& logger; rrc_transaction transaction; diff --git a/lib/rrc/ue/rrc_ue_impl.h b/lib/rrc/ue/rrc_ue_impl.h index f4de78f9ae..f6c696dffe 100644 --- a/lib/rrc/ue/rrc_ue_impl.h +++ b/lib/rrc/ue/rrc_ue_impl.h @@ -56,14 +56,15 @@ class rrc_ue_impl final : public rrc_ue_interface, public rrc_ue_controller void handle_ul_dcch_pdu(const srb_id_t srb_id, byte_buffer pdcp_pdu) override; // rrc_ue_interface - rrc_ue_controller& get_controller() override { return *this; } - rrc_ul_ccch_pdu_handler& get_ul_ccch_pdu_handler() override { return *this; } - rrc_ul_dcch_pdu_handler& get_ul_dcch_pdu_handler() override { return *this; } - rrc_dl_nas_message_handler& get_rrc_dl_nas_message_handler() override { return *this; } - rrc_ue_srb_handler& get_rrc_ue_srb_handler() override { return *this; } - rrc_ue_control_message_handler& get_rrc_ue_control_message_handler() override { return *this; } - rrc_ue_context_handler& get_rrc_ue_context_handler() override { return *this; } - rrc_ue_handover_preparation_handler& get_rrc_ue_handover_preparation_handler() override { return *this; } + rrc_ue_controller& get_controller() override { return *this; } + rrc_ul_ccch_pdu_handler& get_ul_ccch_pdu_handler() override { return *this; } + rrc_ul_dcch_pdu_handler& get_ul_dcch_pdu_handler() override { return *this; } + rrc_dl_nas_message_handler& get_rrc_dl_nas_message_handler() override { return *this; } + rrc_ue_srb_handler& get_rrc_ue_srb_handler() override { return *this; } + rrc_ue_control_message_handler& get_rrc_ue_control_message_handler() override { return *this; } + rrc_ue_radio_access_capability_handler& get_rrc_ue_radio_access_capability_handler() override { return *this; } + rrc_ue_context_handler& get_rrc_ue_context_handler() override { return *this; } + rrc_ue_handover_preparation_handler& get_rrc_ue_handover_preparation_handler() override { return *this; } // rrc_ue_srb_handler void create_srb(const srb_creation_message& msg) override; @@ -74,13 +75,15 @@ class rrc_ue_impl final : public rrc_ue_interface, public rrc_ue_controller // rrc_ue_control_message_handler rrc_ue_security_mode_command_context get_security_mode_command_context() override; - async_task handle_security_mode_command_complete_expected(uint8_t transaction_id) override; + async_task handle_security_mode_complete_expected(uint8_t transaction_id) override; + byte_buffer get_packed_ue_capability_rat_container_list() const override; + byte_buffer get_packed_ue_radio_access_cap_info() const override; async_task handle_rrc_reconfiguration_request(const rrc_reconfiguration_procedure_request& msg) override; rrc_ue_handover_reconfiguration_context get_rrc_ue_handover_reconfiguration_context(const rrc_reconfiguration_procedure_request& request) override; async_task handle_handover_reconfiguration_complete_expected(uint8_t transaction_id) override; async_task handle_rrc_ue_capability_transfer_request(const rrc_ue_capability_transfer_request& msg) override; - rrc_ue_release_context get_rrc_ue_release_context(bool requires_rrc_msg) override; + rrc_ue_release_context get_rrc_ue_release_context(bool requires_rrc_message) override; rrc_ue_transfer_context get_transfer_context() override; std::optional generate_meas_config(std::optional current_meas_config) override; byte_buffer get_rrc_handover_command(const rrc_reconfiguration_procedure_request& request, diff --git a/lib/rrc/ue/rrc_ue_message_handlers.cpp b/lib/rrc/ue/rrc_ue_message_handlers.cpp index 47ba416dbf..939a958446 100644 --- a/lib/rrc/ue/rrc_ue_message_handlers.cpp +++ b/lib/rrc/ue/rrc_ue_message_handlers.cpp @@ -164,6 +164,9 @@ void rrc_ue_impl::handle_pdu(const srb_id_t srb_id, byte_buffer rrc_pdu) case ul_dcch_msg_type_c::c1_c_::types_opts::security_mode_complete: handle_rrc_transaction_complete(ul_dcch_msg, ul_dcch_msg.msg.c1().security_mode_complete().rrc_transaction_id); break; + case ul_dcch_msg_type_c::c1_c_::types_opts::security_mode_fail: + handle_rrc_transaction_complete(ul_dcch_msg, ul_dcch_msg.msg.c1().security_mode_fail().rrc_transaction_id); + break; case ul_dcch_msg_type_c::c1_c_::types_opts::ue_cap_info: handle_rrc_transaction_complete(ul_dcch_msg, ul_dcch_msg.msg.c1().ue_cap_info().rrc_transaction_id); break; @@ -321,7 +324,7 @@ rrc_ue_security_mode_command_context rrc_ue_impl::get_security_mode_command_cont return smc_ctxt; } -async_task rrc_ue_impl::handle_security_mode_command_complete_expected(uint8_t transaction_id) +async_task rrc_ue_impl::handle_security_mode_complete_expected(uint8_t transaction_id) { // arbitrary timeout for RRC Reconfig procedure, UE will be removed if timer fires const std::chrono::milliseconds timeout_ms{1000}; @@ -330,29 +333,63 @@ async_task rrc_ue_impl::handle_security_mode_command_complete_expected(uin [this, timeout_ms, transaction_id, transaction = rrc_transaction{}](coro_context>& ctx) mutable { CORO_BEGIN(ctx); - logger.log_debug("Awaiting RRC Security Mode Command Complete (timeout={}ms)", timeout_ms.count()); + logger.log_debug("Awaiting RRC Security Mode Complete (timeout={}ms)", timeout_ms.count()); // create new transaction for RRC Security Mode Command procedure transaction = event_mng->transactions.create_transaction(transaction_id, timeout_ms); CORO_AWAIT(transaction); - bool procedure_result = false; - if (transaction.has_response()) { - logger.log_debug("Received RRC Security Mode Command Complete"); - procedure_result = true; + if (!transaction.has_response()) { + logger.log_debug("Did not receive RRC Security Mode Complete. Cause: timeout"); + CORO_EARLY_RETURN(false); + } + + if (transaction.response().msg.c1().type() == ul_dcch_msg_type_c::c1_c_::types_opts::security_mode_fail) { + logger.log_warning("Received RRC Security Mode Failure"); + CORO_EARLY_RETURN(false); + } + + if (transaction.response().msg.c1().type() == ul_dcch_msg_type_c::c1_c_::types_opts::security_mode_complete) { + logger.log_debug("Received RRC Security Mode Complete"); handle_security_mode_complete(transaction.response().msg.c1().security_mode_complete()); - } else { - logger.log_debug("Did not receive RRC Security Mode Command Complete. Cause: timeout"); } - CORO_RETURN(procedure_result); + CORO_RETURN(true); }); } +byte_buffer rrc_ue_impl::get_packed_ue_capability_rat_container_list() const +{ + byte_buffer pdu{}; + + if (context.capabilities_list.has_value()) { + asn1::bit_ref bref{pdu}; + + if (pack_dyn_seq_of(bref, context.capabilities_list.value(), 0, 8) != asn1::SRSASN_SUCCESS) { + logger.log_error("Error packing UE Capability RAT Container List"); + return byte_buffer{}; + } + } else { + logger.log_debug("No UE capabilites available"); + } + + return pdu.copy(); +} + +byte_buffer rrc_ue_impl::get_packed_ue_radio_access_cap_info() const +{ + asn1::rrc_nr::ue_radio_access_cap_info_s ue_radio_access_cap_info; + asn1::rrc_nr::ue_radio_access_cap_info_ies_s& ue_radio_access_cap_info_ies = + ue_radio_access_cap_info.crit_exts.set_c1().set_ue_radio_access_cap_info(); + ue_radio_access_cap_info_ies.ue_radio_access_cap_info = get_packed_ue_capability_rat_container_list(); + + return pack_into_pdu(ue_radio_access_cap_info, "UE Radio Access Cap Info"); +} + async_task rrc_ue_impl::handle_rrc_reconfiguration_request(const rrc_reconfiguration_procedure_request& msg) { return launch_async( - context, msg, *this, cu_cp_notifier, *event_mng, get_rrc_ue_srb_handler(), logger); + context, msg, *this, cu_cp_notifier, cu_cp_ue_notifier, *event_mng, get_rrc_ue_srb_handler(), logger); } rrc_ue_handover_reconfiguration_context @@ -460,26 +497,26 @@ rrc_ue_release_context rrc_ue_impl::get_rrc_ue_release_context(bool requires_rrc if (context.srbs.find(srb_id_t::srb1) == context.srbs.end()) { logger.log_error("Can't create RRCRelease PDU. RX {} is not set up", srb_id_t::srb1); return release_context; - } else { - dl_dcch_msg_s dl_dcch_msg; - dl_dcch_msg.msg.set_c1().set_rrc_release().crit_exts.set_rrc_release(); - - // pack DL CCCH msg - pdcp_tx_result pdcp_packing_result = - context.srbs.at(srb_id_t::srb1).pack_rrc_pdu(pack_into_pdu(dl_dcch_msg, "RRCRelease")); - if (!pdcp_packing_result.is_successful()) { - logger.log_info("Requesting UE release. Cause: PDCP packing failed with {}", - pdcp_packing_result.get_failure_cause()); - on_ue_release_required(pdcp_packing_result.get_failure_cause()); - return release_context; - } + } - release_context.rrc_release_pdu = pdcp_packing_result.pop_pdu(); - release_context.srb_id = srb_id_t::srb1; + dl_dcch_msg_s dl_dcch_msg; + dl_dcch_msg.msg.set_c1().set_rrc_release().crit_exts.set_rrc_release(); - // Log Tx message - log_rrc_message(logger, Tx, release_context.rrc_release_pdu, dl_dcch_msg, "DCCH DL"); + // pack DL CCCH msg + pdcp_tx_result pdcp_packing_result = + context.srbs.at(srb_id_t::srb1).pack_rrc_pdu(pack_into_pdu(dl_dcch_msg, "RRCRelease")); + if (!pdcp_packing_result.is_successful()) { + logger.log_info("Requesting UE release. Cause: PDCP packing failed with {}", + pdcp_packing_result.get_failure_cause()); + on_ue_release_required(pdcp_packing_result.get_failure_cause()); + return release_context; } + + release_context.rrc_release_pdu = pdcp_packing_result.pop_pdu(); + release_context.srb_id = srb_id_t::srb1; + + // Log Tx message + log_rrc_message(logger, Tx, release_context.rrc_release_pdu, dl_dcch_msg, "DCCH DL"); } // Log Tx message @@ -507,6 +544,8 @@ rrc_ue_transfer_context rrc_ue_impl::get_transfer_context() transfer_context.srbs = get_srbs(); transfer_context.up_ctx = cu_cp_notifier.on_up_context_required(); transfer_context.handover_preparation_info = get_packed_handover_preparation_message(); + transfer_context.ue_cap_rat_container_list = get_packed_ue_capability_rat_container_list(); + return transfer_context; } diff --git a/lib/scheduler/config/sched_cell_config_helpers.cpp b/lib/scheduler/config/sched_cell_config_helpers.cpp index 07a13171b5..5bf4fa3282 100644 --- a/lib/scheduler/config/sched_cell_config_helpers.cpp +++ b/lib/scheduler/config/sched_cell_config_helpers.cpp @@ -33,11 +33,11 @@ srsran::config_helpers::build_pucch_guardbands_list(const pucch_builder_params& { // Compute the cell PUCCH resource list, depending on which parameter that has been passed. std::vector res_list = srs_du::generate_cell_pucch_res_list( - user_params.nof_ue_pucch_f1_res_harq.to_uint() * user_params.nof_cell_harq_pucch_res_sets + + user_params.nof_ue_pucch_f0_or_f1_res_harq.to_uint() * user_params.nof_cell_harq_pucch_res_sets + user_params.nof_sr_resources, user_params.nof_ue_pucch_f2_res_harq.to_uint() * user_params.nof_cell_harq_pucch_res_sets + user_params.nof_csi_resources, - user_params.f1_params, + user_params.f0_or_f1_params, user_params.f2_params, bwp_size); diff --git a/lib/scheduler/config/serving_cell_config_validator.cpp b/lib/scheduler/config/serving_cell_config_validator.cpp index 502062db26..9b814784ca 100644 --- a/lib/scheduler/config/serving_cell_config_validator.cpp +++ b/lib/scheduler/config/serving_cell_config_validator.cpp @@ -27,6 +27,7 @@ #include "srsran/ran/pucch/pucch_info.h" #include "srsran/scheduler/sched_consts.h" #include "srsran/support/config/validator_helpers.h" +#include using namespace srsran; @@ -35,6 +36,21 @@ using namespace srsran; return make_unexpected(fmt::format(__VA_ARGS__)); \ } +static bool +csi_offset_colliding_with_sr(unsigned sr_offset, unsigned csi_offset, unsigned sr_period, unsigned csi_period) +{ + unsigned lcm_csi_sr_period = std::lcm(sr_period, csi_period); + for (unsigned csi_off = csi_offset; csi_off < lcm_csi_sr_period; csi_off += csi_period) { + for (unsigned sr_off = sr_offset; sr_off < lcm_csi_sr_period; sr_off += sr_period) { + if (csi_off == sr_off) { + return true; + } + } + } + + return false; +}; + validator_result srsran::config_validators::validate_pdcch_cfg(const serving_cell_config& ue_cell_cfg, const dl_config_common& dl_cfg_common) { @@ -144,6 +160,7 @@ validator_result srsran::config_validators::validate_pucch_cfg(const serving_cel [res_id](const pucch_resource& res) { return res.res_id.cell_res_id == res_id; }); }; + // NOTE: No need to check this for Format 0, as this struct doesn't exist for F0. VERIFY(pucch_cfg.format_1_common_param.has_value(), "Missing PUCCH-format1 parameters in PUCCH-Config"); VERIFY(pucch_cfg.format_2_common_param.has_value(), "Missing PUCCH-format2 parameters in PUCCH-Config"); @@ -179,11 +196,22 @@ validator_result srsran::config_validators::validate_pucch_cfg(const serving_cel res.res_id.cell_res_id); } + // Verify that PUCCH Format 0 and Format 1 aren't both present in the UE configuration. + bool has_format_0 = false; + bool has_format_1 = false; + for (auto res : pucch_cfg.pucch_res_list) { + has_format_0 = has_format_0 or res.format == pucch_format::FORMAT_0; + has_format_1 = has_format_1 or res.format == pucch_format::FORMAT_1; + } + VERIFY(not(has_format_0 and has_format_1), + "Only PUCCH Format 0 or Format 1 can be configured in a UE configuration, not both."); + // Check PUCCH Formats for each PUCCH Resource Set. for (auto res_idx : pucch_cfg.pucch_res_set[0].pucch_res_id_list) { const auto* pucch_res_it = get_pucch_resource_with_id(res_idx.cell_res_id); - VERIFY(pucch_cfg.pucch_res_list.end() != pucch_res_it and pucch_res_it->format == pucch_format::FORMAT_1, - "Only PUCCH Resource Format 1 expected in PUCCH resource set 0."); + VERIFY(pucch_cfg.pucch_res_list.end() != pucch_res_it and + (pucch_res_it->format == pucch_format::FORMAT_0 or pucch_res_it->format == pucch_format::FORMAT_1), + "Only PUCCH Resource Format 0 or Format 1 expected in PUCCH resource set 0."); } for (auto res_idx : pucch_cfg.pucch_res_set[1].pucch_res_id_list) { const auto* pucch_res_it = get_pucch_resource_with_id(res_idx.cell_res_id); @@ -235,11 +263,21 @@ validator_result srsran::config_validators::validate_pucch_cfg(const serving_cel const auto csi_report_cfg = create_csi_report_configuration(ue_cell_cfg.csi_meas_cfg.value()); const unsigned csi_report_size = get_csi_report_pucch_size(csi_report_cfg).value(); unsigned sr_offset = pucch_cfg.sr_res_list.front().offset; + const bool csi_sr_collision = + csi_offset_colliding_with_sr(sr_offset, + csi.report_slot_offset, + sr_periodicity_to_slot(pucch_cfg.sr_res_list.front().period), + csi_report_periodicity_to_uint(csi.report_slot_period)); + + // Verify that, with Format 0, the CSI and SR don't fall on the same slot(s). + if (pucch_res_sr->format == pucch_format::FORMAT_0) { + VERIFY(not csi_sr_collision, + "With PUCCH Format 0, we don't support SR opportunities falling on a CSI report slot"); + } + // If SR and CSI are reported within the same slot, 1 SR bit can be multiplexed with CSI within the same PUCCH // resource. - const unsigned csi_period = csi_report_periodicity_to_uint(csi.report_slot_period); - const unsigned lowest_period = std::min(sr_periodicity_to_slot(pucch_cfg.sr_res_list.front().period), csi_period); - unsigned sr_bits_mplexed_with_csi = sr_offset % lowest_period != csi.report_slot_offset % lowest_period ? 0U : 1U; + unsigned sr_bits_mplexed_with_csi = csi_sr_collision ? 1U : 0U; // In the PUCCH resource for CSI, there are no HARQ-ACK bits being reported; therefore we only need to check where // the CSI + SR bits fit into the max payload. const unsigned uci_bits_pucch_resource = csi_report_size + sr_bits_mplexed_with_csi; @@ -255,7 +293,7 @@ validator_result srsran::config_validators::validate_pucch_cfg(const serving_cel const unsigned uci_bits_harq_resource = csi_report_size + harq_bits_mplexed_with_csi + sr_bits_mplexed_with_csi; const unsigned pucch_res_set_idx_for_f2 = 1; for (pucch_res_id_t res_idx : pucch_cfg.pucch_res_set[pucch_res_set_idx_for_f2].pucch_res_id_list) { - auto* res_f2_it = get_pucch_resource_with_id(res_idx.cell_res_id); + const auto* res_f2_it = get_pucch_resource_with_id(res_idx.cell_res_id); const auto& harq_f2_pucch_res_params = std::get(res_f2_it->format_params); const unsigned pucch_harq_f2_max_payload = get_pucch_format2_max_payload(harq_f2_pucch_res_params.nof_prbs, diff --git a/lib/scheduler/config/ue_configuration.cpp b/lib/scheduler/config/ue_configuration.cpp index 214af7e934..3a1a37d7a7 100644 --- a/lib/scheduler/config/ue_configuration.cpp +++ b/lib/scheduler/config/ue_configuration.cpp @@ -27,7 +27,7 @@ #include "../support/pdsch/pdsch_resource_allocation.h" #include "../support/pusch/pusch_default_time_allocation.h" #include "../support/pusch/pusch_resource_allocation.h" -#include "srsran/support/math/gcd.h" +#include "srsran/support/math/lcm.h" #include using namespace srsran; @@ -526,7 +526,7 @@ static void generate_crnti_monitored_pdcch_candidates(bwp_info& bwp_cfg, rnti_t ss_periods.push_back(ss->cfg->get_monitoring_slot_periodicity()); } max_slot_periodicity = lcm(ss_periods); - max_slot_periodicity = lcm(max_slot_periodicity, slots_per_frame); + max_slot_periodicity = std::lcm(max_slot_periodicity, slots_per_frame); } frame_pdcch_candidate_list candidates; diff --git a/lib/scheduler/policy/scheduler_time_pf.cpp b/lib/scheduler/policy/scheduler_time_pf.cpp index 4e8cad282d..b335afa780 100644 --- a/lib/scheduler/policy/scheduler_time_pf.cpp +++ b/lib/scheduler/policy/scheduler_time_pf.cpp @@ -132,7 +132,8 @@ alloc_result scheduler_time_pf::try_dl_alloc(ue_ctxt& ctxt, const ue_repository& 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(); + grant.recommended_nof_bytes = ctxt.dl_newtx_srb_pending_bytes > 0 ? ctxt.dl_newtx_srb_pending_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; @@ -161,7 +162,8 @@ alloc_result scheduler_time_pf::try_ul_alloc(ue_ctxt& ctxt, const ue_repository& 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(); + grant.recommended_nof_bytes = ctxt.ul_newtx_srb_pending_bytes > 0 ? ctxt.ul_newtx_srb_pending_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; @@ -176,17 +178,30 @@ alloc_result scheduler_time_pf::try_ul_alloc(ue_ctxt& ctxt, const ue_repository& 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); + dl_retx_h = nullptr; + dl_newtx_h = nullptr; + dl_prio = 0; + dl_newtx_srb_pending_bytes = 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; } + // Find DL HARQ with transport block containing SRB data to retransmit. + const dl_harq_process* srb_retx_h = nullptr; + for (unsigned i = 0; i != ue_cc->harqs.nof_dl_harqs(); ++i) { + const dl_harq_process& h = ue_cc->harqs.dl_harq(i); + if (h.has_pending_retx() and not h.last_alloc_params().is_fallback and + h.last_alloc_params().tb[0]->contains_srb_data) { + srb_retx_h = &h; + break; + } + } + // Calculate DL priority. - dl_retx_h = ue_cc->harqs.find_pending_dl_retx(); - dl_newtx_h = ue_cc->harqs.find_empty_dl_harq(); + dl_retx_h = srb_retx_h != nullptr ? srb_retx_h : ue_cc->harqs.find_pending_dl_retx(); + dl_newtx_h = ue_cc->harqs.find_empty_dl_harq(); + dl_newtx_srb_pending_bytes = u.pending_dl_srb_newtx_bytes(); 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. @@ -239,19 +254,21 @@ void scheduler_time_pf::ue_ctxt::compute_dl_prio(const ue& u) 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); + ul_retx_h = nullptr; + ul_newtx_h = nullptr; + ul_prio = 0; + sr_ind_received = false; + ul_newtx_srb_pending_bytes = 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 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(); + 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(); + ul_newtx_srb_pending_bytes = u.pending_ul_srb_newtx_bytes(); 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. @@ -336,7 +353,42 @@ bool scheduler_time_pf::ue_dl_prio_compare::operator()(const scheduler_time_pf:: { 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); + const bool is_lhs_srb_retx = + lhs->dl_retx_h != nullptr and (lhs->dl_retx_h->last_alloc_params().tb[0]->contains_srb_data or + (lhs->dl_retx_h->last_alloc_params().tb[1].has_value() and + lhs->dl_retx_h->last_alloc_params().tb[1]->contains_srb_data)); + const bool is_rhs_srb_retx = + rhs->dl_retx_h != nullptr and (rhs->dl_retx_h->last_alloc_params().tb[0]->contains_srb_data or + (rhs->dl_retx_h->last_alloc_params().tb[1].has_value() and + rhs->dl_retx_h->last_alloc_params().tb[1]->contains_srb_data)); + + // First, prioritize UEs with SRB data re-transmissions. + // SRB HARQ retransmission in one UE and not in other UE. + if (is_lhs_srb_retx != is_rhs_srb_retx) { + return is_rhs_srb_retx; + } + // SRB HARQ retransmission in both UEs. + if (is_lhs_srb_retx) { + return lhs->dl_prio < rhs->dl_prio; + } + // Second, prioritize UEs with SRB data new transmission. + // SRB HARQ newTx in one UE and not in other UE. + const bool lhs_has_dl_newtx_srb_pending_bytes = lhs->dl_newtx_srb_pending_bytes > 0; + const bool rhs_has_dl_newtx_srb_pending_bytes = rhs->dl_newtx_srb_pending_bytes > 0; + if (lhs_has_dl_newtx_srb_pending_bytes != rhs_has_dl_newtx_srb_pending_bytes) { + return rhs_has_dl_newtx_srb_pending_bytes; + } + // SRB HARQ newTx in both UEs. + if (lhs_has_dl_newtx_srb_pending_bytes) { + return lhs->dl_prio < rhs->dl_prio; + } + // Third, prioritize UEs with DRB re-transmissions. + // ReTx in one UE and not in other UE. + if (is_lhs_retx != is_rhs_retx) { + return is_rhs_retx; + } + // All other cases compare priorities. + return lhs->dl_prio < rhs->dl_prio; } bool scheduler_time_pf::ue_ul_prio_compare::operator()(const scheduler_time_pf::ue_ctxt* lhs, @@ -344,18 +396,31 @@ bool scheduler_time_pf::ue_ul_prio_compare::operator()(const scheduler_time_pf:: { 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; + // First, prioritize UEs with pending SR. + // SR indication in one UE and not in other UE. + if (lhs->sr_ind_received != rhs->sr_ind_received) { + return rhs->sr_ind_received; } - 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. + // SR indication for both UEs. + if (lhs->sr_ind_received) { + return lhs->ul_prio < rhs->ul_prio; + } + // Second, prioritize UEs with SRB data new transmissions. + // SRB data newTx in one UE and not in other UE. + const bool lhs_has_ul_newtx_srb_pending_bytes = lhs->ul_newtx_srb_pending_bytes > 0; + const bool rhs_has_ul_newtx_srb_pending_bytes = rhs->ul_newtx_srb_pending_bytes > 0; + if (lhs_has_ul_newtx_srb_pending_bytes != rhs_has_ul_newtx_srb_pending_bytes) { + return rhs_has_ul_newtx_srb_pending_bytes; + } + // SRB data newTx in both UEs. + if (lhs_has_ul_newtx_srb_pending_bytes) { return lhs->ul_prio < rhs->ul_prio; } - return false; + // Third, prioritize UEs with re-transmissions. + // ReTx in one UE and not in other UE. + if (is_lhs_retx != is_rhs_retx) { + return is_rhs_retx; + } + // All other cases compare priorities. + return lhs->ul_prio < rhs->ul_prio; } diff --git a/lib/scheduler/policy/scheduler_time_pf.h b/lib/scheduler/policy/scheduler_time_pf.h index f4509900e9..694898d09b 100644 --- a/lib/scheduler/policy/scheduler_time_pf.h +++ b/lib/scheduler/policy/scheduler_time_pf.h @@ -73,6 +73,10 @@ class scheduler_time_pf : public scheduler_policy const dl_harq_process* dl_newtx_h = nullptr; const ul_harq_process* ul_retx_h = nullptr; const ul_harq_process* ul_newtx_h = nullptr; + /// Number of pending newTx bytes in SRBs in DL to be scheduled. + unsigned dl_newtx_srb_pending_bytes = 0; + /// Number of pending newTx bytes in SRBs in UL to be scheduled. + unsigned ul_newtx_srb_pending_bytes = 0; /// Flag indicating whether SR indication from the UE is received or not. bool sr_ind_received = false; diff --git a/lib/scheduler/policy/scheduler_time_rr.cpp b/lib/scheduler/policy/scheduler_time_rr.cpp index 227aca49fa..9f4a7f260c 100644 --- a/lib/scheduler/policy/scheduler_time_rr.cpp +++ b/lib/scheduler/policy/scheduler_time_rr.cpp @@ -127,8 +127,11 @@ static unsigned compute_max_nof_rbs_per_ue_per_slot(const ue_repository& } /// \brief Fetches list of DL HARQ candidates to schedule. -static static_vector -get_ue_dl_harq_candidates(const ue& ue_ref, ue_cell_index_t cell_index, bool is_retx, srslog::basic_logger& logger) +static static_vector get_ue_dl_harq_candidates(const ue& ue_ref, + ue_cell_index_t cell_index, + bool is_retx, + bool ue_with_srb_data_only, + srslog::basic_logger& logger) { static_vector dl_harq_candidates; @@ -140,7 +143,8 @@ get_ue_dl_harq_candidates(const ue& ue_ref, ue_cell_index_t cell_index, bool is_ // Create list of DL HARQ processes with pending retx, sorted from oldest to newest. for (unsigned i = 0; i != ue_cc.harqs.nof_dl_harqs(); ++i) { const dl_harq_process& h = ue_cc.harqs.dl_harq(i); - if (h.has_pending_retx()) { + if (h.has_pending_retx() and not h.last_alloc_params().is_fallback and + (not ue_with_srb_data_only or (h.last_alloc_params().tb[0]->contains_srb_data))) { dl_harq_candidates.push_back(&h); } } @@ -202,7 +206,7 @@ get_ue_ul_harq_candidates(const ue& ue_ref, ue_cell_index_t cell_index, bool is_ [](const ul_harq_process* lhs, const ul_harq_process* rhs) { return lhs->slot_ack() < rhs->slot_ack(); }); } else if (ue_cc.is_active()) { // If there are no pending new Tx bytes, return. - if (not(ue_ref.pending_ul_newtx_bytes() > 0)) { + if (ue_ref.pending_ul_newtx_bytes() == 0) { return ul_harq_candidates; } @@ -260,7 +264,7 @@ du_ue_index_t round_robin_apply(const ue_repository& ue_db, du_ue_index_t next_u // 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 // can only use the RBs that were left from the previous UE allocations. - next_ue_index = to_du_ue_index((unsigned)u.ue_index + 1U); + next_ue_index = to_du_ue_index(static_cast(u.ue_index) + 1U); first_alloc = false; } } @@ -281,8 +285,7 @@ static alloc_result alloc_dl_ue(const ue& u, if (not u.has_pending_dl_newtx_bytes()) { 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)) { + if (ue_with_srb_data_only and not u.has_pending_dl_srb_newtx_bytes()) { return {alloc_status::skip_ue}; } } @@ -303,7 +306,8 @@ static alloc_result alloc_dl_ue(const ue& u, } // Get DL HARQ candidates. - const auto harq_candidates = get_ue_dl_harq_candidates(u, to_ue_cell_index(i), is_retx, logger); + const auto harq_candidates = + get_ue_dl_harq_candidates(u, to_ue_cell_index(i), is_retx, ue_with_srb_data_only, logger); if (harq_candidates.empty()) { // The conditions for a new PDSCH allocation for this UE were not met (e.g. lack of available HARQs). continue; @@ -313,8 +317,9 @@ static alloc_result alloc_dl_ue(const ue& u, for (const dl_harq_process* h_dl : harq_candidates) { ue_pdsch_grant grant{&u, ue_cc.cell_index, h_dl->id}; if (not is_retx) { - grant.recommended_nof_bytes = u.pending_dl_newtx_bytes(); - grant.max_nof_rbs = dl_new_tx_max_nof_rbs_per_ue_per_slot; + grant.recommended_nof_bytes = + ue_with_srb_data_only ? u.pending_dl_srb_newtx_bytes() : u.pending_dl_newtx_bytes(); + grant.max_nof_rbs = dl_new_tx_max_nof_rbs_per_ue_per_slot; } const alloc_result result = pdsch_alloc.allocate_dl_grant(grant); // If the allocation failed due to invalid parameters, we continue iteration. @@ -332,14 +337,21 @@ static alloc_result alloc_ul_ue(const ue& u, ue_pusch_allocator& pusch_alloc, bool is_retx, bool schedule_sr_only, + bool ue_with_srb_data_only, srslog::basic_logger& logger, std::optional ul_new_tx_max_nof_rbs_per_ue_per_slot = {}) { - unsigned pending_newtx_bytes = 0; + unsigned pending_newtx_bytes = 0; + unsigned pending_srb_newtx_bytes = 0; if (not is_retx) { if (schedule_sr_only and not u.has_pending_sr()) { return {alloc_status::skip_ue}; } + // Fetch pending bytes of SRBs. + pending_srb_newtx_bytes = u.pending_ul_srb_newtx_bytes(); + if (ue_with_srb_data_only and pending_srb_newtx_bytes == 0) { + return {alloc_status::skip_ue}; + } pending_newtx_bytes = u.pending_ul_newtx_bytes(); if (pending_newtx_bytes == 0) { return {alloc_status::skip_ue}; @@ -365,7 +377,7 @@ static alloc_result alloc_ul_ue(const ue& u, for (const ul_harq_process* h_ul : harq_candidates) { ue_pusch_grant grant{&u, ue_cc.cell_index, h_ul->id}; if (not is_retx) { - grant.recommended_nof_bytes = u.pending_ul_newtx_bytes(); + grant.recommended_nof_bytes = ue_with_srb_data_only ? pending_srb_newtx_bytes : pending_newtx_bytes; grant.max_nof_rbs = ul_new_tx_max_nof_rbs_per_ue_per_slot; } const alloc_result result = pusch_alloc.allocate_ul_grant(grant); @@ -392,28 +404,36 @@ void scheduler_time_rr::dl_sched(ue_pdsch_allocator& pdsch_alloc, const ue_resource_grid_view& res_grid, const ue_repository& ues) { - // First schedule re-transmissions. - auto retx_ue_function = [this, &res_grid, &pdsch_alloc](const ue& u) { - return alloc_dl_ue(u, res_grid, pdsch_alloc, true, false, logger, expert_cfg); + // First, schedule UEs with SRB data re-transmissions. + auto srb_retx_ue_function = [this, &res_grid, &pdsch_alloc](const ue& u) { + return alloc_dl_ue(u, res_grid, pdsch_alloc, true, true, logger, expert_cfg); }; - next_dl_ue_index = round_robin_apply(ues, next_dl_ue_index, retx_ue_function); + next_dl_ue_index = round_robin_apply(ues, next_dl_ue_index, srb_retx_ue_function); const unsigned dl_new_tx_max_nof_rbs_per_ue_per_slot = compute_max_nof_rbs_per_ue_per_slot(ues, true, res_grid, expert_cfg); if (dl_new_tx_max_nof_rbs_per_ue_per_slot > 0) { - // Second, schedule UEs with SRB data. + // Second, schedule UEs with SRB data new transmission. auto srb_newtx_ue_function = [this, &res_grid, &pdsch_alloc, dl_new_tx_max_nof_rbs_per_ue_per_slot](const ue& u) { return alloc_dl_ue( u, res_grid, pdsch_alloc, false, true, logger, expert_cfg, dl_new_tx_max_nof_rbs_per_ue_per_slot); }; next_dl_ue_index = round_robin_apply(ues, next_dl_ue_index, srb_newtx_ue_function); + } - // Then, schedule new transmissions. - auto tx_ue_function = [this, &res_grid, &pdsch_alloc, dl_new_tx_max_nof_rbs_per_ue_per_slot](const ue& u) { + // Third, schedule UEs with DRB re-transmissions. + auto drb_retx_ue_function = [this, &res_grid, &pdsch_alloc](const ue& u) { + return alloc_dl_ue(u, res_grid, pdsch_alloc, true, false, logger, expert_cfg); + }; + next_dl_ue_index = round_robin_apply(ues, next_dl_ue_index, drb_retx_ue_function); + + if (dl_new_tx_max_nof_rbs_per_ue_per_slot > 0) { + // Then, schedule UEs with DRB new transmissions. + auto drb_newtx_ue_function = [this, &res_grid, &pdsch_alloc, dl_new_tx_max_nof_rbs_per_ue_per_slot](const ue& u) { return alloc_dl_ue( u, res_grid, pdsch_alloc, false, false, logger, expert_cfg, dl_new_tx_max_nof_rbs_per_ue_per_slot); }; - next_dl_ue_index = round_robin_apply(ues, next_dl_ue_index, tx_ue_function); + next_dl_ue_index = round_robin_apply(ues, next_dl_ue_index, drb_newtx_ue_function); } } @@ -426,24 +446,32 @@ void scheduler_time_rr::ul_sched(ue_pusch_allocator& pusch_alloc, return; } - // First schedule UL data re-transmissions. - auto data_retx_ue_function = [this, &res_grid, &pusch_alloc](const ue& u) { - return alloc_ul_ue(u, res_grid, pusch_alloc, true, false, logger); - }; - next_ul_ue_index = round_robin_apply(ues, next_ul_ue_index, data_retx_ue_function); - - // Then, schedule all pending SR. + // First, schedule UEs with pending SR. auto sr_ue_function = [this, &res_grid, &pusch_alloc](const ue& u) { - return alloc_ul_ue(u, res_grid, pusch_alloc, false, true, logger); + return alloc_ul_ue(u, res_grid, pusch_alloc, false, true, false, logger); }; next_ul_ue_index = round_robin_apply(ues, next_ul_ue_index, sr_ue_function); - // Finally, schedule UL data new transmissions. const unsigned ul_new_tx_max_nof_rbs_per_ue_per_slot = compute_max_nof_rbs_per_ue_per_slot(ues, false, res_grid, expert_cfg); + if (ul_new_tx_max_nof_rbs_per_ue_per_slot > 0) { + // Second, schedule UEs with SRB data new transmissions. + auto srb_newtx_ue_function = [this, &res_grid, &pusch_alloc, ul_new_tx_max_nof_rbs_per_ue_per_slot](const ue& u) { + return alloc_ul_ue(u, res_grid, pusch_alloc, false, false, true, logger, ul_new_tx_max_nof_rbs_per_ue_per_slot); + }; + next_ul_ue_index = round_robin_apply(ues, next_ul_ue_index, srb_newtx_ue_function); + } + + // Third, schedule UEs with re-transmissions. + auto data_retx_ue_function = [this, &res_grid, &pusch_alloc](const ue& u) { + return alloc_ul_ue(u, res_grid, pusch_alloc, true, false, false, logger); + }; + next_ul_ue_index = round_robin_apply(ues, next_ul_ue_index, data_retx_ue_function); + + // Then, schedule UEs with new transmissions. if (ul_new_tx_max_nof_rbs_per_ue_per_slot > 0) { auto data_tx_ue_function = [this, &res_grid, &pusch_alloc, ul_new_tx_max_nof_rbs_per_ue_per_slot](const ue& u) { - return alloc_ul_ue(u, res_grid, pusch_alloc, false, false, logger, ul_new_tx_max_nof_rbs_per_ue_per_slot); + return alloc_ul_ue(u, res_grid, pusch_alloc, false, false, false, logger, ul_new_tx_max_nof_rbs_per_ue_per_slot); }; next_ul_ue_index = round_robin_apply(ues, next_ul_ue_index, data_tx_ue_function); } diff --git a/lib/scheduler/pucch_scheduling/pucch_allocator_impl.cpp b/lib/scheduler/pucch_scheduling/pucch_allocator_impl.cpp index 22171a9f7b..8ca0ff2547 100644 --- a/lib/scheduler/pucch_scheduling/pucch_allocator_impl.cpp +++ b/lib/scheduler/pucch_scheduling/pucch_allocator_impl.cpp @@ -290,6 +290,15 @@ std::optional pucch_allocator_impl::alloc_ded_pucch_harq_ack_ue(cell_r // Allocate PUCCH HARQ-ACK grant depending on whether there existing PUCCH grants. if (existing_grant_it != ue_pucchs.end()) { + // Multiplexing of multiple HARQ-ACK bits in a PUCCH common grant is not allowed. + if (existing_grant_it->has_common_pucch) { + logger.debug( + "rnti={}: PUCCH HARQ-ACK for slot={} not allocated. Cause: The UE has a PUCCH common grant at the same slot", + crnti, + sl_ack); + return std::nullopt; + } + pucch_uci_bits new_bits = existing_grant_it->pucch_grants.get_uci_bits(); ++new_bits.harq_ack_nof_bits; @@ -372,8 +381,8 @@ void pucch_allocator_impl::pucch_allocate_sr_opportunity(cell_slot_resource_allo // Save the info in the scheduler list of PUCCH grants. auto& sr_pucch_grant = pucch_grants_alloc_grid[sl_tx.to_uint()].emplace_back(ue_grants{.rnti = crnti}); - sr_pucch_grant.pucch_grants.sr_resource.emplace( - pucch_grant{.type = pucch_grant_type::sr, .pucch_res_cfg = pucch_sr_res}); + sr_pucch_grant.pucch_grants.sr_resource.emplace(pucch_grant{.type = pucch_grant_type::sr}); + sr_pucch_grant.pucch_grants.sr_resource.value().set_res_config(*pucch_sr_res); sr_pucch_grant.pucch_grants.sr_resource.value().bits.sr_bits = sr_nof_bits::one; } @@ -498,27 +507,28 @@ void pucch_allocator_impl::slot_indication(slot_point sl_tx) ////////////// Sub-class definitions ////////////// -ofdm_symbol_range pucch_allocator_impl::pucch_grant::get_symbols() const +void pucch_allocator_impl::pucch_grant::set_res_config(const pucch_resource& res_cfg) { - if (pucch_res_cfg == nullptr) { - return ofdm_symbol_range{NOF_OFDM_SYM_PER_SLOT_NORMAL_CP + 1, NOF_OFDM_SYM_PER_SLOT_NORMAL_CP + 1}; - } - - switch (pucch_res_cfg->format) { + pucch_res_cfg = &res_cfg; + format = res_cfg.format; + switch (res_cfg.format) { case pucch_format::FORMAT_0: { - const auto& f0 = std::get(pucch_res_cfg->format_params); - return ofdm_symbol_range{f0.starting_sym_idx, f0.starting_sym_idx + f0.nof_symbols}; + const auto& f0 = std::get(res_cfg.format_params); + symbols.set(f0.starting_sym_idx, f0.starting_sym_idx + f0.nof_symbols); + break; } case pucch_format::FORMAT_1: { - const auto& f1 = std::get(pucch_res_cfg->format_params); - return ofdm_symbol_range{f1.starting_sym_idx, f1.starting_sym_idx + f1.nof_symbols}; + const auto& f1 = std::get(res_cfg.format_params); + symbols.set(f1.starting_sym_idx, f1.starting_sym_idx + f1.nof_symbols); + break; } case pucch_format::FORMAT_2: { - const auto& f2 = std::get(pucch_res_cfg->format_params); - return ofdm_symbol_range{f2.starting_sym_idx, f2.starting_sym_idx + f2.nof_symbols}; + const auto& f2 = std::get(res_cfg.format_params); + symbols.set(f2.starting_sym_idx, f2.starting_sym_idx + f2.nof_symbols); + break; } default: - return ofdm_symbol_range{NOF_OFDM_SYM_PER_SLOT_NORMAL_CP + 1, NOF_OFDM_SYM_PER_SLOT_NORMAL_CP + 1}; + srsran_assertion_failure("Only PUCCH format 0, 1 and 2 are currentl supported"); } } @@ -568,26 +578,42 @@ bool pucch_allocator_impl::pucch_grant_list::is_emtpy() const return not sr_resource.has_value() and not harq_resource.has_value() and not csi_resource.has_value(); } +unsigned pucch_allocator_impl::pucch_grant_list::get_nof_grants() const +{ + unsigned nof_grants = 0; + if (harq_resource.has_value()) { + ++nof_grants; + } + if (sr_resource.has_value()) { + ++nof_grants; + } + if (csi_resource.has_value()) { + ++nof_grants; + } + return nof_grants; +} + +// Utility used by existing_pucch_pdus_handler to check if a PUCCH PDU is for SR. +static bool sr_id_match(const pucch_resource& pucch_res_cfg_lhs, const pucch_info& rhs) +{ + const auto& f1_cfg = std::get(pucch_res_cfg_lhs.format_params); + const bool prb_match = + pucch_res_cfg_lhs.starting_prb == rhs.resources.prbs.start() and + ((not pucch_res_cfg_lhs.second_hop_prb.has_value() and rhs.resources.second_hop_prbs.empty()) or + (pucch_res_cfg_lhs.second_hop_prb.has_value() and pucch_res_cfg_lhs.second_hop_prb.value() and + pucch_res_cfg_lhs.second_hop_prb == rhs.resources.second_hop_prbs.start())); + const bool symb_match = + f1_cfg.starting_sym_idx == rhs.resources.symbols.start() and f1_cfg.nof_symbols == rhs.resources.symbols.length(); + return prb_match && symb_match && f1_cfg.initial_cyclic_shift == rhs.format_1.initial_cyclic_shift && + f1_cfg.time_domain_occ == rhs.format_1.time_domain_occ; +} + // Contains the existing PUCCH grants currently allocated to a given UE. class existing_pucch_pdus_handler { public: existing_pucch_pdus_handler(rnti_t crnti, span pucchs, const pucch_resource* pucch_res_cfg); - static bool sr_id_match(const pucch_resource& pucch_res_cfg_lhs, const pucch_info& rhs) - { - const auto& f1_cfg = std::get(pucch_res_cfg_lhs.format_params); - const bool prb_match = - pucch_res_cfg_lhs.starting_prb == rhs.resources.prbs.start() and - ((not pucch_res_cfg_lhs.second_hop_prb.has_value() and rhs.resources.second_hop_prbs.empty()) or - (pucch_res_cfg_lhs.second_hop_prb.has_value() and pucch_res_cfg_lhs.second_hop_prb.value() and - pucch_res_cfg_lhs.second_hop_prb == rhs.resources.second_hop_prbs.start())); - const bool symb_match = f1_cfg.starting_sym_idx == rhs.resources.symbols.start() and - f1_cfg.nof_symbols == rhs.resources.symbols.length(); - return prb_match && symb_match && f1_cfg.initial_cyclic_shift == rhs.format_1.initial_cyclic_shift && - f1_cfg.time_domain_occ == rhs.format_1.time_domain_occ; - } - [[nodiscard]] bool is_empty() const { return pdus_cnt == 0; } [[nodiscard]] unsigned get_nof_unallocated_pdu() const { return pdus_cnt; } pucch_info* get_next_grant(static_vector& pucchs); @@ -1039,8 +1065,9 @@ pucch_allocator_impl::find_common_and_ded_harq_res_available(cell_slot_resource_ // Add a current grant entry with the PUCCH resource indicator found above; this will force the function that // multiplexes the resources to use the specific resource with the given PUCCH resource indicator (it could be // either from resource set 1 or 0, depending on whether there is a CSI grant in the same slot). - pucch_grant& harq_grant = existing_grants.pucch_grants.harq_resource.emplace( - pucch_grant{.type = pucch_grant_type::harq_ack, .pucch_res_cfg = ded_resource}); + pucch_grant& harq_grant = + existing_grants.pucch_grants.harq_resource.emplace(pucch_grant{.type = pucch_grant_type::harq_ack}); + harq_grant.set_res_config(*ded_resource); harq_grant.bits.harq_ack_nof_bits = 1U; harq_grant.harq_id.pucch_set_idx = pucch_res_set_idx::set_0; harq_grant.harq_id.pucch_res_ind = d_pri; @@ -1057,6 +1084,7 @@ pucch_allocator_impl::find_common_and_ded_harq_res_available(cell_slot_resource_ if (not pucch_res_ind.has_value()) { resource_manager.cancel_last_ue_res_reservations(pucch_alloc.slot, rnti, ue_cell_cfg); + existing_grants.pucch_grants.harq_resource.reset(); continue; } @@ -1107,8 +1135,8 @@ std::optional pucch_allocator_impl::allocate_harq_grant(cell_slot_reso // Save the info in the scheduler list of PUCCH grants. auto& grants = pucch_grants_alloc_grid[sl_tx.to_uint()].emplace_back(ue_grants{.rnti = crnti}); - grants.pucch_grants.harq_resource.emplace( - pucch_grant{.type = pucch_grant_type::harq_ack, .pucch_res_cfg = pucch_harq_res_info.pucch_res}); + grants.pucch_grants.harq_resource.emplace(pucch_grant{.type = pucch_grant_type::harq_ack}); + grants.pucch_grants.harq_resource.value().set_res_config(*pucch_harq_res_info.pucch_res); grants.pucch_grants.harq_resource.value().harq_id.pucch_set_idx = pucch_res_set_idx::set_0; grants.pucch_grants.harq_resource.value().harq_id.pucch_res_ind = pucch_res_indicator; grants.pucch_grants.harq_resource.value().bits.harq_ack_nof_bits = HARQ_BITS_IN_NEW_PUCCH_GRANT; @@ -1177,8 +1205,8 @@ void pucch_allocator_impl::allocate_csi_grant(cell_slot_resource_allocator& pucc // Save the info in the scheduler list of PUCCH grants. auto& csi_pucch_grant = pucch_grants_alloc_grid[sl_tx.to_uint()].emplace_back(ue_grants{.rnti = crnti}); - csi_pucch_grant.pucch_grants.csi_resource.emplace( - pucch_grant{.type = pucch_grant_type::csi, .pucch_res_cfg = csi_f2_res}); + csi_pucch_grant.pucch_grants.csi_resource.emplace(pucch_grant{.type = pucch_grant_type::csi}); + csi_pucch_grant.pucch_grants.csi_resource.value().set_res_config(*csi_f2_res); csi_pucch_grant.pucch_grants.csi_resource.value().bits.csi_part1_nof_bits = csi_part1_bits; } @@ -1291,7 +1319,7 @@ void pucch_allocator_impl::remove_unsed_pucch_res(slot_point s if (existing_pucchs.pucch_grants.harq_resource.has_value() and (not grants_to_tx.harq_resource.has_value() or - existing_pucchs.pucch_grants.harq_resource->get_format() != grants_to_tx.harq_resource->get_format())) { + existing_pucchs.pucch_grants.harq_resource->format != grants_to_tx.harq_resource->format)) { if (existing_pucchs.pucch_grants.harq_resource.value().harq_id.pucch_set_idx == pucch_res_set_idx::set_0) { resource_manager.release_harq_set_0_resource( sl_tx, existing_pucchs.rnti, ue_cell_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pucch_cfg.value()); @@ -1397,7 +1425,7 @@ pucch_allocator_impl::get_pucch_res_pre_multiplexing(slot_point harq_candidate_grant.harq_id.pucch_set_idx = pucch_set_idx; harq_candidate_grant.harq_id.pucch_res_ind = static_cast(ue_current_grants.pucch_grants.harq_resource.value().harq_id.pucch_res_ind); - harq_candidate_grant.pucch_res_cfg = pucch_res; + harq_candidate_grant.set_res_config(*pucch_res); } // Get a new PUCCH resource for HARQ-ACK from the correct PUCCH resource set. else { @@ -1417,7 +1445,7 @@ pucch_allocator_impl::get_pucch_res_pre_multiplexing(slot_point } harq_candidate_grant.harq_id.pucch_set_idx = pucch_set_idx; harq_candidate_grant.harq_id.pucch_res_ind = static_cast(harq_resource.pucch_res_indicator); - harq_candidate_grant.pucch_res_cfg = harq_resource.pucch_res; + harq_candidate_grant.set_res_config(*harq_resource.pucch_res); } // Only copy the HARQ-ACK bits, as at this stage we only need to consider the UCI bits assuming the resources still // need to be multiplexed. @@ -1442,7 +1470,7 @@ pucch_allocator_impl::get_pucch_res_pre_multiplexing(slot_point ue_current_grants.rnti); return std::nullopt; } - sr_candidate_grant.pucch_res_cfg = sr_resource; + sr_candidate_grant.set_res_config(*sr_resource); // Only copy the SR bits, as at this stage we only need to consider the UCI bits assuming the resources still // need to be multiplexed. sr_candidate_grant.bits.harq_ack_nof_bits = 0U; @@ -1466,7 +1494,7 @@ pucch_allocator_impl::get_pucch_res_pre_multiplexing(slot_point ue_current_grants.rnti); return std::nullopt; } - csi_candidate_grant.pucch_res_cfg = csi_resource; + csi_candidate_grant.set_res_config(*csi_resource); // Only copy the HARQ-ACK bits, as at this stage we only need to consider the UCI bits assuming the resources still // need to be multiplexed. csi_candidate_grant.bits.harq_ack_nof_bits = 0U; @@ -1496,7 +1524,7 @@ pucch_allocator_impl::update_grants_no_multiplexing(slot_point if (harq_grant.harq_id.pucch_set_idx != pucch_res_set_idx::set_0 and candidate_grants.get_uci_bits().get_total_bits() > ue_cell_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pucch_cfg.value().get_max_payload( - harq_grant.get_format())) { + harq_grant.format)) { logger.debug( "rnti={}: PUCCH allocation (HARQ-ACK) for slot={} skipped. Cause: UCI bits exceed PUCCH payload", crnti, sl_tx); return grants_to_tx; @@ -1513,10 +1541,10 @@ pucch_allocator_impl::update_grants_no_multiplexing(slot_point // NOTE: no need to update the CSI grant, because: (i) if the CSI grant exists, then it doesn't carry the HARQ-ACK // bits (they are in the HARQ-ACK grangt); (ii) if it doesn't exist, then it's trivial. grants_to_tx.harq_resource.value().bits.harq_ack_nof_bits = harq_grant.bits.harq_ack_nof_bits; - grants_to_tx.harq_resource.value().pucch_res_cfg = harq_grant.pucch_res_cfg; + grants_to_tx.harq_resource.value().set_res_config(*harq_grant.pucch_res_cfg); if (ue_current_grants.pucch_grants.sr_resource.has_value()) { srsran_assert(candidate_grants.sr_resource.has_value(), "PUCCH SR resource must be present"); - grants_to_tx.sr_resource.value().pucch_res_cfg = candidate_grants.sr_resource->pucch_res_cfg; + grants_to_tx.sr_resource.value().set_res_config(*candidate_grants.sr_resource->pucch_res_cfg); grants_to_tx.sr_resource.value().bits.harq_ack_nof_bits = harq_grant.bits.harq_ack_nof_bits; } @@ -1551,9 +1579,9 @@ pucch_allocator_impl::multiplex_resources(slot_point sl_tx, // Sort the resources in the set based on the number of symbols. auto sort_res_set_q = [&resource_set_q]() { std::sort(resource_set_q.begin(), resource_set_q.end(), [](const pucch_grant& lhs_res, const pucch_grant& rhs_res) { - return lhs_res.get_symbols().start() < rhs_res.get_symbols().start() or - (lhs_res.get_symbols().start() == rhs_res.get_symbols().start() and - lhs_res.get_symbols().length() > rhs_res.get_symbols().length()); + return lhs_res.symbols.start() < rhs_res.symbols.start() or + (lhs_res.symbols.start() == rhs_res.symbols.start() and + lhs_res.symbols.length() > rhs_res.symbols.length()); }); }; @@ -1564,7 +1592,7 @@ pucch_allocator_impl::multiplex_resources(slot_point sl_tx, unsigned j_cnt = 0; while (j_cnt < resource_set_q.size()) { if (j_cnt < resource_set_q.size() - 1 and - resource_set_q[j_cnt - o_cnt].get_symbols().overlaps(resource_set_q[j_cnt + 1].get_symbols())) { + resource_set_q[j_cnt - o_cnt].symbols.overlaps(resource_set_q[j_cnt + 1].symbols)) { ++j_cnt; ++o_cnt; } else { @@ -1600,7 +1628,7 @@ pucch_allocator_impl::multiplex_resources(slot_point sl_tx, // The PUCCH resource multiplexing algorithm above is specified from the UE's perspective. In the GNB, we need to add // an extra resource Format 1 if slot there is a SR opportunity and HARQ bits to be reported with PUCCH Format 1. - if (resource_set_q.size() == 1 and resource_set_q.front().get_format() == pucch_format::FORMAT_1 and + if (resource_set_q.size() == 1 and resource_set_q.front().format == pucch_format::FORMAT_1 and resource_set_q.front().bits.harq_ack_nof_bits != 0 and resource_set_q.front().bits.sr_bits != sr_nof_bits::no_sr) { const pucch_resource* sr_res = resource_manager.reserve_sr_res_available( @@ -1611,10 +1639,11 @@ pucch_allocator_impl::multiplex_resources(slot_point sl_tx, } // For Format 1, the grant with SR bit is the one with the SR-dedicated resource; in the HARQ-ACK grant we only // report the HARQ bits. - pucch_uci_bits bits = {.harq_ack_nof_bits = resource_set_q.front().bits.harq_ack_nof_bits, - .sr_bits = resource_set_q.front().bits.sr_bits, - .csi_part1_nof_bits = 0}; - resource_set_q.emplace_back(pucch_grant{.type = pucch_grant_type::sr, .bits = bits, .pucch_res_cfg = sr_res}); + pucch_uci_bits bits = {.harq_ack_nof_bits = resource_set_q.front().bits.harq_ack_nof_bits, + .sr_bits = resource_set_q.front().bits.sr_bits, + .csi_part1_nof_bits = 0}; + auto& sr_grant_f1 = resource_set_q.emplace_back(pucch_grant{.type = pucch_grant_type::sr, .bits = bits}); + sr_grant_f1.set_res_config(*sr_res); resource_set_q.front().bits.sr_bits = sr_nof_bits::no_sr; } @@ -1622,13 +1651,10 @@ pucch_allocator_impl::multiplex_resources(slot_point sl_tx, for (const auto& mplex_res : resource_set_q) { if (mplex_res.type == pucch_grant_type::harq_ack) { mplexed_grants.harq_resource.emplace(mplex_res); - ++mplexed_grants.nof_grants; } else if (mplex_res.type == pucch_grant_type::sr) { mplexed_grants.sr_resource.emplace(mplex_res); - ++mplexed_grants.nof_grants; } else if (mplex_res.type == pucch_grant_type::csi) { mplexed_grants.csi_resource.emplace(mplex_res); - ++mplexed_grants.nof_grants; } } return mplexed_grants; @@ -1666,8 +1692,8 @@ pucch_allocator_impl::merge_pucch_resources(span= 2, the HARQ-ACK resource has HARQ-ACK bits and optionally CSI // bits. else { - srsran_assert(r_sr.get_format() == pucch_format::FORMAT_0 or r_sr.get_format() == pucch_format::FORMAT_1, + srsran_assert(r_sr.format == pucch_format::FORMAT_0 or r_sr.format == pucch_format::FORMAT_1, "The two resources must have the same format"); // Apply F2 CSI merging rule: SR and CSI PUCCH resources will be multiplexed in the CSI PUCCH resource. // A HARQ resource from PUCCH resource set idx 1 already exits. Use that one. @@ -1696,10 +1722,9 @@ pucch_allocator_impl::merge_pucch_resources(spanget_format() == pucch_format::FORMAT_0) { + if (r_sr_ptr->format == pucch_format::FORMAT_0) { srsran_assertion_failure("This case is not yet supported"); // SR and CSI are not supported on the same slot if SR uses Format 0. return std::nullopt; } - if (r_harq_ptr->get_format() != pucch_format::FORMAT_0 and r_harq_ptr->get_format() != pucch_format::FORMAT_1) { + if (r_harq_ptr->format != pucch_format::FORMAT_0 and r_harq_ptr->format != pucch_format::FORMAT_1) { new_resource = *r_harq_ptr; } else { pucch_harq_resource_alloc_record res_alloc = @@ -1843,17 +1864,16 @@ pucch_allocator_impl::merge_pucch_resources(spanbits.harq_ack_nof_bits; new_resource.bits.sr_bits = r_sr_ptr->bits.sr_bits; new_resource.bits.csi_part1_nof_bits = r_csi_ptr->bits.csi_part1_nof_bits; // Check if the UCI payload fits in the PUCCH resource. - if (new_resource.bits.get_total_bits() <= pucch_cfg.get_max_payload(new_resource.get_format())) { + if (new_resource.bits.get_total_bits() <= pucch_cfg.get_max_payload(new_resource.format)) { return new_resource; } else { return std::nullopt; @@ -1878,8 +1898,9 @@ std::optional pucch_allocator_impl::allocate_grants(cell_slot_resource get_sr_pucch_res_cfg(ue_cell_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pucch_cfg.value())); // Check if we can fit the new PUCCH PDUs in the output results. - if ((grants_to_tx.nof_grants >= existing_pdus.get_nof_unallocated_pdu()) and - (pucch_slot_alloc.result.ul.pucchs.size() + (grants_to_tx.nof_grants - existing_pdus.get_nof_unallocated_pdu()) > + if ((grants_to_tx.get_nof_grants() >= existing_pdus.get_nof_unallocated_pdu()) and + (pucch_slot_alloc.result.ul.pucchs.size() + + (grants_to_tx.get_nof_grants() - existing_pdus.get_nof_unallocated_pdu()) > get_max_pucch_grants(static_cast(pucch_slot_alloc.result.ul.puschs.size())))) { logger.info( "rnti={}: PUCCH allocation for slot={} skipped. Cause: UL grants reached", crnti, pucch_slot_alloc.slot); @@ -1892,15 +1913,14 @@ std::optional pucch_allocator_impl::allocate_grants(cell_slot_resource // If there was a CSI grant, re-use the previous one and update the UCI bits with SR. if (grants_to_tx.csi_resource.has_value() and existing_pucchs.pucch_grants.csi_resource.has_value() and existing_pdus.csi_pdu != nullptr) { - logger.info( - "rnti={}: PUCCH PDU allocated on CSI resource: slot={}, prbs={}, sym={}, h_bits={}, sr_bits={}, cs_bits={}", - crnti, - pucch_slot_alloc.slot, - existing_pdus.csi_pdu->resources.prbs, - existing_pdus.csi_pdu->resources.symbols, - existing_pdus.csi_pdu->format_2.harq_ack_nof_bits, - grants_to_tx.csi_resource.value().bits.sr_bits, - grants_to_tx.csi_resource.value().bits.csi_part1_nof_bits); + logger.debug("rnti={}: PUCCH PDU allocated on CSI resource: slot={} prbs={} sym={} h_bits={} sr_bits={} cs_bits={}", + crnti, + pucch_slot_alloc.slot, + existing_pdus.csi_pdu->resources.prbs, + existing_pdus.csi_pdu->resources.symbols, + existing_pdus.csi_pdu->format_2.harq_ack_nof_bits, + grants_to_tx.csi_resource.value().bits.sr_bits, + grants_to_tx.csi_resource.value().bits.csi_part1_nof_bits); existing_pdus.update_csi_pdu_bits(grants_to_tx.csi_resource.value().bits.csi_part1_nof_bits, grants_to_tx.csi_resource.value().bits.sr_bits); csi_grant_alloc_completed = true; @@ -1909,16 +1929,16 @@ std::optional pucch_allocator_impl::allocate_grants(cell_slot_resource else if (grants_to_tx.sr_resource.has_value() and existing_pucchs.pucch_grants.sr_resource.has_value() and existing_pdus.sr_pdu != nullptr) { // NOTE: the validator is responsible for checking that the is no mix of PUCCH Format 0 and Format 1. - logger.info("rnti={}: PUCCH PDU allocated on SR resource (updated): slot={}, prbs={}, sym={}, cs={}, occ={}, " - "h_bits={}, sr_bits={}", - crnti, - pucch_slot_alloc.slot, - existing_pdus.sr_pdu->resources.prbs, - existing_pdus.sr_pdu->resources.symbols, - existing_pdus.sr_pdu->format_1.initial_cyclic_shift, - existing_pdus.sr_pdu->format_1.time_domain_occ, - grants_to_tx.sr_resource.value().bits.harq_ack_nof_bits, - grants_to_tx.sr_resource.value().bits.sr_bits); + logger.debug("rnti={}: PUCCH PDU allocated on SR resource (updated): slot={} prbs={} sym={} cs={} occ={} " + "h_bits={} sr_bits={}", + crnti, + pucch_slot_alloc.slot, + existing_pdus.sr_pdu->resources.prbs, + existing_pdus.sr_pdu->resources.symbols, + existing_pdus.sr_pdu->format_1.initial_cyclic_shift, + existing_pdus.sr_pdu->format_1.time_domain_occ, + grants_to_tx.sr_resource.value().bits.harq_ack_nof_bits, + grants_to_tx.sr_resource.value().bits.sr_bits); existing_pdus.update_sr_pdu_bits(grants_to_tx.sr_resource.value().bits.sr_bits, grants_to_tx.sr_resource.value().bits.harq_ack_nof_bits); sr_grant_alloc_completed = true; @@ -1926,31 +1946,33 @@ std::optional pucch_allocator_impl::allocate_grants(cell_slot_resource // If there was a HARQ grant of the same PUCCH format, re-use the previous one and update the UCI bits HARQ/CSI/SR // bits. if (grants_to_tx.harq_resource.has_value() and existing_pucchs.pucch_grants.harq_resource.has_value() and - grants_to_tx.harq_resource.value().get_format() == - existing_pucchs.pucch_grants.harq_resource.value().get_format() and + grants_to_tx.harq_resource.value().format == existing_pucchs.pucch_grants.harq_resource.value().format and existing_pdus.harq_pdu != nullptr) { // Update bits; - if (grants_to_tx.harq_resource.value().get_format() == pucch_format::FORMAT_1) { - logger.info("rnti={}: PUCCH PDU allocated on F1 HARQ resource (updated): slot={}, prbs={}, sym={}, cs={}, " - "occ={}, h_bits={}, sr_bits={}", - crnti, - pucch_slot_alloc.slot, - existing_pdus.harq_pdu->resources.prbs, - existing_pdus.harq_pdu->resources.symbols, - existing_pdus.harq_pdu->format_1.initial_cyclic_shift, - existing_pdus.harq_pdu->format_1.time_domain_occ, - grants_to_tx.harq_resource.value().bits.harq_ack_nof_bits, - grants_to_tx.harq_resource.value().bits.sr_bits); + if (grants_to_tx.harq_resource.value().format == pucch_format::FORMAT_1) { + logger.debug("rnti={}: PUCCH PDU allocated on F1 HARQ resource (updated): slot={} p_ind={} prbs={} sym={} cs={} " + "occ={} h_bits={} sr_bits={}", + crnti, + pucch_slot_alloc.slot, + grants_to_tx.harq_resource.value().harq_id.pucch_res_ind, + existing_pdus.harq_pdu->resources.prbs, + existing_pdus.harq_pdu->resources.symbols, + existing_pdus.harq_pdu->format_1.initial_cyclic_shift, + existing_pdus.harq_pdu->format_1.time_domain_occ, + grants_to_tx.harq_resource.value().bits.harq_ack_nof_bits, + grants_to_tx.harq_resource.value().bits.sr_bits); } else { - logger.info("rnti={}: PUCCH PDU allocated on F2 HARQ resource (updated): slot={}, prbs={}, sym={}, h_bits={}, " - "sr_bits={}, cs_bits={}", - crnti, - pucch_slot_alloc.slot, - existing_pdus.harq_pdu->resources.prbs, - existing_pdus.harq_pdu->resources.symbols, - grants_to_tx.harq_resource.value().bits.harq_ack_nof_bits, - grants_to_tx.harq_resource.value().bits.sr_bits, - grants_to_tx.harq_resource.value().bits.csi_part1_nof_bits); + logger.debug( + "rnti={}: PUCCH PDU allocated on F2 HARQ resource (updated): slot={} p_ind={} prbs={} sym={} h_bits={} " + "sr_bits={} cs_bits={}", + crnti, + pucch_slot_alloc.slot, + grants_to_tx.harq_resource.value().harq_id.pucch_res_ind, + existing_pdus.harq_pdu->resources.prbs, + existing_pdus.harq_pdu->resources.symbols, + grants_to_tx.harq_resource.value().bits.harq_ack_nof_bits, + grants_to_tx.harq_resource.value().bits.sr_bits, + grants_to_tx.harq_resource.value().bits.csi_part1_nof_bits); } existing_pdus.update_harq_pdu_bits(grants_to_tx.harq_resource.value().bits.harq_ack_nof_bits, grants_to_tx.harq_resource.value().bits.sr_bits, @@ -1972,20 +1994,19 @@ std::optional pucch_allocator_impl::allocate_grants(cell_slot_resource 0U, grants_to_tx.csi_resource.value().bits.sr_bits, grants_to_tx.csi_resource.value().bits.csi_part1_nof_bits); - logger.info( - "rnti={}: PUCCH PDU allocated on CSI resource: slot={}, prbs={}, sym={}, h_bits={}, sr_bits={}, cs_bits={}", - crnti, - pucch_slot_alloc.slot, - grant->resources.prbs, - grant->resources.symbols, - grant->format_2.harq_ack_nof_bits, - grant->format_2.sr_bits, - grant->format_2.csi_part1_bits); + logger.debug("rnti={}: PUCCH PDU allocated on CSI resource: slot={} prbs={} sym={} h_bits={} sr_bits={} cs_bits={}", + crnti, + pucch_slot_alloc.slot, + grant->resources.prbs, + grant->resources.symbols, + grant->format_2.harq_ack_nof_bits, + grant->format_2.sr_bits, + grant->format_2.csi_part1_bits); } if (grants_to_tx.sr_resource.has_value() and not sr_grant_alloc_completed) { pucch_info* grant = existing_pdus.get_next_grant(pucch_pdus); srsran_assert(grant != nullptr, "The return grant cannot be nullptr"); - if (grants_to_tx.sr_resource.value().get_format() == pucch_format::FORMAT_0) { + if (grants_to_tx.sr_resource.value().format == pucch_format::FORMAT_0) { // TODO srsran_assertion_failure("PUCCH Format 0 is not yet implemented"); } else { @@ -1995,8 +2016,8 @@ std::optional pucch_allocator_impl::allocate_grants(cell_slot_resource grants_to_tx.sr_resource.value().bits.harq_ack_nof_bits, grants_to_tx.sr_resource.value().bits.sr_bits); } - logger.info( - "rnti={}: PUCCH PDU allocated on SR resource: slot={}, prbs={}, sym={}, cs={}, occ={}, h_bits={}, sr_bits={}", + logger.debug( + "rnti={}: PUCCH PDU allocated on SR resource: slot={} prbs={} sym={} cs={} occ={} h_bits={} sr_bits={}", crnti, pucch_slot_alloc.slot, grant->resources.prbs, @@ -2008,10 +2029,10 @@ std::optional pucch_allocator_impl::allocate_grants(cell_slot_resource } if (grants_to_tx.harq_resource.has_value() and not harq_grant_alloc_completed) { pucch_info* grant = existing_pdus.get_next_grant(pucch_pdus); - if (grants_to_tx.harq_resource.value().get_format() == pucch_format::FORMAT_0) { + if (grants_to_tx.harq_resource.value().format == pucch_format::FORMAT_0) { // TODO srsran_assertion_failure("PUCCH Format 0 is not yet implemented"); - } else if (grants_to_tx.harq_resource.value().get_format() == pucch_format::FORMAT_1) { + } else if (grants_to_tx.harq_resource.value().format == pucch_format::FORMAT_1) { fill_pucch_ded_format1_grant(*grant, crnti, *grants_to_tx.harq_resource.value().pucch_res_cfg, @@ -2037,27 +2058,30 @@ std::optional pucch_allocator_impl::allocate_grants(cell_slot_resource grants_to_tx.harq_resource.value().bits.csi_part1_nof_bits); } if (grant->format == pucch_format::FORMAT_1) { - logger.info("rnti={}: PUCCH PDU allocated on F1 HARQ resource: slot={}, prbs={}, sym={}, cs={}, occ={}, " - "h_bits={}, sr_bits={}", - crnti, - pucch_slot_alloc.slot, - grant->resources.prbs, - grant->resources.symbols, - grant->format_1.initial_cyclic_shift, - grant->format_1.time_domain_occ, - grant->format_1.harq_ack_nof_bits, - grant->format_1.sr_bits); + logger.debug("rnti={}: PUCCH PDU allocated on F1 HARQ resource: slot={} p_ind={} prbs={} sym={} cs={} occ={} " + "h_bits={} sr_bits={}", + crnti, + pucch_slot_alloc.slot, + grants_to_tx.harq_resource.value().harq_id.pucch_res_ind, + grant->resources.prbs, + grant->resources.symbols, + grant->format_1.initial_cyclic_shift, + grant->format_1.time_domain_occ, + grant->format_1.harq_ack_nof_bits, + grant->format_1.sr_bits); } else { - logger.info("rnti={}: PUCCH PDU allocated on F2 HARQ resource: slot={}, format={}, prbs={}, sym={}, h_bits={}, " - "sr_bits={}, cs_bits={}", - crnti, - pucch_slot_alloc.slot, - grant->format, - grant->resources.prbs, - grant->resources.symbols, - grant->format_2.harq_ack_nof_bits, - grant->format_2.sr_bits, - grant->format_2.csi_part1_bits); + logger.debug( + "rnti={}: PUCCH PDU allocated on F2 HARQ resource: slot={} p_ind={} format={} prbs={} sym={} h_bits={} " + "sr_bits={} cs_bits={}", + crnti, + pucch_slot_alloc.slot, + grants_to_tx.harq_resource.value().harq_id.pucch_res_ind, + grant->format, + grant->resources.prbs, + grant->resources.symbols, + grant->format_2.harq_ack_nof_bits, + grant->format_2.sr_bits, + grant->format_2.csi_part1_bits); } } diff --git a/lib/scheduler/pucch_scheduling/pucch_allocator_impl.h b/lib/scheduler/pucch_scheduling/pucch_allocator_impl.h index 3550bbb1fd..5953493491 100644 --- a/lib/scheduler/pucch_scheduling/pucch_allocator_impl.h +++ b/lib/scheduler/pucch_scheduling/pucch_allocator_impl.h @@ -112,22 +112,19 @@ class pucch_allocator_impl final : public pucch_allocator /// \brief Defines a PUCCH grant (and its relevant information) currently allocated to a given UE. /// It is used internally to keep track of the UEs' allocations of the PUCCH grants with dedicated resources. - class pucch_grant - { - public: + struct pucch_grant { pucch_grant_type type; + pucch_format format = pucch_format::NOF_FORMATS; // Only relevant for HARQ-ACK resources. - harq_res_id harq_id; - pucch_uci_bits bits; + harq_res_id harq_id; + pucch_uci_bits bits; + ofdm_symbol_range symbols = {NOF_OFDM_SYM_PER_SLOT_NORMAL_CP + 1, NOF_OFDM_SYM_PER_SLOT_NORMAL_CP + 1}; // NOTE: The pointer to the PUCCH resource configuration can only be used within the same slot; this is to prevent // the possibility that the re-configurations can null the pointer before an allocation and the next. const pucch_resource* pucch_res_cfg = nullptr; - [[nodiscard]] pucch_format get_format() const - { - return pucch_res_cfg != nullptr ? pucch_res_cfg->format : pucch_format::NOF_FORMATS; - } - [[nodiscard]] ofdm_symbol_range get_symbols() const; + // Set format and symbols given the configuration. + void set_res_config(const pucch_resource& res_cfg); }; /// \brief List of possible PUCCH grants that allocated to a UE, at a given slot. @@ -137,10 +134,10 @@ class pucch_allocator_impl final : public pucch_allocator std::optional harq_resource; std::optional sr_resource; std::optional csi_resource; - unsigned nof_grants = 0; [[nodiscard]] pucch_uci_bits get_uci_bits() const; [[nodiscard]] bool is_emtpy() const; + [[nodiscard]] unsigned get_nof_grants() const; }; /// Keeps track of the PUCCH grants (common + dedicated) for a given UE. diff --git a/lib/scheduler/pucch_scheduling/pucch_resource_manager.cpp b/lib/scheduler/pucch_scheduling/pucch_resource_manager.cpp index 27158ac4ba..6a7ffa950a 100644 --- a/lib/scheduler/pucch_scheduling/pucch_resource_manager.cpp +++ b/lib/scheduler/pucch_scheduling/pucch_resource_manager.cpp @@ -331,8 +331,14 @@ void pucch_resource_manager::cancel_last_ue_res_reservations(slot_point rnti_t crnti, const ue_cell_configuration& ue_cell_cfg) { + if (last_ue_allocations.rnti == rnti_t::INVALID_RNTI) { + return; + } + if (crnti != last_ue_allocations.rnti) { - srsran_assertion_failure("Trying to cancel a UE allocation that was not the last one"); + srsran_assertion_failure("rnti={}: cancelling PUCCH resource reservations of another UE (rnti={}) is not allowed", + crnti, + last_ue_allocations.rnti); return; } diff --git a/lib/scheduler/ue_scheduling/harq_process.cpp b/lib/scheduler/ue_scheduling/harq_process.cpp index f9c5e63501..f4c77e71dc 100644 --- a/lib/scheduler/ue_scheduling/harq_process.cpp +++ b/lib/scheduler/ue_scheduling/harq_process.cpp @@ -207,6 +207,8 @@ void dl_harq_process::new_tx(slot_point pdsch_slot, prev_tx_params.nof_layers = nof_layers; prev_tx_params.is_fallback = is_fallback_; prev_tx_params.tb[0].emplace(); + // NOTE: Correct value will be set when \c save_alloc_params is called. + prev_tx_params.tb[0]->contains_srb_data = false; prev_tx_params.tb[1].reset(); pucch_ack_to_receive = 0; chosen_ack = mac_harq_ack_report_status::dtx; @@ -237,6 +239,8 @@ void dl_harq_process::tx_2_tb(slot_point pdsch_slot, if (tb_tx_req[i] == tb_tx_request::newtx) { base_type::new_tx_tb_common(i, max_harq_nof_retxs, harq_bit_idx); prev_tx_params.tb[i].emplace(); + // NOTE: Correct value will be set when \c save_alloc_params is called. + prev_tx_params.tb[i]->contains_srb_data = false; } else if (tb_tx_req[i] == tb_tx_request::retx) { base_type::new_retx_tb_common(i, harq_bit_idx); } else { @@ -284,7 +288,9 @@ dl_harq_process::ack_info(uint32_t tb_idx, mac_harq_ack_report_status ack, std:: return status_update::no_update; } -void dl_harq_process::save_alloc_params(const dl_harq_sched_context& ctx, const pdsch_information& pdsch) +void dl_harq_process::save_alloc_params(const dl_harq_sched_context& ctx, + const pdsch_information& pdsch, + bool contains_srb_data) { unsigned tb_idx = empty(0) ? 1 : 0; for (const pdsch_codeword& cw : pdsch.codewords) { @@ -292,8 +298,9 @@ void dl_harq_process::save_alloc_params(const dl_harq_sched_context& ctx, const prev_tx_params.tb[tb_idx]->mcs_table = cw.mcs_table; prev_tx_params.tb[tb_idx]->mcs = cw.mcs_index; if (tb(tb_idx).nof_retxs == 0) { - prev_tx_params.tb[tb_idx]->tbs_bytes = cw.tb_size_bytes; - prev_tx_params.tb[tb_idx]->olla_mcs = ctx.olla_mcs; + prev_tx_params.tb[tb_idx]->tbs_bytes = cw.tb_size_bytes; + prev_tx_params.tb[tb_idx]->olla_mcs = ctx.olla_mcs; + prev_tx_params.tb[tb_idx]->contains_srb_data = contains_srb_data; } else { srsran_assert(ctx.dci_cfg_type == prev_tx_params.dci_cfg_type, "DCI format and RNTI type cannot change during DL HARQ retxs"); diff --git a/lib/scheduler/ue_scheduling/harq_process.h b/lib/scheduler/ue_scheduling/harq_process.h index f65684d80f..e40326ffd6 100644 --- a/lib/scheduler/ue_scheduling/harq_process.h +++ b/lib/scheduler/ue_scheduling/harq_process.h @@ -283,6 +283,8 @@ class dl_harq_process : public detail::harq_process pdsch_mcs_table mcs_table; sch_mcs_index mcs; unsigned tbs_bytes; + /// Flag indicating whether the TB contains data from SRB or not. + bool contains_srb_data; /// \brief MCS originally suggested by the OLLA. It might differ from the actual MCS used. std::optional olla_mcs; }; @@ -352,7 +354,8 @@ class dl_harq_process : public detail::harq_process /// \brief Stores grant parameters that are associated with the HARQ allocation (e.g. DCI format, PRBs, MCS) so that /// they can be later fetched and optionally reused. - void save_alloc_params(const dl_harq_sched_context& ctx, const pdsch_information& pdsch); + void + save_alloc_params(const dl_harq_sched_context& ctx, const pdsch_information& pdsch, bool contains_srb_data = false); void increment_pucch_counter(); diff --git a/lib/scheduler/ue_scheduling/ue.cpp b/lib/scheduler/ue_scheduling/ue.cpp index adcb3531ea..7c7e288b88 100644 --- a/lib/scheduler/ue_scheduling/ue.cpp +++ b/lib/scheduler/ue_scheduling/ue.cpp @@ -139,6 +139,19 @@ unsigned ue::pending_dl_newtx_bytes(lcid_t lcid) const return lcid != INVALID_LCID ? dl_lc_ch_mgr.pending_bytes(lcid) : dl_lc_ch_mgr.pending_bytes(); } +unsigned ue::pending_dl_srb_newtx_bytes() const +{ + return dl_lc_ch_mgr.pending_bytes(LCID_SRB1) + dl_lc_ch_mgr.pending_bytes(LCID_SRB2); +} + +unsigned ue::pending_ul_srb_newtx_bytes() const +{ + // LCG ID 0 is used by default for SRBs as per TS 38.331, clause 9.2.1. + // NOTE: Ensure SRB LCG ID matches the one sent to UE. + const lcg_id_t srb_lcg_id = uint_to_lcg_id(0); + return ul_lc_ch_mgr.pending_bytes(srb_lcg_id); +} + unsigned ue::pending_ul_newtx_bytes() const { constexpr static unsigned SR_GRANT_BYTES = 512; diff --git a/lib/scheduler/ue_scheduling/ue.h b/lib/scheduler/ue_scheduling/ue.h index a791d8fdf4..137a6ac5e9 100644 --- a/lib/scheduler/ue_scheduling/ue.h +++ b/lib/scheduler/ue_scheduling/ue.h @@ -128,6 +128,14 @@ class ue /// \remark Excludes SRB0 and UE Contention Resolution Identity CE. bool has_pending_dl_newtx_bytes() const { return dl_lc_ch_mgr.has_pending_bytes(); } + /// \brief Checks if there are DL pending bytes in SRBs that are yet to be allocated in a DL HARQ. + /// This method is faster than computing \c pending_dl_newtx_bytes() > 0. + /// \remark Excludes SRB0 pending bytes. + bool has_pending_dl_srb_newtx_bytes() const + { + return dl_lc_ch_mgr.has_pending_bytes(LCID_SRB1) or dl_lc_ch_mgr.has_pending_bytes(LCID_SRB2); + } + /// \brief Checks if there are DL pending bytes for a specific LCID that are yet to be allocated in a DL HARQ. bool has_pending_dl_newtx_bytes(lcid_t lcid) const { return dl_lc_ch_mgr.has_pending_bytes(lcid); } @@ -147,10 +155,22 @@ class ue /// \return The number of DL pending bytes that are not already allocated in a DL HARQ. unsigned pending_dl_newtx_bytes(lcid_t lcid = lcid_t::INVALID_LCID) const; + /// \brief Computes the number of DL pending bytes in SRBs that are not already allocated in a DL + /// HARQ. The value is used to derive the required transport block size for an DL grant. + /// \return The number of DL pending bytes in SRBs that are not already allocated in a DL HARQ. + /// \remark Excludes SRB0 pending bytes. + unsigned pending_dl_srb_newtx_bytes() const; + /// \brief Computes the number of UL pending bytes that are not already allocated in a UL HARQ. The value is used /// to derive the required transport block size for an UL grant. unsigned pending_ul_newtx_bytes() const; + /// \brief Computes the number of UL pending bytes in SRBs. The value is used to derive the required transport block + /// size for an UL grant. + /// \return The number of UL pending bytes in SRBs. + /// \remark The returned UL pending bytes does not exclude already allocated bytes in UL HARQ(s). + unsigned pending_ul_srb_newtx_bytes() const; + /// \brief Returns whether a SR indication handling is pending. bool has_pending_sr() const; diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index 5741e6443a..1650c1a9cc 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -474,8 +474,6 @@ alloc_result ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gra } ue_cc->last_pdsch_allocated_slot = pdsch_alloc.slot; - h_dl.save_alloc_params(pdsch_sched_ctx, msg.pdsch_cfg); - if (is_new_data) { // Set MAC logical channels to schedule in this PDU if it is a newtx. u.build_dl_transport_block_info(msg.tb_list.emplace_back(), msg.pdsch_cfg.codewords[0].tb_size_bytes); @@ -484,6 +482,20 @@ alloc_result ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gra msg.context.buffer_occupancy = u.pending_dl_newtx_bytes(); } + bool contains_srb_data = false; + if (is_new_data) { + const auto* it = std::find_if(msg.tb_list.back().lc_chs_to_sched.begin(), + msg.tb_list.back().lc_chs_to_sched.end(), + [](const dl_msg_lc_info& lc_info) { + return lc_info.lcid.is_sdu() and lc_info.lcid.to_lcid() < LCID_MIN_DRB; + }); + contains_srb_data = it != msg.tb_list.back().lc_chs_to_sched.end(); + } else { + contains_srb_data = h_dl.last_alloc_params().tb[0]->contains_srb_data; + } + + h_dl.save_alloc_params(pdsch_sched_ctx, msg.pdsch_cfg, contains_srb_data); + return {alloc_status::success, h_dl.last_alloc_params().tb[0]->tbs_bytes}; } diff --git a/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp b/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp index 70a322f61a..5321353891 100644 --- a/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp +++ b/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp @@ -1254,7 +1254,7 @@ unsigned ue_fallback_scheduler::fill_dl_srb_grant(ue& u, } // Save in HARQ the parameters set for this PDCCH and PDSCH PDUs. - h_dl.save_alloc_params(dl_harq_sched_context{pdcch.dci.type}, msg.pdsch_cfg); + h_dl.save_alloc_params(dl_harq_sched_context{pdcch.dci.type}, msg.pdsch_cfg, true); return srb1_bytes_allocated; } diff --git a/lib/srsvec/simd.h b/lib/srsvec/simd.h index e27cf6d618..3e43fa02e3 100644 --- a/lib/srsvec/simd.h +++ b/lib/srsvec/simd.h @@ -31,6 +31,7 @@ #ifndef __clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#pragma GCC diagnostic ignored "-Wuninitialized" #endif // __clang__ #include diff --git a/lib/support/executors/task_execution_manager.cpp b/lib/support/executors/task_execution_manager.cpp index b7647bf176..bc200617cd 100644 --- a/lib/support/executors/task_execution_manager.cpp +++ b/lib/support/executors/task_execution_manager.cpp @@ -208,8 +208,12 @@ class common_task_execution_context : public task_execution_context // Create executors that own the strand through reference counting. for (unsigned i = 0; i != strand_cfg.queues.size(); ++i) { - enqueue_priority prio = detail::queue_index_to_enqueue_priority(i, strand_cfg.queues.size()); - execs.emplace_back(strand_cfg.queues[i].name, make_priority_task_executor_ptr(prio, shared_strand)); + enqueue_priority prio = detail::queue_index_to_enqueue_priority(i, strand_cfg.queues.size()); + auto prio_exec = make_priority_task_executor_ptr(prio, shared_strand); + if (strand_cfg.queues[i].synchronous) { + prio_exec = make_sync_executor(std::move(prio_exec)); + } + execs.emplace_back(strand_cfg.queues[i].name, std::move(prio_exec)); } } else { // Single priority level case. @@ -217,6 +221,9 @@ class common_task_execution_context : public task_execution_context qparams.policy = strand_cfg.queues[0].policy; qparams.size = strand_cfg.queues[0].size; auto strand_ptr = make_task_strand_ptr(exec_type{basic_exec}, qparams); + if (strand_cfg.queues[0].synchronous) { + strand_ptr = make_sync_executor(std::move(strand_ptr)); + } // Strand becomes the executor. execs.emplace_back(strand_cfg.queues[0].name, std::move(strand_ptr)); diff --git a/tests/benchmarks/du_high/du_high_benchmark.cpp b/tests/benchmarks/du_high/du_high_benchmark.cpp index ddf756f3c0..9e59e78d4b 100644 --- a/tests/benchmarks/du_high/du_high_benchmark.cpp +++ b/tests/benchmarks/du_high/du_high_benchmark.cpp @@ -638,16 +638,17 @@ class du_high_bench cfg.sched_ue_metrics_notifier = &metrics_handler; // Increase nof. PUCCH resources to accommodate more UEs. - cfg.cells[0].pucch_cfg.nof_sr_resources = 30; - cfg.cells[0].pucch_cfg.nof_csi_resources = 30; - cfg.cells[0].pucch_cfg.nof_ue_pucch_f2_res_harq = 8; - cfg.cells[0].pucch_cfg.nof_ue_pucch_f1_res_harq = 8; - cfg.cells[0].pucch_cfg.nof_cell_harq_pucch_res_sets = 4; - cfg.cells[0].pucch_cfg.f1_params.nof_cyc_shifts = srsran::nof_cyclic_shifts::six; - cfg.cells[0].pucch_cfg.f1_params.occ_supported = true; - cfg.sched_cfg.ue.max_pucchs_per_slot = 61; - cfg.sched_cfg.ue.max_puschs_per_slot = 61; - cfg.sched_cfg.ue.max_ul_grants_per_slot = 64; + cfg.cells[0].pucch_cfg.nof_sr_resources = 30; + cfg.cells[0].pucch_cfg.nof_csi_resources = 30; + cfg.cells[0].pucch_cfg.nof_ue_pucch_f2_res_harq = 8; + cfg.cells[0].pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq = 8; + cfg.cells[0].pucch_cfg.nof_cell_harq_pucch_res_sets = 4; + auto& f1_params = cfg.cells[0].pucch_cfg.f0_or_f1_params.emplace(); + f1_params.nof_cyc_shifts = srsran::nof_cyclic_shifts::six; + f1_params.occ_supported = true; + cfg.sched_cfg.ue.max_pucchs_per_slot = 61; + cfg.sched_cfg.ue.max_puschs_per_slot = 61; + cfg.sched_cfg.ue.max_ul_grants_per_slot = 64; du_hi = std::make_unique(cfg); diff --git a/tests/benchmarks/phy/upper/equalization/channel_equalizer_benchmark.cpp b/tests/benchmarks/phy/upper/equalization/channel_equalizer_benchmark.cpp index 2c19974a0d..ae26cc682f 100644 --- a/tests/benchmarks/phy/upper/equalization/channel_equalizer_benchmark.cpp +++ b/tests/benchmarks/phy/upper/equalization/channel_equalizer_benchmark.cpp @@ -20,18 +20,19 @@ * */ +#include "srsran/phy/constants.h" +#include "srsran/phy/support/re_buffer.h" +#include "srsran/phy/upper/equalization/dynamic_ch_est_list.h" #include "srsran/phy/upper/equalization/equalization_factories.h" +#include "srsran/ran/cyclic_prefix.h" #include "srsran/support/benchmark_utils.h" +#include "srsran/support/math_utils.h" #include "srsran/support/srsran_test.h" #include #include using namespace srsran; -// Equalizer data dimensions. -using re_dims = channel_equalizer::re_list::dims; -using ch_dims = channel_equalizer::ch_est_list::dims; - // Random generator. static std::mt19937 rgen(0); @@ -123,24 +124,22 @@ int main(int argc, char** argv) unsigned nof_subcarriers = nof_prb * NRE; // Create input and output data tensors. - dynamic_tensor(re_dims::nof_dims), cbf16_t, re_dims> rx_symbols( - {nof_subcarriers * nof_ofdm_symbols, nof_rx_ports}); - std::vector eq_symbols(nof_subcarriers * nof_ofdm_symbols * nof_tx_layers); - std::vector eq_noise_vars(nof_subcarriers * nof_ofdm_symbols * nof_tx_layers); + dynamic_re_buffer rx_symbols(nof_rx_ports, nof_subcarriers * nof_ofdm_symbols); + std::vector eq_symbols(nof_subcarriers * nof_ofdm_symbols * nof_tx_layers); + std::vector eq_noise_vars(nof_subcarriers * nof_ofdm_symbols * nof_tx_layers); // Create channel estimates tensor. - dynamic_tensor(ch_dims::nof_dims), cbf16_t, ch_dims> channel_ests( - {nof_subcarriers * nof_ofdm_symbols, nof_rx_ports, nof_tx_layers}); + dynamic_ch_est_list channel_ests(nof_subcarriers * nof_ofdm_symbols, nof_rx_ports, nof_tx_layers); for (unsigned i_rx_port = 0; i_rx_port != nof_rx_ports; ++i_rx_port) { // Generate Rx symbols. - span symbols = rx_symbols.get_view<>({i_rx_port}); + span symbols = rx_symbols.get_slice(i_rx_port); std::generate( symbols.begin(), symbols.end(), [&symbol_dist]() { return cf_t(symbol_dist(rgen), symbol_dist(rgen)); }); for (unsigned i_tx_layer = 0; i_tx_layer != nof_tx_layers; ++i_tx_layer) { // Generate estimates. - span ests = channel_ests.get_view<>({i_rx_port, i_tx_layer}); + span ests = channel_ests.get_channel(i_rx_port, i_tx_layer); std::generate(ests.begin(), ests.end(), [&ch_mag_dist, &ch_phase_dist]() { return std::polar(ch_mag_dist(rgen), ch_phase_dist(rgen)); }); diff --git a/tests/benchmarks/scheduler/scheduler_multi_ue_benchmark.cpp b/tests/benchmarks/scheduler/scheduler_multi_ue_benchmark.cpp index 7ef9219232..9ea1c6018f 100644 --- a/tests/benchmarks/scheduler/scheduler_multi_ue_benchmark.cpp +++ b/tests/benchmarks/scheduler/scheduler_multi_ue_benchmark.cpp @@ -26,7 +26,7 @@ #include "srsran/adt/circular_array.h" #include "srsran/scheduler/scheduler_factory.h" #include "srsran/support/benchmark_utils.h" -#include "srsran/support/math/gcd.h" +#include "srsran/support/math/lcm.h" #include using namespace srsran; @@ -89,12 +89,12 @@ class multi_ue_sched_simulator sch(create_scheduler(scheduler_config{expert_cfg, cfg_notif, metric_notif})), next_sl_tx(builder_params.scs_common, 0) { - du_cell_cfgs = {config_helpers::make_default_du_cell_config(builder_params)}; - du_cell_cfgs[0].pucch_cfg.f2_params.max_code_rate = max_pucch_code_rate::dot_35; - du_cell_cfgs[0].pucch_cfg.nof_csi_resources = 4; - du_cell_cfgs[0].pucch_cfg.nof_sr_resources = 2; - du_cell_cfgs[0].pucch_cfg.nof_ue_pucch_f1_res_harq = 3; - du_cell_cfgs[0].pucch_cfg.nof_ue_pucch_f2_res_harq = 6; + du_cell_cfgs = {config_helpers::make_default_du_cell_config(builder_params)}; + du_cell_cfgs[0].pucch_cfg.f2_params.max_code_rate = max_pucch_code_rate::dot_35; + du_cell_cfgs[0].pucch_cfg.nof_csi_resources = 4; + du_cell_cfgs[0].pucch_cfg.nof_sr_resources = 2; + du_cell_cfgs[0].pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq = 3; + du_cell_cfgs[0].pucch_cfg.nof_ue_pucch_f2_res_harq = 6; sched_cell_configuration_request_message cell_cfg_msg = test_helpers::make_default_sched_cell_configuration_request(builder_params); diff --git a/tests/e2e/tests/handover.py b/tests/e2e/tests/handover.py index 3eb3b2ef43..98132dae9a 100644 --- a/tests/e2e/tests/handover.py +++ b/tests/e2e/tests/handover.py @@ -222,7 +222,7 @@ def _handover_multi_ues( always_download_artifacts=always_download_artifacts, ) - start_network(ue_array, gnb, fivegc, gnb_post_cmd="log --mac_level=debug --cu_level=debug") + start_network(ue_array, gnb, fivegc, gnb_post_cmd=("log --cu_level=debug", "log --mac_level=debug")) ue_attach_info_dict = ue_start_and_attach(ue_array, gnb, fivegc) diff --git a/tests/e2e/tests/iperf.py b/tests/e2e/tests/iperf.py index 76c51d8927..de44ab1e8b 100644 --- a/tests/e2e/tests/iperf.py +++ b/tests/e2e/tests/iperf.py @@ -541,7 +541,7 @@ def test_zmq( always_download_artifacts=False, bitrate_threshold=0, ue_stop_timeout=1, - gnb_post_cmd="log --hex_max_size=32 cu_cp --inactivity_timer=600", + gnb_post_cmd=("log --hex_max_size=32 cu_cp --inactivity_timer=600", ""), ) @@ -626,7 +626,7 @@ def _iperf( always_download_artifacts: bool, warning_as_errors: bool = True, bitrate_threshold: float = 0, # bitrate != 0 - gnb_post_cmd: str = "", + gnb_post_cmd: Tuple[str, ...] = tuple(), plmn: Optional[PLMN] = None, common_search_space_enable: bool = False, prach_config_index=-1, diff --git a/tests/e2e/tests/iperf_alt.py b/tests/e2e/tests/iperf_alt.py index 9878424e97..08b61f9e88 100644 --- a/tests/e2e/tests/iperf_alt.py +++ b/tests/e2e/tests/iperf_alt.py @@ -90,7 +90,7 @@ def test_multiple_configs_zmq( always_download_artifacts=False, ) - ue_attach_info_dict = start_and_attach((ue,), gnb, fivegc, gnb_post_cmd=config) + ue_attach_info_dict = start_and_attach((ue,), gnb, fivegc, gnb_post_cmd=(config,)) iperf_parallel( ue_attach_info_dict, diff --git a/tests/e2e/tests/ping.py b/tests/e2e/tests/ping.py index c1c831bdc8..b0abdad367 100644 --- a/tests/e2e/tests/ping.py +++ b/tests/e2e/tests/ping.py @@ -201,7 +201,7 @@ def test_zmq( time_alignment_calibration=0, ue_stop_timeout=1, enable_security_mode=ciphering, - post_command="cu_cp --inactivity_timer=600", + post_command=("cu_cp --inactivity_timer=600", ""), ) @@ -242,7 +242,10 @@ def test_zmq_valgrind( time_alignment_calibration=0, log_search=False, always_download_artifacts=True, - pre_command="valgrind --leak-check=full --track-origins=yes --exit-on-first-error=no --error-exitcode=22", + pre_command=( + "valgrind --leak-check=full --track-origins=yes --exit-on-first-error=no --error-exitcode=22", + "valgrind --leak-check=full --track-origins=yes --exit-on-first-error=no --error-exitcode=22", + ), gnb_stop_timeout=gnb_stop_timeout, ) stop( @@ -354,8 +357,8 @@ def _ping( always_download_artifacts: bool = False, ping_count: int = 10, reattach_count: int = 0, - pre_command: str = "", - post_command: str = "", + pre_command: Tuple[str, ...] = tuple(), + post_command: Tuple[str, ...] = tuple(), gnb_stop_timeout: int = 0, ue_stop_timeout: int = 0, plmn: Optional[PLMN] = None, diff --git a/tests/e2e/tests/reestablishment.py b/tests/e2e/tests/reestablishment.py index 43be0459de..83a7dcd662 100644 --- a/tests/e2e/tests/reestablishment.py +++ b/tests/e2e/tests/reestablishment.py @@ -427,7 +427,7 @@ def _test_reestablishments( always_download_artifacts=always_download_artifacts, ) - start_network(ue_array, gnb, fivegc, gnb_post_cmd="log --mac_level=debug --cu_level=debug") + start_network(ue_array, gnb, fivegc, gnb_post_cmd=("log --cu_level=debug", "log --mac_level=debug")) ue_attach_info_dict = ue_start_and_attach(ue_array, gnb, fivegc) diff --git a/tests/e2e/tests/steps/kpis.py b/tests/e2e/tests/steps/kpis.py index 3b5e7b6665..f218d5a2c2 100644 --- a/tests/e2e/tests/steps/kpis.py +++ b/tests/e2e/tests/steps/kpis.py @@ -22,7 +22,6 @@ KPI related logic """ -from contextlib import suppress from dataclasses import dataclass, fields from typing import Optional, Sequence @@ -70,34 +69,21 @@ def get_kpis( # GNB gnb_metrics: Metrics = gnb.GetMetrics(Empty()) - ul_nof_ok_aggregate = 0 - dl_nof_ok_aggregate = 0 - ul_nof_ko_aggregate = 0 - dl_nof_ko_aggregate = 0 + kpis.ul_brate_aggregate = gnb_metrics.total.ul_bitrate + kpis.ul_brate_min = gnb_metrics.total.ul_bitrate_min + kpis.ul_brate_max = gnb_metrics.total.ul_bitrate_max - for ue_info in gnb_metrics.ue_array: - kpis.ul_brate_aggregate += ue_info.ul_bitrate - kpis.dl_brate_aggregate += ue_info.dl_bitrate + kpis.dl_brate_aggregate = gnb_metrics.total.dl_bitrate + kpis.dl_brate_min = gnb_metrics.total.dl_bitrate_min + kpis.dl_brate_max = gnb_metrics.total.dl_bitrate_max - with suppress(TypeError): - kpis.ul_brate_min = min(*filter(lambda value: value != 0, (kpis.ul_brate_min, ue_info.ul_bitrate_min))) - with suppress(TypeError): - kpis.dl_brate_min = min(*filter(lambda value: value != 0, (kpis.dl_brate_min, ue_info.dl_bitrate_min))) + kpis.nof_ko_aggregate = gnb_metrics.total.dl_nof_ko + gnb_metrics.total.ul_nof_ko - kpis.ul_brate_max = max(ue_info.ul_bitrate_max, kpis.ul_brate_max) - kpis.dl_brate_max = max(ue_info.dl_bitrate_max, kpis.dl_brate_max) + total_ul_ko_ok = gnb_metrics.total.ul_nof_ok + gnb_metrics.total.ul_nof_ko + total_dl_ko_ok = gnb_metrics.total.dl_nof_ok + gnb_metrics.total.dl_nof_ko - ul_nof_ok_aggregate += ue_info.ul_nof_ok - dl_nof_ok_aggregate += ue_info.dl_nof_ok - ul_nof_ko_aggregate += ue_info.ul_nof_ko - dl_nof_ko_aggregate += ue_info.dl_nof_ko - kpis.nof_ko_aggregate += ue_info.ul_nof_ko + ue_info.dl_nof_ko - - tota_ul_ko_ok = ul_nof_ok_aggregate + ul_nof_ko_aggregate - total_dl_ko_ok = dl_nof_ok_aggregate + dl_nof_ko_aggregate - - kpis.ul_bler_aggregate = 0 if not tota_ul_ko_ok else ul_nof_ko_aggregate / tota_ul_ko_ok - kpis.dl_bler_aggregate = 0 if not total_dl_ko_ok else dl_nof_ko_aggregate / total_dl_ko_ok + kpis.ul_bler_aggregate = 0 if not total_ul_ko_ok else gnb_metrics.total.ul_nof_ko / total_ul_ko_ok + kpis.dl_bler_aggregate = 0 if not total_dl_ko_ok else gnb_metrics.total.dl_nof_ko / total_dl_ko_ok # UE for ue in ue_array: diff --git a/tests/e2e/tests/steps/stub.py b/tests/e2e/tests/steps/stub.py index 3453811011..3c624ff843 100644 --- a/tests/e2e/tests/steps/stub.py +++ b/tests/e2e/tests/steps/stub.py @@ -69,8 +69,8 @@ def start_and_attach( ue_startup_timeout: int = UE_STARTUP_TIMEOUT, gnb_startup_timeout: int = GNB_STARTUP_TIMEOUT, fivegc_startup_timeout: int = FIVEGC_STARTUP_TIMEOUT, - gnb_pre_cmd: str = "", - gnb_post_cmd: str = "", + gnb_pre_cmd: Tuple[str, ...] = tuple(), + gnb_post_cmd: Tuple[str, ...] = tuple(), attach_timeout: int = ATTACH_TIMEOUT, plmn: Optional[PLMN] = None, ) -> Dict[UEStub, UEAttachedInfo]: @@ -114,8 +114,8 @@ def start_network( fivegc: FiveGCStub, gnb_startup_timeout: int = GNB_STARTUP_TIMEOUT, fivegc_startup_timeout: int = FIVEGC_STARTUP_TIMEOUT, - gnb_pre_cmd: str = "", - gnb_post_cmd: str = "", + gnb_pre_cmd: Tuple[str, ...] = tuple(), + gnb_post_cmd: Tuple[str, ...] = tuple(), plmn: Optional[PLMN] = None, ): """ diff --git a/tests/e2e/tests/test_mode.py b/tests/e2e/tests/test_mode.py index 0257a5ae3e..5a033d332e 100644 --- a/tests/e2e/tests/test_mode.py +++ b/tests/e2e/tests/test_mode.py @@ -126,9 +126,10 @@ def test_ue( fivegc_definition=fivegc_def, start_info=StartInfo( timeout=gnb_startup_timeout, - post_commands=f"cell_cfg --nof_antennas_dl {nof_ant} --nof_antennas_ul {nof_ant}" - + " " - + extra_config, + post_commands=( + "", + f"cell_cfg --nof_antennas_dl {nof_ant} --nof_antennas_ul {nof_ant}" + " " + extra_config, + ), ), ) ) @@ -240,7 +241,7 @@ def _test_ru( fivegc_definition=FiveGCDefinition(amf_ip=gnb_def.zmq_ip, amf_port=38412), start_info=StartInfo( timeout=gnb_startup_timeout, - post_commands="amf --no_core 1", + post_commands=("amf --no_core 1",), ), ) ) diff --git a/tests/e2e/tests/test_mode/config_ue.yml b/tests/e2e/tests/test_mode/config_ue.yml index 9a579fe085..9096968ce8 100644 --- a/tests/e2e/tests/test_mode/config_ue.yml +++ b/tests/e2e/tests/test_mode/config_ue.yml @@ -45,7 +45,7 @@ cell_cfg: prach_frequency_start: 12 pucch: sr_period_ms: 10 - f1_nof_cell_res_sr: 30 + f0_or_f1_nof_cell_res_sr: 30 min_k1: 2 csi: csi_rs_period: 40 diff --git a/tests/e2e/tests/validate_configuration.py b/tests/e2e/tests/validate_configuration.py index 0aed036fd3..a95803bfeb 100644 --- a/tests/e2e/tests/validate_configuration.py +++ b/tests/e2e/tests/validate_configuration.py @@ -137,7 +137,7 @@ def run_config( fivegc_definition=fivegc_def, start_info=StartInfo( timeout=timeout, - post_commands=(f"log --filename stdout {extra_config}"), + post_commands=(f"log --filename stdout {extra_config}",), ), ) ) diff --git a/tests/e2e/tests/viavi.py b/tests/e2e/tests/viavi.py index 00d4b40213..50363b6193 100644 --- a/tests/e2e/tests/viavi.py +++ b/tests/e2e/tests/viavi.py @@ -371,7 +371,7 @@ def _test_viavi( fivegc_definition=FiveGCDefinition(amf_ip=amf_ip, amf_port=amf_port), start_info=StartInfo( timeout=gnb_startup_timeout, - post_commands=test_declaration.gnb_extra_commands, + post_commands=(test_declaration.gnb_extra_commands,), ), ) ) diff --git a/tests/integrationtests/phy/upper/channel_processors/CMakeLists.txt b/tests/integrationtests/phy/upper/channel_processors/CMakeLists.txt index 6e7fe7a669..fdc907b831 100644 --- a/tests/integrationtests/phy/upper/channel_processors/CMakeLists.txt +++ b/tests/integrationtests/phy/upper/channel_processors/CMakeLists.txt @@ -31,10 +31,8 @@ target_link_libraries(pxsch_bler_test srsran_channel_precoder srslog srsvec - gtest - gtest_main ) -gtest_discover_tests(pxsch_bler_test) +add_test(pxsch_bler_test pxsch_bler_test -R 10) add_executable(pxsch_chain_test pxsch_chain_test.cpp) target_link_libraries(pxsch_chain_test diff --git a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test.cpp b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test.cpp index b2765b9a5d..71259d1fd1 100644 --- a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test.cpp +++ b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test.cpp @@ -39,7 +39,7 @@ #include "srsran/support/executors/task_worker_pool.h" #include "fmt/ostream.h" #include -#include +#include #include #include #include @@ -47,59 +47,75 @@ using namespace srsran; -static constexpr subcarrier_spacing scs = subcarrier_spacing::kHz30; -static constexpr uint16_t rnti = 0x1234; -static constexpr unsigned bwp_size_rb = 273; -static constexpr unsigned bwp_start_rb = 0; -static constexpr unsigned nof_layers = 1; -static constexpr unsigned nof_ofdm_symbols = 14; -static const symbol_slot_mask dmrs_symbol_mask = {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}; -static constexpr unsigned nof_ldpc_iterations = 10; -static constexpr dmrs_type dmrs = dmrs_type::TYPE1; -static constexpr unsigned nof_cdm_groups_without_data = 2; -static constexpr cyclic_prefix cp = cyclic_prefix::NORMAL; -static constexpr unsigned rv = 0; -static constexpr unsigned n_id = 0; -static constexpr unsigned scrambling_id = 0; -static constexpr bool n_scid = false; -static constexpr bool use_early_stop = true; -static unsigned max_nof_threads = std::thread::hardware_concurrency(); -static bool show_stats = true; -static unsigned nof_repetitions = 10 * 10240 * pow2(to_numerology_value(scs)); +static constexpr subcarrier_spacing scs = subcarrier_spacing::kHz30; +static constexpr uint16_t rnti = 0x1234; +static constexpr unsigned bwp_size_rb = 273; +static constexpr unsigned bwp_start_rb = 0; +static constexpr unsigned nof_layers = 1; +static constexpr unsigned nof_ofdm_symbols = 14; +static const symbol_slot_mask dmrs_symbol_mask = {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; +static constexpr unsigned nof_ldpc_iterations = 10; +static constexpr dmrs_type dmrs = dmrs_type::TYPE1; +static constexpr unsigned nof_cdm_groups_without_data = 2; +static constexpr cyclic_prefix cp = cyclic_prefix::NORMAL; +static constexpr unsigned rv = 0; +static constexpr unsigned n_id = 0; +static constexpr unsigned scrambling_id = 0; +static constexpr bool n_scid = false; +static constexpr bool use_early_stop = true; +static unsigned max_nof_threads = std::thread::hardware_concurrency(); +static bool show_stats = true; +static unsigned nof_repetitions = 1000; +static std::string channel_delay_profile = "single-tap"; +static std::string channel_fading_distribution = "uniform-phase"; +static float sinr_dB = 60.0F; +static unsigned nof_corrupted_re_per_ofdm_symbol = 0; +static unsigned nof_rx_ports = 2; +static pusch_mcs_table mcs_table = pusch_mcs_table::qam64; +static sch_mcs_index mcs_index = 20; +static prb_interval freq_allocation = {bwp_start_rb, bwp_size_rb}; +static bool enable_dc_position = false; namespace { -// Test parameters. -struct pxsch_bler_params { - // Fading delay profile: TDLA, TDLB, or TDLC. - std::string channel_delay_profile; - // Signal to Interference and Noise Ratio in decibels. - float sinr_dB; - // Number of receive ports. - unsigned nof_rx_ports; - // Modulation and code scheme table - pusch_mcs_table mcs_table; - // Modulation and code scheme index. - sch_mcs_index mcs_index; - // Frequency allocation. - prb_interval freq_allocation; -}; +const char* to_string(pusch_mcs_table table) +{ + switch (table) { + case pusch_mcs_table::qam64: + return "qam64"; + case pusch_mcs_table::qam256: + return "qam256"; + case pusch_mcs_table::qam64LowSe: + return "qam64LowSe"; + case pusch_mcs_table::qam64_tp: + return "qam64_tp"; + case pusch_mcs_table::qam64LowSe_tp: + return "qam64LowSe_tp"; + } + return "invalid"; +} -std::ostream& operator<<(std::ostream& os, const pxsch_bler_params& params) +std::optional to_mcs_table(const char* str) { - fmt::print(os, - "channel={} sinr={}dB nof_rx_ports={} mcs={} f_alloc=from{}to{}", - params.channel_delay_profile, - params.sinr_dB, - params.nof_rx_ports, - params.mcs_index.to_uint(), - params.freq_allocation.start(), - params.freq_allocation.stop()); - return os; + for (unsigned table_idx = 0; table_idx != 5; ++table_idx) { + pusch_mcs_table mcs_table = static_cast(table_idx); + if (strcmp(str, to_string(mcs_table)) == 0) { + return mcs_table; + } + } + + return std::nullopt; } -class PxschBlerTestFixture : public ::testing::TestWithParam +class pxsch_bler_test { +public: + pxsch_bler_test() { setup(); } + + void run() { loop(); } + + ~pxsch_bler_test() { teardown(); } + private: std::shared_ptr create_grid_factory() { @@ -109,7 +125,6 @@ class PxschBlerTestFixture : public ::testing::TestWithParam return create_resource_grid_factory(precod_factory); } -protected: class pdsch_processor_notifier_adaptor : public pdsch_processor_notifier { public: @@ -154,7 +169,6 @@ class PxschBlerTestFixture : public ::testing::TestWithParam return sch; } - private: bool completed = false; pusch_processor_result_control uci; pusch_processor_result_data sch; @@ -162,7 +176,7 @@ class PxschBlerTestFixture : public ::testing::TestWithParam std::condition_variable cvar; }; - void SetUp() override + void setup() { // Prepare executors. worker_pool = @@ -172,19 +186,13 @@ class PxschBlerTestFixture : public ::testing::TestWithParam // Prepare logging. srslog::fetch_basic_logger("ALL").set_level(srslog::basic_levels::warning); - // Extract test parameters. - const std::string& channel = GetParam().channel_delay_profile; - pusch_mcs_table mcs_table = GetParam().mcs_table; - sch_mcs_index mcs_index = GetParam().mcs_index; - prb_interval rb_mapping = GetParam().freq_allocation; - // Compute modulation and code scheme. sch_mcs_description mcs_descr = pusch_mcs_get_config(mcs_table, mcs_index, false); // Calculate transport block size. tbs_calculator_configuration tbs_config = {}; tbs_config.mcs_descr = mcs_descr; - tbs_config.n_prb = rb_mapping.length(); + tbs_config.n_prb = freq_allocation.length(); tbs_config.nof_layers = nof_layers; tbs_config.nof_symb_sh = nof_ofdm_symbols; tbs_config.nof_dmrs_prb = dmrs.nof_dmrs_per_rb() * dmrs_symbol_mask.count() * nof_cdm_groups_without_data; @@ -195,7 +203,8 @@ class PxschBlerTestFixture : public ::testing::TestWithParam get_ldpc_base_graph(mcs_descr.get_normalised_target_code_rate(), units::bits(tbs)); // Generate frequency allocation. - rb_allocation freq_alloc = rb_allocation::make_type1(rb_mapping.start(), rb_mapping.length(), std::nullopt); + rb_allocation freq_alloc = + rb_allocation::make_type1(freq_allocation.start(), freq_allocation.length(), std::nullopt); // Create PDSCH processor factory. std::shared_ptr pdsch_proc_factory = @@ -221,7 +230,7 @@ class PxschBlerTestFixture : public ::testing::TestWithParam // Create resource grids. tx_grid = grid_factory->create(nof_layers, MAX_NSYMB_PER_SLOT, NRE * MAX_RB); - rx_grid = grid_factory->create(GetParam().nof_rx_ports, MAX_NSYMB_PER_SLOT, NRE * MAX_RB); + rx_grid = grid_factory->create(nof_rx_ports, MAX_NSYMB_PER_SLOT, NRE * MAX_RB); // Calculate number of codeblocks. nof_codeblocks = ldpc::compute_nof_codeblocks(units::bits(tbs), ldpc_base_graph); @@ -245,7 +254,6 @@ class PxschBlerTestFixture : public ::testing::TestWithParam pdsch_config.bwp_size_rb = bwp_size_rb; pdsch_config.bwp_start_rb = bwp_start_rb; pdsch_config.cp = cp; - pdsch_config.codewords = {{mcs_descr.modulation, rv}}; pdsch_config.n_id = n_id; pdsch_config.ref_point = pdsch_processor::pdu_t::PRB0; pdsch_config.dmrs_symbol_mask = dmrs_symbol_mask; @@ -262,8 +270,9 @@ class PxschBlerTestFixture : public ::testing::TestWithParam pdsch_config.ratio_pdsch_data_to_sss_dB = 0.0F; pdsch_config.ratio_pdsch_dmrs_to_sss_dB = get_sch_to_dmrs_ratio_dB(nof_cdm_groups_without_data); pdsch_config.precoding = precoding_configuration::make_wideband(make_identity(nof_layers)); + pdsch_config.codewords.emplace_back(pdsch_processor::codeword_description{mcs_descr.modulation, rv}); - static_vector rx_ports(GetParam().nof_rx_ports); + static_vector rx_ports(nof_rx_ports); std::iota(rx_ports.begin(), rx_ports.end(), 0U); // Prepare PUSCH processor configuration. @@ -288,15 +297,21 @@ class PxschBlerTestFixture : public ::testing::TestWithParam pusch_config.start_symbol_index = 0; pusch_config.nof_symbols = nof_ofdm_symbols; pusch_config.tbs_lbrm = tbs_lbrm_default; - pusch_config.dc_position = {}; //{bwp_size_rb * NRE / 2}; + pusch_config.dc_position = {}; + + if (enable_dc_position) { + pusch_config.dc_position = {bwp_size_rb * NRE / 2}; + } // Resize data to accomodate the transport block. tx_data.resize(tbs / 8); rx_data.resize(tbs / 8); - emulator = std::make_unique(channel, - GetParam().sinr_dB, - GetParam().nof_rx_ports, + emulator = std::make_unique(channel_delay_profile, + channel_fading_distribution, + sinr_dB, + nof_corrupted_re_per_ofdm_symbol, + nof_rx_ports, MAX_RB * NRE, nof_ofdm_symbols, max_nof_threads, @@ -304,12 +319,136 @@ class PxschBlerTestFixture : public ::testing::TestWithParam *executor); } - void TearDown() override + void teardown() { worker_pool->stop(); srslog::flush(); } + void loop() + { + uint64_t count = 0; + uint64_t crc_error_count = 0; + uint64_t data_error_count = 0; + unsigned max_iterations = std::numeric_limits::min(); + unsigned min_iterations = std::numeric_limits::max(); + uint64_t count_iterations = 0; + sample_statistics sinr_stats; + sample_statistics evm_stats; + sample_statistics ta_stats_us; + + std::mt19937 rgen(0); + + // Iterate different seeds. + for (unsigned n = 0; n != nof_repetitions; ++n) { + // Generate random data. + std::generate(tx_data.begin(), tx_data.end(), [&rgen]() { return static_cast(rgen() & 0xff); }); + + // Process PDSCH. + pdsch_processor_notifier_adaptor tx_notifier; + transmitter->process(tx_grid->get_mapper(), tx_notifier, {tx_data}, pdsch_config); + tx_notifier.wait_for_completion(); + + emulator->run(rx_grid->get_writer(), tx_grid->get_reader()); + + // Get a receive buffer. + unique_rx_buffer buffer = + buffer_pool->get_pool().reserve(pusch_config.slot, trx_buffer_identifier(rnti, 0), nof_codeblocks, true); + report_error_if_not(buffer.is_valid(), "Invalid buffer."); + + // Process PUSCH. + pusch_processor_notifier_adaptor rx_notifier; + receiver->process(rx_data, std::move(buffer), rx_notifier, rx_grid->get_reader(), pusch_config); + + const pusch_processor_result_data& sch_result = rx_notifier.wait_for_completion(); + + // Accumulate counters. + ++count; + if (!sch_result.data.tb_crc_ok) { + ++crc_error_count; + } + if (tx_data != rx_data) { + ++data_error_count; + } + + max_iterations = std::max(sch_result.data.ldpc_decoder_stats.get_max(), max_iterations); + min_iterations = std::min(sch_result.data.ldpc_decoder_stats.get_min(), min_iterations); + count_iterations += static_cast(sch_result.data.ldpc_decoder_stats.get_nof_observations() * + sch_result.data.ldpc_decoder_stats.get_mean()); + if (sch_result.csi.get_evm().has_value()) { + evm_stats.update(sch_result.csi.get_evm().value()); + } + if (sch_result.csi.get_sinr_dB().has_value()) { + sinr_stats.update(sch_result.csi.get_sinr_dB().value()); + } + if (sch_result.csi.get_time_alignment().has_value()) { + ta_stats_us.update(sch_result.csi.get_time_alignment()->to_seconds() * 1e6); + } + + // Increment slots. + ++pdsch_config.slot; + ++pusch_config.slot; + + if (show_stats && (n % 100 == 0)) { + // Calculate resultant metrics. + double crc_bler = static_cast(crc_error_count) / static_cast(count); + double data_bler = static_cast(data_error_count) / static_cast(count); + double mean_iterations = static_cast(count_iterations) / static_cast(count * nof_codeblocks); + + fmt::print("[{:>5.1f}%] " + "Iterations={{{:<2} {:<2} {:<3.1f}}}; " + "BLER={:.10f}/{:.10f}; " + "SINR={{{:+.2f} {:+.2f} {:+.2f}}}; " + "EVM={{{:.3f} {:.3f} {:.3f}}}; " + "TA={{{:.2f} {:.2f} {:.2f}}}us\r", + static_cast(n) / static_cast(nof_repetitions) * 100.0, + min_iterations, + max_iterations, + mean_iterations, + crc_bler, + data_bler, + sinr_stats.get_min(), + sinr_stats.get_max(), + sinr_stats.get_mean(), + sinr_stats.get_std(), + evm_stats.get_min(), + evm_stats.get_max(), + evm_stats.get_mean(), + ta_stats_us.get_min(), + ta_stats_us.get_max(), + ta_stats_us.get_mean()); + } + } + + // Calculate resultant metrics. + double crc_bler = static_cast(crc_error_count) / static_cast(count); + double data_bler = static_cast(data_error_count) / static_cast(count); + double mean_iterations = static_cast(count_iterations) / static_cast(count * nof_codeblocks); + + // Print results. + if (show_stats) { + fmt::print("Iterations={{{:<2} {:<2} {:<3.1f}}}; " + "BLER={:.10f}/{:.10f}; " + "SINR={{{:+.2f} {:+.2f} {:+.2f}}}; " + "EVM={{{:.3f} {:.3f} {:.3f}}}; " + "TA={{{:.2f} {:.2f} {:.2f}}}us;\n", + min_iterations, + max_iterations, + mean_iterations, + crc_bler, + data_bler, + sinr_stats.get_min(), + sinr_stats.get_max(), + sinr_stats.get_mean(), + evm_stats.get_min(), + evm_stats.get_max(), + evm_stats.get_mean(), + ta_stats_us.get_min(), + ta_stats_us.get_max(), + ta_stats_us.get_mean()); + } + } + unsigned nof_codeblocks; std::unique_ptr transmitter; @@ -331,145 +470,84 @@ class PxschBlerTestFixture : public ::testing::TestWithParam }; } // namespace -TEST_P(PxschBlerTestFixture, Fading) +static void usage(std::string_view prog) { - uint64_t count = 0; - uint64_t error_count = 0; - unsigned max_iterations = std::numeric_limits::min(); - unsigned min_iterations = std::numeric_limits::max(); - uint64_t count_iterations = 0; - sample_statistics sinr_stats; - sample_statistics evm_stats; - sample_statistics ta_stats_us; - - std::mt19937 rgen(0); - - // Iterate different seeds. - for (unsigned n = 0; n != nof_repetitions; ++n) { - // Generate random data. - std::generate(tx_data.begin(), tx_data.end(), [&rgen]() { return static_cast(rgen() & 0xff); }); - - // Process PDSCH. - pdsch_processor_notifier_adaptor tx_notifier; - transmitter->process(tx_grid->get_mapper(), tx_notifier, {tx_data}, pdsch_config); - tx_notifier.wait_for_completion(); - - emulator->run(rx_grid->get_writer(), tx_grid->get_reader()); - - // Get a receive buffer. - unique_rx_buffer buffer = - buffer_pool->get_pool().reserve(pusch_config.slot, trx_buffer_identifier(rnti, 0), nof_codeblocks, true); - ASSERT_TRUE(buffer.is_valid()); - - // Process PUSCH. - pusch_processor_notifier_adaptor rx_notifier; - receiver->process(rx_data, std::move(buffer), rx_notifier, rx_grid->get_reader(), pusch_config); - - const pusch_processor_result_data& sch_result = rx_notifier.wait_for_completion(); - - // Accumulate counters. - ++count; - if (!sch_result.data.tb_crc_ok) { - ++error_count; - } - max_iterations = std::max(sch_result.data.ldpc_decoder_stats.get_max(), max_iterations); - min_iterations = std::min(sch_result.data.ldpc_decoder_stats.get_min(), min_iterations); - count_iterations += static_cast(sch_result.data.ldpc_decoder_stats.get_nof_observations() * - sch_result.data.ldpc_decoder_stats.get_mean()); - if (sch_result.csi.get_evm().has_value()) { - evm_stats.update(sch_result.csi.get_evm().value()); - } - if (sch_result.csi.get_sinr_dB().has_value()) { - sinr_stats.update(sch_result.csi.get_sinr_dB().value()); - } - if (sch_result.csi.get_time_alignment().has_value()) { - ta_stats_us.update(sch_result.csi.get_time_alignment()->to_seconds() * 1e6); - } - - // Increment slots. - ++pdsch_config.slot; - ++pusch_config.slot; - - // Set following line to 1 for printing partial results. - if (show_stats && (n % 100 == 0)) { - // Calculate resultant metrics. - double bler = static_cast(error_count) / static_cast(count); - double mean_iterations = static_cast(count_iterations) / static_cast(count * nof_codeblocks); + fmt::print("Usage: {}\n", prog); + fmt::print("\t-C Channel delay profile: single-tap, TDLA, TDLB or TDLC. [Default {}]\n", channel_delay_profile); + fmt::print("\t-F Channel fading distribution: uniform-phase or rayleigh. [Default {}]\n", + channel_fading_distribution); + fmt::print("\t-D Toggle enable DC position. [Default {}]\n", enable_dc_position); + fmt::print("\t-S SINR. [Default {}]\n", sinr_dB); + fmt::print("\t-N Number of corrupted RE per OFDM symbol. [Default {}]\n", nof_corrupted_re_per_ofdm_symbol); + fmt::print("\t-P Number of receive ports. [Default {}]\n", nof_rx_ports); + fmt::print("\t-M MCS table. [Default {}]\n", mcs_table); + fmt::print("\t-m MCS index. [Default {}]\n", mcs_index); + fmt::print("\t-R Number of slots to process. [Default {}]\n", nof_repetitions); + fmt::print("\t-v Toggle preliminary stats. [Default {}]\n", show_stats); + fmt::print("\t-h Print this message.\n"); +} - fmt::print("[{:>5.1f}%] " - "Iterations={{{:<2} {:<2} {:<3.1f}}}; " - "BLER={:.10f}; " - "SINR={{{:+.2f} {:+.2f} {:+.2f}}}; " - "EVM={{{:.3f} {:.3f} {:.3f}}}; " - "TA={{{:.2f} {:.2f} {:.2f}}}us\r", - static_cast(n) / static_cast(nof_repetitions) * 100.0, - min_iterations, - max_iterations, - mean_iterations, - bler, - sinr_stats.get_min(), - sinr_stats.get_max(), - sinr_stats.get_mean(), - evm_stats.get_min(), - evm_stats.get_max(), - evm_stats.get_mean(), - ta_stats_us.get_min(), - ta_stats_us.get_max(), - ta_stats_us.get_mean()); +static void parse_args(int argc, char** argv) +{ + int opt = 0; + while ((opt = getopt(argc, argv, "C:F:S:N:P:R:M:m:Dvh")) != -1) { + switch (opt) { + case 'C': + if (optarg != nullptr) { + channel_delay_profile = std::string(optarg); + } + break; + case 'F': + if (optarg != nullptr) { + channel_fading_distribution = std::string(optarg); + } + break; + case 'D': + enable_dc_position = !enable_dc_position; + break; + case 'S': + sinr_dB = std::strtof(optarg, nullptr); + break; + case 'N': + nof_corrupted_re_per_ofdm_symbol = std::strtol(optarg, nullptr, 10); + break; + case 'P': + nof_rx_ports = std::strtol(optarg, nullptr, 10); + break; + case 'M': + if (optarg != nullptr) { + std::optional table = to_mcs_table(optarg); + if (!table) { + fmt::print("Invalid MCS table {}.", optarg); + usage(argv[0]); + exit(-1); + } + mcs_table = table.value(); + } + break; + case 'm': + mcs_index = std::strtol(optarg, nullptr, 10); + break; + case 'R': + nof_repetitions = std::strtol(optarg, nullptr, 10); + break; + case 'v': + show_stats = !show_stats; + break; + case 'h': + default: + usage(argv[0]); + exit(-1); } } - - // Calculate resultant metrics. - double bler = static_cast(error_count) / static_cast(count); - double mean_iterations = static_cast(count_iterations) / static_cast(count * nof_codeblocks); - - // Print results. - if (show_stats) { - fmt::print("Iterations={{{:<2} {:<2} {:<3.1f}}}; " - "BLER={:.10f}; " - "SINR={{{:+.2f} {:+.2f} {:+.2f}}}; " - "EVM={{{:.3f} {:.3f} {:.3f}}}; " - "TA={{{:.2f} {:.2f} {:.2f}}}us;\n", - min_iterations, - max_iterations, - mean_iterations, - bler, - sinr_stats.get_min(), - sinr_stats.get_max(), - sinr_stats.get_mean(), - evm_stats.get_min(), - evm_stats.get_max(), - evm_stats.get_mean(), - ta_stats_us.get_min(), - ta_stats_us.get_max(), - ta_stats_us.get_mean()); - } } -static const std::vector test_cases = { - {"Single-tap", 20.0, 2, pusch_mcs_table::qam64, 28, prb_interval{bwp_start_rb, bwp_size_rb}}, - {"TDLA", 60.0, 1, pusch_mcs_table::qam256, 27, prb_interval{bwp_start_rb, bwp_size_rb}}, - {"TDLA", 60.0, 2, pusch_mcs_table::qam256, 27, prb_interval{bwp_start_rb, bwp_size_rb}}, - {"TDLA", 60.0, 4, pusch_mcs_table::qam256, 27, prb_interval{bwp_start_rb, bwp_size_rb}}, - {"TDLB", 60.0, 1, pusch_mcs_table::qam256, 10, prb_interval{bwp_start_rb, bwp_size_rb}}, - {"TDLB", 60.0, 2, pusch_mcs_table::qam256, 27, prb_interval{bwp_start_rb, bwp_size_rb}}, - {"TDLB", 60.0, 4, pusch_mcs_table::qam256, 27, prb_interval{bwp_start_rb, bwp_size_rb}}, - {"TDLC", 60.0, 1, pusch_mcs_table::qam256, 8, prb_interval{bwp_start_rb, bwp_size_rb}}, - {"TDLC", 60.0, 2, pusch_mcs_table::qam256, 12, prb_interval{bwp_start_rb, bwp_size_rb}}, - {"TDLC", 60.0, 4, pusch_mcs_table::qam256, 19, prb_interval{bwp_start_rb, bwp_size_rb}}}; - -INSTANTIATE_TEST_SUITE_P(PxschBlertest, PxschBlerTestFixture, ::testing::ValuesIn(test_cases)); - int main(int argc, char** argv) { - // If the test is called from CTest, it might have more than one command line argument. In this case, disable the - // partial results and reduce the number of repetitions. - if (argc > 1) { - show_stats = false; - nof_repetitions = 4; - max_nof_threads = 2; - } + parse_args(argc, argv); + + pxsch_bler_test test; + test.run(); - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + return 0; } \ No newline at end of file diff --git a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.cpp b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.cpp index 90a9ca5460..13ea062617 100644 --- a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.cpp +++ b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.cpp @@ -23,65 +23,74 @@ #include "pxsch_bler_test_channel_emulator.h" #include "srsran/adt/span.h" #include "srsran/srsvec/add.h" +#include "srsran/srsvec/copy.h" #include "srsran/srsvec/dot_prod.h" #include "srsran/srsvec/prod.h" #include "srsran/srsvec/sc_prod.h" #include "srsran/srsvec/zero.h" #include #include +#include #include using namespace srsran; unsigned channel_emulator::concurrent_channel_emulator::seed = 0; +/// \brief TDP channel tap. +/// +/// The first element is the tap delay in nanoseconds, and the second element is the tap amplitude in dB. +using tdl_tap = std::pair; + /// Single-tap profile. -constexpr static std::array, 12> taps_single = {{{200, 0.0}}}; +constexpr static std::array taps_single = {{{0, 0.0}}}; /// TDLA fading profile. -constexpr static std::array, 12> taps_tdla = {{{0, -15.5}, - {10, 0.0}, - {15, -5.1}, - {20, -5.1}, - {25, -9.6}, - {50, -8.2}, - {65, -13.1}, - {75, -11.5}, - {105, -11.0}, - {135, -16.2}, - {150, -16.6}, - {290, -26.2}}}; +constexpr static std::array taps_tdla = {{{0, -15.5}, + {10, 0.0}, + {15, -5.1}, + {20, -5.1}, + {25, -9.6}, + {50, -8.2}, + {65, -13.1}, + {75, -11.5}, + {105, -11.0}, + {135, -16.2}, + {150, -16.6}, + {290, -26.2}}}; /// TDLB fading profile. -constexpr static std::array, 12> taps_tdlb = {{{0, 0}, - {10, -2.2}, - {20, -0.6}, - {30, -0.6}, - {35, -0.3}, - {45, -1.2}, - {55, -5.9}, - {120, -2.2}, - {170, -0.8}, - {245, -6.3}, - {330, -7.5}, - {480, -7.1}}}; +constexpr static std::array taps_tdlb = {{{0, 0}, + {10, -2.2}, + {20, -0.6}, + {30, -0.6}, + {35, -0.3}, + {45, -1.2}, + {55, -5.9}, + {120, -2.2}, + {170, -0.8}, + {245, -6.3}, + {330, -7.5}, + {480, -7.1}}}; /// TDLC fading profile. -constexpr static std::array, 12> taps_tdlc = {{{0, -6.9}, - {65, 0}, - {70, -7.7}, - {190, -2.5}, - {195, -2.4}, - {200, -9.9}, - {240, -8.0}, - {325, -6.6}, - {520, -7.1}, - {1045, -13.0}, - {1510, -14.2}, - {2595, -16.0}}}; - -channel_emulator::channel_emulator(std::string channel, +constexpr static std::array taps_tdlc = {{{0, -6.9}, + {65, 0}, + {70, -7.7}, + {190, -2.5}, + {195, -2.4}, + {200, -9.9}, + {240, -8.0}, + {325, -6.6}, + {520, -7.1}, + {1045, -13.0}, + {1510, -14.2}, + {2595, -16.0}}}; + +channel_emulator::channel_emulator(std::string delay_profile, + std::string fading_distribution_, float sinr_dB, + unsigned nof_corrupted_re_per_symbol, unsigned nof_rx_ports, unsigned nof_subc, unsigned nof_symbols, @@ -89,30 +98,36 @@ channel_emulator::channel_emulator(std::string channel, subcarrier_spacing scs, task_executor& executor_) : nof_ofdm_symbols(nof_symbols), - dist_taps(cf_t(), 1.0F), freq_domain_channel({nof_subc, nof_rx_ports}), temp_channel(nof_subc), - emulators(max_nof_threads, sinr_dB, nof_subc), + emulators(max_nof_threads, sinr_dB, nof_corrupted_re_per_symbol, nof_subc), executor(executor_) { // Select fading channel taps. - span> taps; - if (channel == "Single-tap") { + span taps; + if (delay_profile == "single-tap") { taps = taps_single; - } else if (channel == "TDLA") { + } else if (delay_profile == "TDLA") { taps = taps_tdla; - } else if (channel == "TDLB") { + } else if (delay_profile == "TDLB") { taps = taps_tdlb; - } else if (channel == "TDLC") { + } else if (delay_profile == "TDLC") { taps = taps_tdlc; } - report_fatal_error_if_not(!taps.empty(), "Invalid channel '{}'.", channel); + report_fatal_error_if_not(!taps.empty(), "Invalid delay profile '{}'.", delay_profile); + + if (fading_distribution_ == "rayleigh") { + fading_distribution = rayleigh; + } else if (fading_distribution_ == "uniform-phase") { + fading_distribution = uniform_phase; + } + report_fatal_error_if_not( + fading_distribution != invalid_distribution, "Invalid fading distribution '{}'.", fading_distribution_); // Estimate taps power. - float taps_power = - std::accumulate(taps.begin(), taps.end(), 0.0F, [](float acc, const std::pair& tap) { - return acc + convert_dB_to_power(tap.second); - }); + float taps_power = std::accumulate(taps.begin(), taps.end(), 0.0F, [](float acc, const tdl_tap& tap) { + return acc + convert_dB_to_power(tap.second); + }); // Calculate power normalization coefficient. float norm_coefficient = 1.0F / std::sqrt(nof_rx_ports * taps_power); @@ -152,8 +167,22 @@ void channel_emulator::run(resource_grid_writer& rx_grid, const resource_grid_re // Generate frequency domain response for the entire slot. srsvec::zero(chan_freq_respone); for (unsigned i_tap = 0; i_tap != nof_taps; ++i_tap) { - // Multiply tap frequency response by a random complex coefficient. - srsvec::sc_prod(taps_channel_response.get_view({i_tap}), dist_taps(rgen), temp_channel); + // Select tap from the fading distribution. + cf_t tap; + switch (fading_distribution) { + case rayleigh: + tap = dist_rayleigh(rgen); + break; + case uniform_phase: + tap = std::polar(1.0F, dist_uniform_phase(rgen)); + break; + case invalid_distribution: + tap = std::numeric_limits::quiet_NaN(); + break; + } + + // Multiply tap frequency response by a fading distribution tap. + srsvec::sc_prod(taps_channel_response.get_view({i_tap}), tap, temp_channel); // Accumulate tap frequency response. srsvec::add(chan_freq_respone, temp_channel, chan_freq_respone); @@ -179,9 +208,11 @@ void channel_emulator::run(resource_grid_writer& rx_grid, const resource_grid_re void channel_emulator::concurrent_channel_emulator::run(resource_grid_writer& rx_grid, const resource_grid_reader& tx_grid, span freq_response, - unsigned int i_port, - unsigned int i_symbol) + unsigned i_port, + unsigned i_symbol) { + using namespace std::complex_literals; + // Get OFDM symbol. tx_grid.get(temp_ofdm_symbol, 0, i_symbol, 0); @@ -192,6 +223,19 @@ void channel_emulator::concurrent_channel_emulator::run(resource_grid_writer& std::generate(temp_awgn.begin(), temp_awgn.end(), [this]() { return dist_awgn(rgen); }); srsvec::add(temp_ofdm_symbol, temp_awgn, temp_ofdm_symbol); + // Corrupt REs. + std::set corrupted_i_subc; + for (unsigned i = 0; i != nof_corrupted_re; ++i) { + // Select a subcarrier that has not been corruped before. + unsigned i_subc; + do { + i_subc = dist_corrupted_re(rgen); + } while (corrupted_i_subc.count(i_subc) != 0); + corrupted_i_subc.emplace(i_subc); + + temp_ofdm_symbol[i_subc] *= 1i; + } + // Write the OFDM symbol back to the grid. rx_grid.put(i_port, i_symbol, 0, temp_ofdm_symbol); } \ No newline at end of file diff --git a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.h b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.h index 67a78d26a4..de0a4d9919 100644 --- a/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.h +++ b/tests/integrationtests/phy/upper/channel_processors/pxsch_bler_test_channel_emulator.h @@ -37,9 +37,9 @@ namespace srsran { /// \brief Implements a frequency domain channel emulator. /// -/// This channel emulator works in frequency domain. It applies a fading channel model without doppler dispersion. The -/// fading profile is selected upon creation and the tap coefficients are randomly generated in a uncorrelated fashion -/// upon every call to run(). +/// This channel emulator works in the frequency domain. It applies a fading channel model without doppler dispersion. +/// The fading profile is selected upon creation and the tap coefficients are randomly generated in a uncorrelated +/// fashion upon every call to run(). /// /// The fading channel run() is not thread safe. It deterministically selects the tap coefficients. However, the noise /// addition is performed asynchronously, which can lead to different results across executions. @@ -50,16 +50,21 @@ class channel_emulator public: /// \brief Creates a channel emulator. /// - /// \param[in] channel Channel fading profile. The valid profiles are TDLA, TDLB and TDLC. - /// \param[in] sinr_dB Signal-to-Interference-plus-Noise Ratio. - /// \param[in] nof_rx_ports Number of receive ports. - /// \param[in] nof_subc Number of the resource grid subcarriers. - /// \param[in] nof_symbols Number of OFDM symbols per slot. - /// \param[in] max_nof_threads Maximum number of threads used by the asynchronous executor. - /// \param[in] scs Resource grid subcarrier spacing. - /// \param[in] executor_ Asynchronous task execution. - channel_emulator(std::string channel, + /// \param[in] delay_profile Delay profile - The valid profiles are TDLA, TDLB, TDLC, and single-tap. + /// \param[in] fading_distribution Fading distribution - The valid distributions are rayleigh and + /// uniform-phase. + /// \param[in] sinr_dB Signal-to-Interference-plus-Noise Ratio. + /// \param[in] nof_corrupted_re_per_symbol Number of corrupted RE per OFDM symbol. Set to zero for no corrupted RE. + /// \param[in] nof_rx_ports Number of receive ports. + /// \param[in] nof_subc Number of resource grid subcarriers. + /// \param[in] nof_symbols Number of OFDM symbols per slot. + /// \param[in] max_nof_threads Maximum number of threads used by the asynchronous executor. + /// \param[in] scs Resource grid subcarrier spacing. + /// \param[in] executor_ Asynchronous task execution. + channel_emulator(std::string delay_profile, + std::string fading_distribution, float sinr_dB, + unsigned nof_corrupted_re_per_symbol, unsigned nof_rx_ports, unsigned nof_subc, unsigned nof_symbols, @@ -78,9 +83,11 @@ class channel_emulator class concurrent_channel_emulator { public: - concurrent_channel_emulator(float sinr_dB, unsigned nof_subc) : + concurrent_channel_emulator(float sinr_dB, unsigned nof_corrupted_re_, unsigned nof_subc) : rgen(seed++), dist_awgn(cf_t(), convert_dB_to_amplitude(-sinr_dB)), + dist_corrupted_re(0, nof_subc - 1), + nof_corrupted_re(nof_corrupted_re_), temp_ofdm_symbol(nof_subc), temp_awgn(nof_subc) { @@ -105,18 +112,29 @@ class channel_emulator std::mt19937 rgen; /// Additive white gaussian noise distribution. complex_normal_distribution dist_awgn; + /// Distribution for simulating corrupted RE. + std::uniform_int_distribution dist_corrupted_re; + /// Number of corrupted RE per OFDM symbol. + unsigned nof_corrupted_re; /// Temporary OFDM frequency domain symbol. std::vector temp_ofdm_symbol; /// Temporary generated noise for adding to an OFDM symbol. std::vector temp_awgn; }; + enum { + invalid_distribution, + rayleigh, + uniform_phase, + } fading_distribution = invalid_distribution; /// Number of OFDM symbols per slot. unsigned nof_ofdm_symbols; /// Random generator for taps phase and power. std::mt19937 rgen; /// Random distribution for uncorrelated tap weights. - complex_normal_distribution dist_taps; + complex_normal_distribution dist_rayleigh; + /// Uniform real distribution for phase. + std::uniform_real_distribution dist_uniform_phase = std::uniform_real_distribution(-M_PI, M_PI); /// Temporary channel sum. dynamic_tensor<2, cf_t> freq_domain_channel; /// Temporary channel. diff --git a/tests/test_doubles/ngap/ngap_test_message_validators.cpp b/tests/test_doubles/ngap/ngap_test_message_validators.cpp index 9029f65705..5896990201 100644 --- a/tests/test_doubles/ngap/ngap_test_message_validators.cpp +++ b/tests/test_doubles/ngap/ngap_test_message_validators.cpp @@ -46,9 +46,23 @@ bool srsran::test_helpers::is_valid_initial_context_setup_response(const srs_cu_ return true; } +bool srsran::test_helpers::is_valid_initial_context_setup_failure(const srs_cu_cp::ngap_message& msg) +{ + TRUE_OR_RETURN(msg.pdu.type() == asn1::ngap::ngap_pdu_c::types_opts::unsuccessful_outcome); + TRUE_OR_RETURN(msg.pdu.unsuccessful_outcome().proc_code == ASN1_NGAP_ID_INIT_CONTEXT_SETUP); + return true; +} + bool srsran::test_helpers::is_valid_ue_context_release_request(const srs_cu_cp::ngap_message& msg) { TRUE_OR_RETURN(msg.pdu.type() == asn1::ngap::ngap_pdu_c::types_opts::init_msg); TRUE_OR_RETURN(msg.pdu.init_msg().proc_code == ASN1_NGAP_ID_UE_CONTEXT_RELEASE_REQUEST); return true; } + +bool srsran::test_helpers::is_valid_ue_radio_capability_info_indication(const srs_cu_cp::ngap_message& msg) +{ + TRUE_OR_RETURN(msg.pdu.type() == asn1::ngap::ngap_pdu_c::types_opts::init_msg); + TRUE_OR_RETURN(msg.pdu.init_msg().proc_code == ASN1_NGAP_ID_UE_RADIO_CAP_INFO_IND); + return true; +} \ No newline at end of file diff --git a/tests/test_doubles/ngap/ngap_test_message_validators.h b/tests/test_doubles/ngap/ngap_test_message_validators.h index b773047d40..6de1a49c16 100644 --- a/tests/test_doubles/ngap/ngap_test_message_validators.h +++ b/tests/test_doubles/ngap/ngap_test_message_validators.h @@ -36,7 +36,11 @@ bool is_valid_init_ue_message(const srs_cu_cp::ngap_message& msg); bool is_valid_initial_context_setup_response(const srs_cu_cp::ngap_message& msg); +bool is_valid_initial_context_setup_failure(const srs_cu_cp::ngap_message& msg); + bool is_valid_ue_context_release_request(const srs_cu_cp::ngap_message& msg); +bool is_valid_ue_radio_capability_info_indication(const srs_cu_cp::ngap_message& msg); + } // namespace test_helpers } // namespace srsran \ No newline at end of file diff --git a/tests/test_doubles/rrc/rrc_test_message_validators.cpp b/tests/test_doubles/rrc/rrc_test_message_validators.cpp index f5d4673e25..1afcaea974 100644 --- a/tests/test_doubles/rrc/rrc_test_message_validators.cpp +++ b/tests/test_doubles/rrc/rrc_test_message_validators.cpp @@ -79,3 +79,39 @@ bool srsran::test_helpers::is_valid_rrc_security_mode_command(const byte_buffer& TRUE_OR_RETURN(dcch.unpack(bref) == asn1::SRSASN_SUCCESS); return is_valid_rrc_security_mode_command(dcch); } + +bool srsran::test_helpers::is_valid_rrc_ue_capability_enquiry(const asn1::rrc_nr::dl_dcch_msg_s& msg) +{ + TRUE_OR_RETURN(msg.msg.type().value == asn1::rrc_nr::dl_dcch_msg_type_c::types_opts::c1); + TRUE_OR_RETURN(msg.msg.c1().type().value == asn1::rrc_nr::dl_dcch_msg_type_c::c1_c_::types_opts::ue_cap_enquiry); + TRUE_OR_RETURN(msg.msg.c1().ue_cap_enquiry().crit_exts.type().value == + asn1::rrc_nr::ue_cap_enquiry_s::crit_exts_c_::types_opts::ue_cap_enquiry); + return true; +} + +bool srsran::test_helpers::is_valid_rrc_ue_capability_enquiry(const byte_buffer& dl_dcch_msg) +{ + asn1::cbit_ref bref{dl_dcch_msg}; + asn1::rrc_nr::dl_dcch_msg_s dcch; + TRUE_OR_RETURN(dcch.unpack(bref) == asn1::SRSASN_SUCCESS); + return is_valid_rrc_ue_capability_enquiry(dcch); +} + +bool srsran::test_helpers::is_valid_rrc_reconfiguration(const asn1::rrc_nr::dl_dcch_msg_s& msg) +{ + TRUE_OR_RETURN(msg.msg.type().value == asn1::rrc_nr::dl_dcch_msg_type_c::types_opts::c1); + TRUE_OR_RETURN(msg.msg.c1().type().value == asn1::rrc_nr::dl_dcch_msg_type_c::c1_c_::types_opts::rrc_recfg); + TRUE_OR_RETURN(msg.msg.c1().rrc_recfg().crit_exts.type().value == + asn1::rrc_nr::rrc_recfg_s::crit_exts_c_::types_opts::rrc_recfg); + TRUE_OR_RETURN(msg.msg.c1().rrc_recfg().crit_exts.rrc_recfg().non_crit_ext_present); + TRUE_OR_RETURN(msg.msg.c1().rrc_recfg().crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list.size() != 0); + return true; +} + +bool srsran::test_helpers::is_valid_rrc_reconfiguration(const byte_buffer& dl_dcch_msg) +{ + asn1::cbit_ref bref{dl_dcch_msg}; + asn1::rrc_nr::dl_dcch_msg_s dcch; + TRUE_OR_RETURN(dcch.unpack(bref) == asn1::SRSASN_SUCCESS); + return is_valid_rrc_reconfiguration(dcch); +} \ No newline at end of file diff --git a/tests/test_doubles/rrc/rrc_test_message_validators.h b/tests/test_doubles/rrc/rrc_test_message_validators.h index 7ae9feac08..fd013f2cc6 100644 --- a/tests/test_doubles/rrc/rrc_test_message_validators.h +++ b/tests/test_doubles/rrc/rrc_test_message_validators.h @@ -40,5 +40,13 @@ bool is_valid_rrc_reestablishment(const byte_buffer& dl_dcch_msg); bool is_valid_rrc_security_mode_command(const asn1::rrc_nr::dl_dcch_msg_s& msg); bool is_valid_rrc_security_mode_command(const byte_buffer& dl_dcch_msg); +/// \brief Check if DL-DCCH message is a valid RRC UE Capability Enquiry message. +bool is_valid_rrc_ue_capability_enquiry(const asn1::rrc_nr::dl_dcch_msg_s& msg); +bool is_valid_rrc_ue_capability_enquiry(const byte_buffer& dl_dcch_msg); + +/// \brief Check if DL-DCCH message is a valid RRC Reconfiguration message. +bool is_valid_rrc_reconfiguration(const asn1::rrc_nr::dl_dcch_msg_s& msg); +bool is_valid_rrc_reconfiguration(const byte_buffer& dl_dcch_msg); + } // namespace test_helpers } // namespace srsran \ No newline at end of file diff --git a/tests/unittests/cu_cp/CMakeLists.txt b/tests/unittests/cu_cp/CMakeLists.txt index ac32e0fc3d..ab1789cb5c 100644 --- a/tests/unittests/cu_cp/CMakeLists.txt +++ b/tests/unittests/cu_cp/CMakeLists.txt @@ -44,7 +44,7 @@ target_include_directories(cu_cp_test_helpers PRIVATE ${CMAKE_SOURCE_DIR}) target_link_libraries(cu_cp_test_helpers srsran_cu_cp srsran_support srslog f1ap_test_helpers e1ap_test_helpers f1ap_asn1 ngap_asn1 e1ap_asn1) -add_executable(cu_cp_test cu_cp_test.cpp cu_cp_connectivity_test.cpp cu_cp_setup_test.cpp cu_cp_reestablishment_test.cpp) +add_executable(cu_cp_test cu_cp_test.cpp cu_cp_connectivity_test.cpp cu_cp_setup_test.cpp cu_cp_reestablishment_test.cpp cu_cp_initial_context_setup_test.cpp) set_target_properties(cu_cp_test PROPERTIES UNITY_BUILD ON) target_link_libraries(cu_cp_test cu_cp_test_helpers diff --git a/tests/unittests/cu_cp/cu_cp_initial_context_setup_test.cpp b/tests/unittests/cu_cp/cu_cp_initial_context_setup_test.cpp new file mode 100644 index 0000000000..b9c45f6f05 --- /dev/null +++ b/tests/unittests/cu_cp/cu_cp_initial_context_setup_test.cpp @@ -0,0 +1,339 @@ +/* + * + * 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_cp_test_environment.h" +#include "tests/test_doubles/f1ap/f1ap_test_message_validators.h" +#include "tests/test_doubles/f1ap/f1ap_test_messages.h" +#include "tests/test_doubles/ngap/ngap_test_message_validators.h" +#include "tests/test_doubles/rrc/rrc_test_message_validators.h" +#include "tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.h" +#include "tests/unittests/f1ap/common/f1ap_cu_test_messages.h" +#include "tests/unittests/ngap/ngap_test_messages.h" +#include "srsran/e1ap/common/e1ap_types.h" +#include "srsran/f1ap/common/f1ap_message.h" +#include "srsran/ngap/ngap_message.h" +#include + +using namespace srsran; +using namespace srs_cu_cp; + +class cu_cp_initial_context_setup_test : public cu_cp_test_environment, public ::testing::Test +{ +public: + cu_cp_initial_context_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(); + + // Setup DU. + std::optional ret = connect_new_du(); + EXPECT_TRUE(ret.has_value()); + du_idx = ret.value(); + EXPECT_TRUE(this->run_f1_setup(du_idx)); + + // Setup CU-UP. + ret = connect_new_cu_up(); + EXPECT_TRUE(ret.has_value()); + cu_up_idx = ret.value(); + EXPECT_TRUE(this->run_e1_setup(cu_up_idx)); + + // Connect UE 0x4601. + EXPECT_TRUE(connect_new_ue(du_idx, du_ue_id, crnti)); + EXPECT_TRUE(authenticate_ue(du_idx, du_ue_id, amf_ue_id_t::min)); + + ue_ctx = this->find_ue_context(du_idx, du_ue_id); + + EXPECT_NE(ue_ctx, nullptr); + } + + void send_initial_context_setup_request(bool with_pdu_sessions = false) + { + srsran_assert(not this->get_amf().try_pop_rx_pdu(ngap_pdu), "there are still NGAP messages to pop from AMF"); + srsran_assert(not this->get_du(du_idx).try_pop_dl_pdu(f1ap_pdu), "there are still F1AP DL messages to pop from DU"); + + // Inject NGAP Initial Context Setup Request + ngap_message init_ctxt_setup_req; + if (with_pdu_sessions) { + init_ctxt_setup_req = generate_valid_initial_context_setup_request_message_with_pdu_session( + ue_ctx->amf_ue_id.value(), ue_ctx->ran_ue_id.value()); + } else { + init_ctxt_setup_req = + generate_valid_initial_context_setup_request_message(ue_ctx->amf_ue_id.value(), ue_ctx->ran_ue_id.value()); + } + + get_amf().push_tx_pdu(init_ctxt_setup_req); + } + + void send_ue_context_setup_request_and_await_response() + { + // Wait for F1AP UE Context Setup Request (containing Security Mode Command). + bool result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); + report_fatal_error_if_not(result, "Failed to receive Security Mode Command"); + report_fatal_error_if_not(test_helpers::is_valid_ue_context_setup_request(f1ap_pdu), + "Invalid UE Context Setup Request"); + const byte_buffer& rrc_container = test_helpers::get_rrc_container(f1ap_pdu); + report_fatal_error_if_not( + test_helpers::is_valid_rrc_security_mode_command(test_helpers::extract_dl_dcch_msg(rrc_container)), + "Invalid Security Mode Command"); + + // Inject UE Context Setup Response + f1ap_message ue_ctxt_setup_response = generate_ue_context_setup_response(ue_ctx->cu_ue_id.value(), du_ue_id); + get_du(du_idx).push_ul_pdu(ue_ctxt_setup_response); + } + + void send_security_mode_complete_and_await_ue_capability_enquiry() + { + // Inject Security Mode Complete + f1ap_message ul_rrc_msg_transfer = generate_ul_rrc_message_transfer( + ue_ctx->cu_ue_id.value(), du_ue_id, srb_id_t::srb1, make_byte_buffer("00032a00fd5ec7ff").value()); + get_du(du_idx).push_ul_pdu(ul_rrc_msg_transfer); + + // Wait for UE Capability Enquiry + bool result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); + report_fatal_error_if_not(result, "Failed to receive UE Capability Enquiry"); + report_fatal_error_if_not(test_helpers::is_valid_dl_rrc_message_transfer(f1ap_pdu), + "Invalid DL RRC Message Transfer"); + const byte_buffer& rrc_container = test_helpers::get_rrc_container(f1ap_pdu); + report_fatal_error_if_not( + test_helpers::is_valid_rrc_ue_capability_enquiry(test_helpers::extract_dl_dcch_msg(rrc_container)), + "Invalid UE Capability Enquiry"); + } + + void send_ue_capability_info_and_await_registration_accept_and_initial_context_setup_response() + { + // Inject UL RRC Message Transfer (containing UE Capability Info) + get_du(du_idx).push_ul_pdu(test_helpers::create_ul_rrc_message_transfer( + du_ue_id, + ue_ctx->cu_ue_id.value(), + srb_id_t::srb1, + make_byte_buffer( + "00044c821930680ce811d1968097e360e1480005824c5c00060fc2c00637fe002e00131401a0000000880058d006007" + "a071e439f0000240400e0300000000100186c0000700809df000000000000030368000800004b2ca000a07143c001c0" + "03c000000100200409028098a8660c") + .value())); + + // Wait for DL RRC Message Transfer (containing NAS Registration Accept) + bool result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); + report_fatal_error_if_not(result, "Failed to receive DL RRC Message, containing NAS Registration Accept"); + report_fatal_error_if_not(test_helpers::is_valid_dl_rrc_message_transfer(f1ap_pdu), + "Invalid DL RRC Message Transfer"); + + // Wait for Initial Context Setup Response + result = this->wait_for_ngap_tx_pdu(ngap_pdu); + report_fatal_error_if_not(result, "Failed to receive Initial Context Setup Response"); + report_fatal_error_if_not(test_helpers::is_valid_initial_context_setup_response(ngap_pdu), + "Invalid init ctxt setup"); + } + + void send_ue_capability_info_and_handle_pdu_session_resource_setup_request() + { + // Inject UL RRC Message Transfer (containing UE Capability Info) + get_du(du_idx).push_ul_pdu(test_helpers::create_ul_rrc_message_transfer( + du_ue_id, + ue_ctx->cu_ue_id.value(), + srb_id_t::srb1, + make_byte_buffer( + "00044c821930680ce811d1968097e360e1480005824c5c00060fc2c00637fe002e00131401a0000000880058d006007" + "a071e439f0000240400e0300000000100186c0000700809df000000000000030368000800004b2ca000a07143c001c0" + "03c000000100200409028098a8660c") + .value())); + + // Wait for E1AP Bearer Context Setup Request + bool result = this->wait_for_e1ap_tx_pdu(0, e1ap_pdu); + report_fatal_error_if_not(result, "Failed to receive E1AP Bearer Context Setup Request"); + + cu_cp_e1ap_id = + int_to_gnb_cu_cp_ue_e1ap_id(e1ap_pdu.pdu.init_msg().value.bearer_context_setup_request()->gnb_cu_cp_ue_e1ap_id); + cu_up_e1ap_id = int_to_gnb_cu_up_ue_e1ap_id(0); + + // Inject Bearer Context Setup Response and wait for F1AP UE Context Modification Request. + get_cu_up(0).push_tx_pdu(generate_bearer_context_setup_response(cu_cp_e1ap_id, cu_up_e1ap_id)); + result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); + report_fatal_error_if_not(result, "Failed to receive F1AP UE Context Modification Request"); + report_fatal_error_if_not(test_helpers::is_valid_ue_context_modification_request(f1ap_pdu), + "Invalid UE Context Modification"); + + // Inject UE Context Modification Response and wait for Bearer Context Modification to be sent to CU-UP. + get_du(du_idx).push_ul_pdu( + test_helpers::generate_ue_context_modification_response(du_ue_id, ue_ctx->cu_ue_id.value(), crnti)); + result = this->wait_for_e1ap_tx_pdu(0, e1ap_pdu); + report_fatal_error_if_not(result, "Failed to receive E1AP Bearer Context Modification"); + + // Inject E1AP Bearer Context Modification Response and wait for DL RRC Message (containing RRC Reconfiguration) + get_cu_up(0).push_tx_pdu(generate_bearer_context_modification_response(cu_cp_e1ap_id, cu_up_e1ap_id)); + result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); + report_fatal_error_if_not(result, "Failed to receive F1AP DL RRC Message (containing RRC Reconfiguration)"); + report_fatal_error_if_not(test_helpers::is_valid_dl_rrc_message_transfer(f1ap_pdu), + "Invalid DL RRC Message Transfer"); + + // Make sure RRC Reconfiguration contains NAS PDU + const byte_buffer& rrc_container = test_helpers::get_rrc_container(f1ap_pdu); + report_fatal_error_if_not( + test_helpers::is_valid_rrc_reconfiguration(test_helpers::extract_dl_dcch_msg(rrc_container)), + "Invalid RRC Reconfiguration"); + } + + void send_rrc_reconfiguration_complete_and_await_initial_context_setup_response() + { + // Inject UL RRC Message Transfer (containing RRC Reconfiguration Complete) + f1ap_message ul_rrc_msg_transfer = generate_ul_rrc_message_transfer( + ue_ctx->cu_ue_id.value(), du_ue_id, srb_id_t::srb1, make_byte_buffer("00050e00a18bc2b3").value()); + get_du(du_idx).push_ul_pdu(ul_rrc_msg_transfer); + + // Wait for Initial Context Setup Response + bool result = this->wait_for_ngap_tx_pdu(ngap_pdu); + report_fatal_error_if_not(result, "Failed to receive Initial Context Setup Response"); + report_fatal_error_if_not(test_helpers::is_valid_initial_context_setup_response(ngap_pdu), + "Invalid init ctxt setup"); + } + + void await_initial_context_setup_failure() + { + // Wait for NGAP Initial Context Setup Failure + bool result = this->wait_for_ngap_tx_pdu(ngap_pdu); + report_fatal_error_if_not(result, "Failed to receive Initial Context Setup Failure"); + report_fatal_error_if_not(test_helpers::is_valid_initial_context_setup_failure(ngap_pdu), + "Invalid Initial Context Setup Failure"); + } + + void await_ue_capability_info_indication() + { + // Wait for UE Capability Info Indication + bool result = this->wait_for_ngap_tx_pdu(ngap_pdu); + report_fatal_error_if_not(result, "Failed to receive UE Radio Capability Info Indication"); + report_fatal_error_if_not(test_helpers::is_valid_ue_radio_capability_info_indication(ngap_pdu), + "Invalid UE Radio Capability Info Indication"); + } + + void await_ue_context_release_request() + { + // Wait for UE Context Release Request + bool result = this->wait_for_ngap_tx_pdu(ngap_pdu); + report_fatal_error_if_not(result, "Failed to receive UE Context Release Request"); + report_fatal_error_if_not(test_helpers::is_valid_ue_context_release_request(ngap_pdu), + "Invalid UE Context Release Request"); + } + + unsigned du_idx = 0; + unsigned cu_up_idx = 0; + + gnb_du_ue_f1ap_id_t du_ue_id = int_to_gnb_du_ue_f1ap_id(0); + rnti_t crnti = to_rnti(0x4601); + gnb_cu_cp_ue_e1ap_id_t cu_cp_e1ap_id; + gnb_cu_up_ue_e1ap_id_t cu_up_e1ap_id; + + const ue_context* ue_ctx = nullptr; + + ngap_message ngap_pdu; + f1ap_message f1ap_pdu; + e1ap_message e1ap_pdu; +}; + +TEST_F(cu_cp_initial_context_setup_test, when_ue_context_setup_fails_then_initial_context_setup_fails) +{ + // Inject Initial Context Setup Request + send_initial_context_setup_request(); + + // Inject UE Context Setup Failure + f1ap_message ue_ctxt_setup_failure = + generate_ue_context_setup_failure(find_ue_context(du_idx, du_ue_id)->cu_ue_id.value(), du_ue_id); + get_du(du_idx).push_ul_pdu(ue_ctxt_setup_failure); + + // Wait for NGAP Initial Context Setup Failure + await_initial_context_setup_failure(); +} + +TEST_F(cu_cp_initial_context_setup_test, when_security_mode_command_fails_then_initial_context_setup_fails) +{ + // Inject Initial Context Setup Request + send_initial_context_setup_request(); + + // Wait for F1AP UE Context Setup Request (containing Security Mode Command) and inject UE Context Setup Response + send_ue_context_setup_request_and_await_response(); + + // Inject Security Mode Failure + f1ap_message ul_rrc_msg_transfer = generate_ul_rrc_message_transfer( + ue_ctx->cu_ue_id.value(), du_ue_id, srb_id_t::srb1, make_byte_buffer("00033200c1bf019d").value()); + get_du(du_idx).push_ul_pdu(ul_rrc_msg_transfer); + + // Wait for NGAP Initial Context Setup Failure + await_initial_context_setup_failure(); +} + +TEST_F(cu_cp_initial_context_setup_test, when_ue_capability_enquiry_fails_then_initial_context_setup_fails) +{ + // Inject Initial Context Setup Request + send_initial_context_setup_request(); + + // Wait for F1AP UE Context Setup Request (containing Security Mode Command) and inject UE Context Setup Response + send_ue_context_setup_request_and_await_response(); + + // Inject Security Mode Complete and await UE Capability Enquiry + send_security_mode_complete_and_await_ue_capability_enquiry(); + + // Fail UE Capability Enquiry (UE doesn't respond) + ASSERT_FALSE(tick_until(std::chrono::milliseconds(this->get_cu_cp_cfg().rrc_config.rrc_procedure_timeout_ms), + [&]() { return false; })); + + // Wait for NGAP Initial Context Setup Failure + await_initial_context_setup_failure(); +} + +TEST_F(cu_cp_initial_context_setup_test, when_ue_capability_enquiry_successful_then_initial_context_setup_succeeds) +{ + // Inject Initial Context Setup Request + send_initial_context_setup_request(); + + // Wait for F1AP UE Context Setup Request (containing Security Mode Command) and inject UE Context Setup Response + send_ue_context_setup_request_and_await_response(); + + // Inject Security Mode Complete and await UE Capability Enquiry + send_security_mode_complete_and_await_ue_capability_enquiry(); + + // Inject UE Capability Info and await DL RRC Message (Registration Accept) and Initial Context Setup Response + send_ue_capability_info_and_await_registration_accept_and_initial_context_setup_response(); + + // Wait for UE Capability Info Indication + await_ue_capability_info_indication(); +} + +TEST_F(cu_cp_initial_context_setup_test, + when_initial_context_setup_contains_valid_pdu_sessions_to_setup_then_initial_context_setup_succeeds) +{ + // Inject Initial Context Setup Request + send_initial_context_setup_request(true); + + // Wait for F1AP UE Context Setup Request (containing Security Mode Command) and inject UE Context Setup Response + send_ue_context_setup_request_and_await_response(); + + // Inject Security Mode Complete and await UE Capability Enquiry + send_security_mode_complete_and_await_ue_capability_enquiry(); + + // Inject UE Capability Info and handle PDU Session Resource Setup List Context Request + send_ue_capability_info_and_handle_pdu_session_resource_setup_request(); + + // Inject RRC Reconfiguration Complete and await Initial Context Setup Response + send_rrc_reconfiguration_complete_and_await_initial_context_setup_response(); + + // Wait for UE Capability Info Indication + await_ue_capability_info_indication(); +} \ No newline at end of file diff --git a/tests/unittests/cu_cp/cu_cp_test_environment.cpp b/tests/unittests/cu_cp/cu_cp_test_environment.cpp index f916bedad2..8732294b8b 100644 --- a/tests/unittests/cu_cp/cu_cp_test_environment.cpp +++ b/tests/unittests/cu_cp/cu_cp_test_environment.cpp @@ -37,6 +37,7 @@ #include "srsran/cu_cp/cu_cp_factory.h" #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/e1ap/common/e1ap_message.h" +#include "srsran/e1ap/common/e1ap_types.h" #include "srsran/f1ap/common/f1ap_message.h" #include "srsran/ngap/ngap_message.h" #include "srsran/support/executors/task_worker.h" @@ -127,7 +128,7 @@ bool cu_cp_test_environment::tick_until(std::chrono::milliseconds timeout, const return true; } - // Push to CU-CP worker task taht checks the state of the condition. + // Push to CU-CP worker task that checks the state of the condition. done = false; cu_cp_workers->worker.push_task_blocking([&]() { // Need to tick the clock. @@ -323,7 +324,7 @@ bool cu_cp_test_environment::authenticate_ue(unsigned du_idx, gnb_du_ue_f1ap_id_ // Inject NGAP DL message (authentication request) ngap_message dl_nas_transport = - srs_cu_cp::generate_downlink_nas_transport_message(*ue_ctx.amf_ue_id, *ue_ctx.ran_ue_id); + srs_cu_cp::generate_downlink_nas_transport_message(ue_ctx.amf_ue_id.value(), ue_ctx.ran_ue_id.value()); get_amf().push_tx_pdu(dl_nas_transport); // Wait for DL RRC message transfer (containing NAS message) @@ -336,7 +337,7 @@ bool cu_cp_test_environment::authenticate_ue(unsigned du_idx, gnb_du_ue_f1ap_id_ // Inject UL RRC msg transfer (authentication response) f1ap_message ul_rrc_msg_transfer = generate_ul_rrc_message_transfer( - *ue_ctx.cu_ue_id, + ue_ctx.cu_ue_id.value(), du_ue_id, srb_id_t::srb1, make_byte_buffer("00013a0abf002b96882dac46355c4f34464ddaf7b43fde37ae8000000000").value()); @@ -349,7 +350,7 @@ bool cu_cp_test_environment::authenticate_ue(unsigned du_idx, gnb_du_ue_f1ap_id_ } // Inject DL NAS Transport message (ue security mode command) - dl_nas_transport = generate_downlink_nas_transport_message(amf_ue_id, *ue_ctx.ran_ue_id); + dl_nas_transport = generate_downlink_nas_transport_message(amf_ue_id, ue_ctx.ran_ue_id.value()); get_amf().push_tx_pdu(dl_nas_transport); result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); @@ -361,7 +362,7 @@ bool cu_cp_test_environment::authenticate_ue(unsigned du_idx, gnb_du_ue_f1ap_id_ // Inject UL RRC msg transfer (ue security mode complete) ul_rrc_msg_transfer = generate_ul_rrc_message_transfer( - *ue_ctx.cu_ue_id, + ue_ctx.cu_ue_id.value(), du_ue_id, srb_id_t::srb1, make_byte_buffer("00023a1cbf0243241cb5003f002f3b80048290a1b283800000f8b880103f0020bc800680807888787f800008192a3b4" @@ -397,10 +398,12 @@ bool cu_cp_test_environment::setup_ue_security(unsigned du_idx, gnb_du_ue_f1ap_i report_fatal_error_if_not(result, "Failed to receive Security Mode Command"); report_fatal_error_if_not(test_helpers::is_valid_ue_context_setup_request(f1ap_pdu), "Invalid UE Context Setup Request"); - const byte_buffer& rrc_container = test_helpers::get_rrc_container(f1ap_pdu); - report_fatal_error_if_not( - test_helpers::is_valid_rrc_security_mode_command(test_helpers::extract_dl_dcch_msg(rrc_container)), - "Invalid Security Mode command"); + { + const byte_buffer& rrc_container = test_helpers::get_rrc_container(f1ap_pdu); + report_fatal_error_if_not( + test_helpers::is_valid_rrc_security_mode_command(test_helpers::extract_dl_dcch_msg(rrc_container)), + "Invalid Security Mode command"); + } // Inject UE Context Setup Response f1ap_message ue_ctxt_setup_response = generate_ue_context_setup_response(ue_ctx.cu_ue_id.value(), du_ue_id); @@ -411,23 +414,45 @@ bool cu_cp_test_environment::setup_ue_security(unsigned du_idx, gnb_du_ue_f1ap_i ue_ctx.cu_ue_id.value(), du_ue_id, srb_id_t::srb1, make_byte_buffer("00032a00fd5ec7ff").value()); get_du(du_idx).push_ul_pdu(ul_rrc_msg_transfer); - // Wait for DL RRC Message Transfer (containing RRC Reconfiguration, containing NAS Registration Accept) + // Wait for UE Capability Enquiry result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); - report_fatal_error_if_not( - result, "Failed to receive DL RRC Message, containing RRC Reconfiguration, containing NAS Registration Accept"); + report_fatal_error_if_not(result, "Failed to receive DL RRC Message, containing RRC UE Capability Enquiry"); report_fatal_error_if_not(test_helpers::is_valid_dl_rrc_message_transfer(f1ap_pdu), "Invalid DL RRC Message Transfer"); + { + const byte_buffer& rrc_container = test_helpers::get_rrc_container(f1ap_pdu); + report_fatal_error_if_not( + test_helpers::is_valid_rrc_ue_capability_enquiry(test_helpers::extract_dl_dcch_msg(rrc_container)), + "Invalid UE Capability Enquiry"); + } - // Inject UL RRC Message Transfer (containing RRC Reconfiguration Complete) - ul_rrc_msg_transfer = generate_ul_rrc_message_transfer( - ue_ctx.cu_ue_id.value(), du_ue_id, srb_id_t::srb1, make_byte_buffer("00040c00fbca0d80").value()); - get_du(du_idx).push_ul_pdu(ul_rrc_msg_transfer); + // Inject UL RRC Message Transfer (containing UE Capability Info) + get_du(du_idx).push_ul_pdu(test_helpers::create_ul_rrc_message_transfer( + du_ue_id, + ue_ctx.cu_ue_id.value(), + srb_id_t::srb1, + make_byte_buffer("00044c821930680ce811d1968097e360e1480005824c5c00060fc2c00637fe002e00131401a0000000880058d006007" + "a071e439f0000240400e0300000000100186c0000700809df000000000000030368000800004b2ca000a07143c001c0" + "03c000000100200409028098a8660c") + .value())); + + // Wait for DL RRC Message Transfer (containing NAS Registration Accept) + result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); + report_fatal_error_if_not(result, "Failed to receive DL RRC Message, containing NAS Registration Accept"); + report_fatal_error_if_not(test_helpers::is_valid_dl_rrc_message_transfer(f1ap_pdu), + "Invalid DL RRC Message Transfer"); // Wait for Initial Context Setup Response. result = this->wait_for_ngap_tx_pdu(ngap_pdu); report_fatal_error_if_not(result, "Failed to receive Initial Context Setup Response"); report_fatal_error_if_not(test_helpers::is_valid_initial_context_setup_response(ngap_pdu), "Invalid init ctxt setup"); + // Wait for UE Radio Capability Info Indication. + result = this->wait_for_ngap_tx_pdu(ngap_pdu); + report_fatal_error_if_not(result, "Failed to receive UE Radio Capability Info Indication"); + report_fatal_error_if_not(test_helpers::is_valid_ue_radio_capability_info_indication(ngap_pdu), + "Invalid UE Radio Capability Info Indication"); + return true; } @@ -455,15 +480,18 @@ bool cu_cp_test_environment::attach_ue(unsigned du_idx, srsran_assert(not this->get_cu_up(0).try_pop_rx_pdu(e1ap_pdu), "there are still E1AP messages to pop from CU-UP"); // Inject Registration Complete and wait UL NAS message. - get_du(du_idx).push_ul_pdu(test_helpers::create_ul_rrc_message_transfer( - du_ue_id, *ue_ctx.cu_ue_id, srb_id_t::srb1, make_byte_buffer("00053a053f015362c51680bf00218086b09a5b").value())); + get_du(du_idx).push_ul_pdu( + test_helpers::create_ul_rrc_message_transfer(du_ue_id, + ue_ctx.cu_ue_id.value(), + srb_id_t::srb1, + make_byte_buffer("00053a053f015362c51680bf00218086b09a5b").value())); bool result = this->wait_for_ngap_tx_pdu(ngap_pdu); report_fatal_error_if_not(result, "Failed to receive Registration Complete"); // Inject PDU Session Establishment Request and wait UL NAS message. get_du(du_idx).push_ul_pdu(test_helpers::create_ul_rrc_message_transfer( du_ue_id, - *ue_ctx.cu_ue_id, + ue_ctx.cu_ue_id.value(), srb_id_t::srb1, make_byte_buffer("00063a253f011ffa9203013f0033808018970080e0ffffc9d8bd8013404010880080000840830000000041830000000" "00000800001800005000006000006800008800900c092838339b939b0b83700e03a21bb") @@ -474,7 +502,7 @@ bool cu_cp_test_environment::attach_ue(unsigned du_idx, // Inject Configuration Update Command ngap_message dl_nas_transport_msg = generate_downlink_nas_transport_message( amf_ue_id, - *ue_ctx.ran_ue_id, + ue_ctx.ran_ue_id.value(), make_byte_buffer("7e0205545bfc027e0054430f90004f00700065006e00350047005346004732800131235200490100").value()); get_amf().push_tx_pdu(dl_nas_transport_msg); result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); @@ -482,30 +510,19 @@ bool cu_cp_test_environment::attach_ue(unsigned du_idx, report_fatal_error_if_not(test_helpers::is_valid_dl_rrc_message_transfer(f1ap_pdu), "Invalid DL RRC Message Transfer"); - // Inject PDU Session Resource Setup Request and wait for F1AP DL RRC Message (containing ueCapabilityEnquiry). + // Inject PDU Session Resource Setup Request and wait for E1AP Bearer Context Setup Request. ngap_message pdu_session_resource_setup_request = generate_valid_pdu_session_resource_setup_request_message( - amf_ue_id, *ue_ctx.ran_ue_id, uint_to_pdu_session_id(1)); + amf_ue_id, ue_ctx.ran_ue_id.value(), uint_to_pdu_session_id(1)); get_amf().push_tx_pdu(pdu_session_resource_setup_request); - result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); - report_fatal_error_if_not(result, "Failed to receive DL RRC Message with RRC ueCapabilityEnquiry"); - report_fatal_error_if_not(test_helpers::is_valid_dl_rrc_message_transfer(f1ap_pdu), - "Invalid DL RRC Message Transfer"); - - // Inject UE capability info and wait for E1AP Bearer Context Setup. - get_du(du_idx).push_ul_pdu(test_helpers::create_ul_rrc_message_transfer( - du_ue_id, - *ue_ctx.cu_ue_id, - srb_id_t::srb1, - make_byte_buffer("00074e821930680ce811d1968097e360e1480005824c5c00060fc2c00637fe002e00131401a0000000880058d006007" - "a071e439f0000240400e0300000000100186c0000700809df000000000000030368000800004b2ca000a07143c001c0" - "03c00000010020040902807b0dba95") - .value())); result = this->wait_for_e1ap_tx_pdu(0, e1ap_pdu); - report_fatal_error_if_not(result, "Failed to receive E1AP Bearer Context Setup"); + report_fatal_error_if_not(result, "Failed to receive E1AP Bearer Context Setup Request"); + + gnb_cu_cp_ue_e1ap_id_t cu_cp_e1ap_id = + int_to_gnb_cu_cp_ue_e1ap_id(e1ap_pdu.pdu.init_msg().value.bearer_context_setup_request()->gnb_cu_cp_ue_e1ap_id); + gnb_cu_up_ue_e1ap_id_t cu_up_e1ap_id = int_to_gnb_cu_up_ue_e1ap_id(0); // Inject Bearer Context Setup Response and wait for F1AP UE Context Modification Request. - get_cu_up(0).push_tx_pdu( - generate_bearer_context_setup_response(int_to_gnb_cu_cp_ue_e1ap_id(0), int_to_gnb_cu_up_ue_e1ap_id(0))); + get_cu_up(0).push_tx_pdu(generate_bearer_context_setup_response(cu_cp_e1ap_id, cu_up_e1ap_id)); result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); report_fatal_error_if_not(result, "Failed to receive F1AP UE Context Modification Request"); report_fatal_error_if_not(test_helpers::is_valid_ue_context_modification_request(f1ap_pdu), @@ -513,21 +530,26 @@ bool cu_cp_test_environment::attach_ue(unsigned du_idx, // Inject UE Context Modification Response and wait for Bearer Context Modification to be sent to CU-UP. get_du(du_idx).push_ul_pdu( - test_helpers::generate_ue_context_modification_response(du_ue_id, *ue_ctx.cu_ue_id, crnti)); + test_helpers::generate_ue_context_modification_response(du_ue_id, ue_ctx.cu_ue_id.value(), crnti)); result = this->wait_for_e1ap_tx_pdu(0, e1ap_pdu); report_fatal_error_if_not(result, "Failed to receive E1AP Bearer Context Modification"); // Inject E1AP Bearer Context Modification Response and wait for DL RRC Message (containing RRC Reconfiguration) - get_cu_up(0).push_tx_pdu( - generate_bearer_context_modification_response(int_to_gnb_cu_cp_ue_e1ap_id(0), int_to_gnb_cu_up_ue_e1ap_id(0))); + get_cu_up(0).push_tx_pdu(generate_bearer_context_modification_response(cu_cp_e1ap_id, cu_up_e1ap_id)); result = this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu); report_fatal_error_if_not(result, "Failed to receive F1AP DL RRC Message (containing RRC Reconfiguration)"); report_fatal_error_if_not(test_helpers::is_valid_dl_rrc_message_transfer(f1ap_pdu), "Invalid DL RRC Message Transfer"); + { + const byte_buffer& rrc_container = test_helpers::get_rrc_container(f1ap_pdu); + report_fatal_error_if_not( + test_helpers::is_valid_rrc_reconfiguration(test_helpers::extract_dl_dcch_msg(rrc_container)), + "Invalid RRC Reconfiguration"); + } // Inject RRC Reconfiguration Complete and wait for PDU Session Resource Setup Response to be sent to AMF. get_du(du_idx).push_ul_pdu(test_helpers::create_ul_rrc_message_transfer( - du_ue_id, *ue_ctx.cu_ue_id, srb_id_t::srb1, make_byte_buffer("00080800e6847bbd").value())); + du_ue_id, ue_ctx.cu_ue_id.value(), srb_id_t::srb1, make_byte_buffer("00070e00cc6fcda5").value())); result = this->wait_for_ngap_tx_pdu(ngap_pdu); report_fatal_error_if_not(result, "Failed to receive PDU Session Resource Setup Response"); @@ -612,9 +634,13 @@ bool cu_cp_test_environment::reestablish_ue(unsigned du_idx, e1ap_message e1ap_pdu; report_fatal_error_if_not(this->wait_for_e1ap_tx_pdu(0, e1ap_pdu), "E1AP BearerContextModificationRequest NOT sent"); + gnb_cu_cp_ue_e1ap_id_t cu_cp_e1ap_id = + int_to_gnb_cu_cp_ue_e1ap_id(e1ap_pdu.pdu.init_msg().value.bearer_context_mod_request()->gnb_cu_cp_ue_e1ap_id); + gnb_cu_up_ue_e1ap_id_t cu_up_e1ap_id = + int_to_gnb_cu_up_ue_e1ap_id(e1ap_pdu.pdu.init_msg().value.bearer_context_mod_request()->gnb_cu_up_ue_e1ap_id); + // EVENT: Inject E1AP Bearer Context Modification Response - get_cu_up(0).push_tx_pdu( - generate_bearer_context_modification_response(int_to_gnb_cu_cp_ue_e1ap_id(0), int_to_gnb_cu_up_ue_e1ap_id(0))); + get_cu_up(0).push_tx_pdu(generate_bearer_context_modification_response(cu_cp_e1ap_id, cu_up_e1ap_id)); // STATUS: CU-CP sends F1AP UE Context Modification Request to DU. report_fatal_error_if_not(this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu), "F1AP UEContextModificationRequest NOT sent"); @@ -627,8 +653,7 @@ bool cu_cp_test_environment::reestablish_ue(unsigned du_idx, report_fatal_error_if_not(this->wait_for_e1ap_tx_pdu(0, e1ap_pdu), "E1AP BearerContextModificationRequest NOT sent"); // EVENT: CU-UP sends E1AP Bearer Context Modification Response - get_cu_up(0).push_tx_pdu( - generate_bearer_context_modification_response(int_to_gnb_cu_cp_ue_e1ap_id(0), int_to_gnb_cu_up_ue_e1ap_id(0))); + get_cu_up(0).push_tx_pdu(generate_bearer_context_modification_response(cu_cp_e1ap_id, cu_up_e1ap_id)); // STATUS: CU-CP sends F1AP DL RRC Message Transfer (containing RRC Reconfiguration). report_fatal_error_if_not(this->wait_for_f1ap_tx_pdu(du_idx, f1ap_pdu), "F1AP DL RRC Message NOT sent"); diff --git a/tests/unittests/cu_cp/cu_cp_test_helpers.cpp b/tests/unittests/cu_cp/cu_cp_test_helpers.cpp index ae428bb7b2..308a2b434c 100644 --- a/tests/unittests/cu_cp/cu_cp_test_helpers.cpp +++ b/tests/unittests/cu_cp/cu_cp_test_helpers.cpp @@ -280,9 +280,15 @@ void cu_cp_test::setup_security(amf_ue_id_t amf_ue_id, cu_ue_id, du_ue_id, srb_id_t::srb1, make_byte_buffer("00032a00fd5ec7ff").value()); f1c_gw.get_du(du_index).on_new_message(ul_rrc_msg_transfer); - // Inject RRC Reconfiguration Complete + // Inject UE capability info ul_rrc_msg_transfer = generate_ul_rrc_message_transfer( - cu_ue_id, du_ue_id, srb_id_t::srb1, make_byte_buffer("00040c00fbca0d80").value()); + cu_ue_id, + du_ue_id, + srb_id_t::srb1, + make_byte_buffer("00044c821930680ce811d1968097e360e1480005824c5c00060fc2c00637fe002e00131401a0000000880058d006007" + "a071e439f0000240400e0300000000100186c0000700809df000000000000030368000800004b2ca000a07143c001c0" + "03c000000100200409028098a8660c") + .value()); f1c_gw.get_du(du_index).on_new_message(ul_rrc_msg_transfer); } @@ -339,23 +345,6 @@ void cu_cp_test::add_pdu_sessions(std::vector psis, generate_valid_pdu_session_resource_setup_request_message(amf_ue_id, ran_ue_id, psi); cu_cp_obj->get_ngap_message_handler().handle_message(pdu_session_resource_setup_request); - // check that the UE capability enquiry was sent to the DU - ASSERT_EQ(f1c_gw.last_tx_pdus(0).back().pdu.type(), asn1::f1ap::f1ap_pdu_c::types_opts::options::init_msg); - ASSERT_EQ(f1c_gw.last_tx_pdus(0).back().pdu.init_msg().value.type().value, - asn1::f1ap::f1ap_elem_procs_o::init_msg_c::types_opts::dl_rrc_msg_transfer); - - // Inject UE capability info - f1ap_message ul_rrc_msg_transfer = generate_ul_rrc_message_transfer( - cu_ue_id, - du_ue_id, - srb_id_t::srb1, - make_byte_buffer( - "00074e821930680ce811d1968097e360e1480005824c5c00060fc2c00637fe002e00131401a0000000880058d006007" - "a071e439f0000240400e0300000000100186c0000700809df000000000000030368000800004b2ca000a07143c001c0" - "03c00000010020040902807b0dba95") - .value()); - f1c_gw.get_du(du_index).on_new_message(ul_rrc_msg_transfer); - if (initial_pdu_session) { initial_pdu_session = false; @@ -399,6 +388,15 @@ void cu_cp_test::add_pdu_sessions(std::vector psis, ASSERT_EQ(e1ap_gw.last_tx_pdus(0).back().pdu.init_msg().value.type().value, asn1::e1ap::e1ap_elem_procs_o::init_msg_c::types_opts::bearer_context_mod_request); + // check that the UE Context Modification Request contains the UE capabilities + ASSERT_TRUE(f1c_gw.last_tx_pdus(0).back().pdu.init_msg().value.ue_context_mod_request()->cu_to_du_rrc_info_present); + ASSERT_NE(f1c_gw.last_tx_pdus(0) + .back() + .pdu.init_msg() + .value.ue_context_mod_request() + ->cu_to_du_rrc_info.ue_cap_rat_container_list.size(), + 0U); + // Inject Bearer Context Modification Response e1ap_message bearer_context_mod_resp = generate_bearer_context_modification_response(cu_cp_ue_e1ap_id, cu_up_ue_e1ap_id); @@ -413,8 +411,8 @@ void cu_cp_test::add_pdu_sessions(std::vector psis, asn1::f1ap::f1ap_elem_procs_o::init_msg_c::types_opts::dl_rrc_msg_transfer); // Inject RRC Reconfiguration Complete - ul_rrc_msg_transfer = generate_ul_rrc_message_transfer( - cu_ue_id, du_ue_id, srb_id_t::srb1, make_byte_buffer("00080800e6847bbd").value()); + f1ap_message ul_rrc_msg_transfer = generate_ul_rrc_message_transfer( + cu_ue_id, du_ue_id, srb_id_t::srb1, make_byte_buffer("00070e00cc6fcda5").value()); f1c_gw.get_du(du_index).on_new_message(ul_rrc_msg_transfer); // check that the PDU Session Resource Setup Response was sent to the AMF diff --git a/tests/unittests/cu_cp/cu_cp_test_messages.cpp b/tests/unittests/cu_cp/cu_cp_test_messages.cpp index f6da61d19c..e66e38150a 100644 --- a/tests/unittests/cu_cp/cu_cp_test_messages.cpp +++ b/tests/unittests/cu_cp/cu_cp_test_messages.cpp @@ -31,11 +31,12 @@ using namespace srsran; using namespace srs_cu_cp; -cu_cp_ue_context_release_command srsran::srs_cu_cp::generate_ue_context_release_command(ue_index_t ue_index) +cu_cp_ue_context_release_command srsran::srs_cu_cp::generate_ue_context_release_command(ue_index_t ue_index, + ngap_cause_t cause) { cu_cp_ue_context_release_command ue_context_release_command = {}; ue_context_release_command.ue_index = ue_index; - ue_context_release_command.cause = ngap_cause_radio_network_t::unspecified; + ue_context_release_command.cause = cause; return ue_context_release_command; } diff --git a/tests/unittests/cu_cp/cu_cp_test_messages.h b/tests/unittests/cu_cp/cu_cp_test_messages.h index 6bd4a5c1cc..aeab6e8a4f 100644 --- a/tests/unittests/cu_cp/cu_cp_test_messages.h +++ b/tests/unittests/cu_cp/cu_cp_test_messages.h @@ -32,8 +32,10 @@ namespace srs_cu_cp { /// \brief Generate a dummy UE Context Release Command. /// \param[in] ue_index The UE Index to use. +/// \param[in] cause The cause to use. /// \return The dummy UE Context Release Command. -cu_cp_ue_context_release_command generate_ue_context_release_command(ue_index_t ue_index); +cu_cp_ue_context_release_command +generate_ue_context_release_command(ue_index_t ue_index, ngap_cause_t cause = ngap_cause_radio_network_t::unspecified); /// \brief Generate a dummy PDU Session Resource Setup request. cu_cp_pdu_session_resource_setup_request generate_pdu_session_resource_setup(ue_index_t ue_index = ue_index_t::min, 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 5c06ef077d..57cdf5daae 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,6 +21,7 @@ */ #include "lib/cu_cp/du_processor/du_configuration_manager.h" +#include "srsran/ngap/ngap_configuration.h" #include "srsran/rrc/rrc_config.h" #include @@ -34,23 +35,33 @@ static rrc_cfg_t create_basic_rrc_config() return cfg; } -static cu_cp_served_cell_info create_basic_served_cell_info() +static ngap_configuration create_basic_ngap_config() +{ + ngap_configuration cfg{}; + cfg.gnb_id = {411, 22}; + cfg.plmn = plmn_identity::test_value(); + cfg.tac = 7; + return cfg; +} + +static cu_cp_served_cell_info create_basic_served_cell_info(unsigned du_counter) { cu_cp_served_cell_info cell_info; 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.nr_cgi.nci = nr_cell_identity::create({411, 22}, du_counter).value(); cell_info.five_gs_tac = 7; - cell_info.nr_pci = 1; + cell_info.nr_pci = du_counter; + cell_info.served_plmns = {plmn_identity::test_value()}; return cell_info; } -static du_setup_request create_basic_du_setup_request() +static du_setup_request create_basic_du_setup_request(unsigned du_counter = 0) { du_setup_request req; - req.gnb_du_id = gnb_du_id_t::min; - req.gnb_du_name = "srsdu"; + req.gnb_du_id = int_to_gnb_du_id(du_counter); + req.gnb_du_name = fmt::format("srsdu{}", du_counter); auto& cell = req.gnb_du_served_cells_list.emplace_back(); - cell.served_cell_info = create_basic_served_cell_info(); + cell.served_cell_info = create_basic_served_cell_info(du_counter); cell.gnb_du_sys_info.emplace(); cell.gnb_du_sys_info->mib_msg = byte_buffer::create({0x0, 0x1, 0x2}).value(); cell.gnb_du_sys_info->sib1_msg = byte_buffer::create({0x3, 0x4, 0x5}).value(); @@ -60,9 +71,10 @@ static du_setup_request create_basic_du_setup_request() class du_configuration_manager_test : public ::testing::Test { public: - du_configuration_manager_test() : du_cfg_mng(rrc_cfg) {} + du_configuration_manager_test() : du_cfg_mng(ngap_cfg, rrc_cfg) {} - rrc_cfg_t rrc_cfg = create_basic_rrc_config(); + rrc_cfg_t rrc_cfg = create_basic_rrc_config(); + ngap_configuration ngap_cfg = create_basic_ngap_config(); du_configuration_manager du_cfg_mng; }; @@ -98,16 +110,68 @@ TEST_F(du_configuration_manager_test, when_du_cfg_handler_goes_out_of_scope_then ASSERT_EQ(du_cfg_mng.nof_dus(), 0); } +TEST_F(du_configuration_manager_test, when_two_dus_have_valid_configs_then_the_two_dus_are_added) +{ + auto setup_req1 = create_basic_du_setup_request(0); + auto setup_req2 = create_basic_du_setup_request(1); + + auto du_cfg_updater = du_cfg_mng.create_du_handler(); + auto ret = du_cfg_updater->handle_new_du_config(setup_req1); + ASSERT_TRUE(ret.has_value()); + + auto du_cfg_updater2 = du_cfg_mng.create_du_handler(); + ret = du_cfg_updater2->handle_new_du_config(setup_req2); + ASSERT_TRUE(ret.has_value()); + + ASSERT_EQ(du_cfg_mng.nof_dus(), 2); +} + TEST_F(du_configuration_manager_test, when_du_has_duplicate_du_id_then_setup_fails) { + auto setup_req1 = create_basic_du_setup_request(0); + auto setup_req2 = create_basic_du_setup_request(1); + setup_req2.gnb_du_id = setup_req1.gnb_du_id; + auto du_cfg_updater = du_cfg_mng.create_du_handler(); - auto setup_req = create_basic_du_setup_request(); - auto ret = du_cfg_updater->handle_new_du_config(setup_req); + auto ret = du_cfg_updater->handle_new_du_config(setup_req1); ASSERT_TRUE(ret.has_value()); auto du_cfg_updater2 = du_cfg_mng.create_du_handler(); - ret = du_cfg_updater2->handle_new_du_config(setup_req); + ret = du_cfg_updater2->handle_new_du_config(setup_req2); ASSERT_FALSE(ret.has_value()); + ASSERT_EQ(du_cfg_mng.nof_dus(), 1); fmt::print("DU creation failed with error: {}\n", ret.error().cause_str); } + +TEST_F(du_configuration_manager_test, when_du_has_duplicate_nci_then_setup_fails) +{ + auto setup_req1 = create_basic_du_setup_request(0); + auto setup_req2 = create_basic_du_setup_request(1); + setup_req2.gnb_du_served_cells_list[0].served_cell_info.nr_cgi = + setup_req1.gnb_du_served_cells_list[0].served_cell_info.nr_cgi; + + auto du_cfg_updater = du_cfg_mng.create_du_handler(); + auto ret = du_cfg_updater->handle_new_du_config(setup_req1); + ASSERT_TRUE(ret.has_value()); + + auto du_cfg_updater2 = du_cfg_mng.create_du_handler(); + ret = du_cfg_updater2->handle_new_du_config(setup_req2); + ASSERT_FALSE(ret.has_value()); + + ASSERT_EQ(du_cfg_mng.nof_dus(), 1); + fmt::print("DU creation failed with error: {}\n", ret.error().cause_str); +} + +TEST_F(du_configuration_manager_test, when_du_has_different_plmn_then_setup_fails) +{ + auto setup_req = create_basic_du_setup_request(); + setup_req.gnb_du_served_cells_list[0].served_cell_info.nr_cgi.plmn_id = plmn_identity::parse("00102").value(); + + auto du_cfg_updater = du_cfg_mng.create_du_handler(); + auto ret = du_cfg_updater->handle_new_du_config(setup_req); + ASSERT_FALSE(ret.has_value()); + + ASSERT_EQ(du_cfg_mng.nof_dus(), 0); + fmt::print("DU creation failed with error: {}\n", ret.error().cause_str); +} 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 849273eb9b..7984347185 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 @@ -44,7 +44,10 @@ class dummy_task_sched final : public common_task_scheduler } // namespace du_processor_test::du_processor_test() : - rrc_cfg{gnb_id_t{411, 22}}, common_task_sched(std::make_unique()), du_cfg_mgr{rrc_cfg} + ngap_cfg{gnb_id_t{411, 22}, "srsgnb"}, + rrc_cfg{gnb_id_t{411, 22}}, + common_task_sched(std::make_unique()), + du_cfg_mgr{ngap_cfg, rrc_cfg} { test_logger.set_level(srslog::basic_levels::debug); cu_cp_logger.set_level(srslog::basic_levels::debug); 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 01a5a8ece0..aeec39ca9e 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 @@ -52,6 +52,7 @@ class du_processor_test : public ::testing::Test srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); srslog::basic_logger& cu_cp_logger = srslog::fetch_basic_logger("CU-CP"); + ngap_configuration ngap_cfg; rrc_cfg_t rrc_cfg; timer_manager timers; manual_task_worker ctrl_worker{128}; diff --git a/tests/unittests/cu_cp/mobility/inter_cu_handover_routine_test.cpp b/tests/unittests/cu_cp/mobility/inter_cu_handover_routine_test.cpp index 92656c30f9..1aebe6bbfd 100644 --- a/tests/unittests/cu_cp/mobility/inter_cu_handover_routine_test.cpp +++ b/tests/unittests/cu_cp/mobility/inter_cu_handover_routine_test.cpp @@ -67,7 +67,7 @@ class inter_cu_handover_routine_test : public mobility_test generate_ul_rrc_message_transfer(int_to_gnb_cu_ue_f1ap_id(0), int_to_gnb_du_ue_f1ap_id(0), srb_id_t::srb1, - make_byte_buffer("000900410004015f741fe0804bf183fc980605b7").value()); + make_byte_buffer("000800410004015f741fe0804bf183fcaa6e9699").value()); test_logger.info("Injecting UL RRC message (RRC Measurement Report)"); f1c_gw.get_du(source_du_index).on_new_message(ul_rrc_msg); } 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 e2ed4a7d25..f0c36b0249 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,6 +21,7 @@ */ #include "mobility_test_helpers.h" +#include "srsran/asn1/f1ap/f1ap_pdu_contents_ue.h" #include "srsran/support/async/async_test_utils.h" #include "srsran/support/test_utils.h" #include @@ -70,7 +71,7 @@ class inter_du_handover_routine_test : public mobility_test generate_ul_rrc_message_transfer(int_to_gnb_cu_ue_f1ap_id(0), int_to_gnb_du_ue_f1ap_id(0), srb_id_t::srb1, - make_byte_buffer("000900410004015f741fe0804bf183fc980605b7").value()); + make_byte_buffer("000800410004015f741fe0804bf183fcaa6e9699").value()); test_logger.info("Injecting UL RRC message (RRC Measurement Report)"); f1c_gw.get_du(source_du_index).on_new_message(ul_rrc_msg); } @@ -157,7 +158,7 @@ class inter_du_handover_routine_test : public mobility_test f1ap_message rrc_recfg_complete = generate_ul_rrc_message_transfer(int_to_gnb_cu_ue_f1ap_id(0), int_to_gnb_du_ue_f1ap_id(0), srb_id_t::srb1, - make_byte_buffer("80000a00ddc7574a").value()); + make_byte_buffer("8000080035c41efd").value()); f1c_gw.get_du(target_du_index).on_new_message(rrc_recfg_complete); } @@ -260,6 +261,17 @@ TEST_F(inter_du_handover_routine_test, when_ho_succeeds_then_source_ue_is_remove // Start handover by injecting measurement report inject_rrc_meas_report(); + // check that the UE Context Setup Request contains the UE capabilities + ASSERT_EQ(f1c_gw.last_tx_pdus(1).back().pdu.type(), asn1::f1ap::f1ap_pdu_c::types_opts::options::init_msg); + ASSERT_EQ(f1c_gw.last_tx_pdus(1).back().pdu.init_msg().value.type().value, + asn1::f1ap::f1ap_elem_procs_o::init_msg_c::types_opts::ue_context_setup_request); + ASSERT_NE(f1c_gw.last_tx_pdus(1) + .back() + .pdu.init_msg() + .value.ue_context_setup_request() + ->cu_to_du_rrc_info.ue_cap_rat_container_list.size(), + 0U); + // Inject UE Context Setup Response inject_ue_context_setup_response(); diff --git a/tests/unittests/cu_cp/test_helpers.h b/tests/unittests/cu_cp/test_helpers.h index b704f9a8f5..f5b62e14ff 100644 --- a/tests/unittests/cu_cp/test_helpers.h +++ b/tests/unittests/cu_cp/test_helpers.h @@ -507,6 +507,11 @@ struct dummy_f1ap_ue_context_manager : public f1ap_ue_context_manager { { logger.info("Received a new UE context release command"); + last_release_command.ue_index = msg.ue_index; + last_release_command.cause = msg.cause; + last_release_command.rrc_release_pdu = msg.rrc_release_pdu.copy(); + last_release_command.srb_id = msg.srb_id; + return launch_async([msg](coro_context>& ctx) mutable { CORO_BEGIN(ctx); CORO_RETURN(msg.ue_index); @@ -517,6 +522,8 @@ struct dummy_f1ap_ue_context_manager : public f1ap_ue_context_manager { const f1ap_ue_context_modification_request& get_ctxt_mod_request() { return ue_context_modifcation_request; } + f1ap_ue_context_release_command last_release_command; + private: void make_partial_copy(f1ap_ue_context_modification_request& target, const f1ap_ue_context_modification_request& source) @@ -562,6 +569,12 @@ struct dummy_du_processor_rrc_ue_control_message_notifier : public du_processor_ }); } + byte_buffer get_packed_ue_capability_rat_container_list() override + { + logger.info("Received a new request to get packed UE capabilities"); + return byte_buffer{}; + } + rrc_ue_handover_reconfiguration_context get_rrc_ue_handover_reconfiguration_context(const rrc_reconfiguration_procedure_request& request) override { diff --git a/tests/unittests/du_manager/du_ran_resource_manager_test.cpp b/tests/unittests/du_manager/du_ran_resource_manager_test.cpp index b09ea17910..62e63df96c 100644 --- a/tests/unittests/du_manager/du_ran_resource_manager_test.cpp +++ b/tests/unittests/du_manager/du_ran_resource_manager_test.cpp @@ -22,7 +22,7 @@ #include "lib/du_manager/ran_resource_management/du_ran_resource_manager_impl.h" #include "srsran/du/du_cell_config_helpers.h" -#include "srsran/support/math/gcd.h" +#include "srsran/support/math/lcm.h" #include "srsran/support/test_utils.h" #include @@ -122,7 +122,7 @@ class du_ran_resource_manager_tester_base const du_cell_config& du_cfg = cell_cfg_list[0]; const unsigned nof_sr_f1_res_per_ue = 1U; const unsigned nof_csi_f2_res_per_ue = 1U; - bool pucch_checker = pucch_cfg.pucch_res_list.size() == du_cfg.pucch_cfg.nof_ue_pucch_f1_res_harq.to_uint() + + bool pucch_checker = pucch_cfg.pucch_res_list.size() == du_cfg.pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq.to_uint() + du_cfg.pucch_cfg.nof_ue_pucch_f2_res_harq.to_uint() + nof_sr_f1_res_per_ue + nof_csi_f2_res_per_ue; @@ -130,14 +130,14 @@ class du_ran_resource_manager_tester_base // UE can have different values within this range. pucch_checker = pucch_checker and pucch_cfg.sr_res_list.front().pucch_res_id.cell_res_id >= - du_cfg.pucch_cfg.nof_ue_pucch_f1_res_harq.to_uint() and + du_cfg.pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq.to_uint() and pucch_cfg.sr_res_list.front().pucch_res_id.cell_res_id < - du_cfg.pucch_cfg.nof_ue_pucch_f1_res_harq.to_uint() + du_cfg.pucch_cfg.nof_sr_resources; + du_cfg.pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq.to_uint() + du_cfg.pucch_cfg.nof_sr_resources; // We always put the CSI PUCCH resource is always at the end of the list. if (csi_pucch_res.has_value()) { pucch_checker = - pucch_checker and csi_pucch_res.value() >= du_cfg.pucch_cfg.nof_ue_pucch_f1_res_harq.to_uint() + + pucch_checker and csi_pucch_res.value() >= du_cfg.pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq.to_uint() + du_cfg.pucch_cfg.nof_sr_resources + du_cfg.pucch_cfg.nof_ue_pucch_f2_res_harq.to_uint(); } @@ -314,15 +314,16 @@ using namespace du_test_multiple_pucch_cfg; static du_cell_config make_custom_du_cell_config(const pucch_cfg_builder_params& pucch_params_) { - du_cell_config du_cfg = config_helpers::make_default_du_cell_config(); - auto& pucch_params = du_cfg.pucch_cfg; - pucch_params.nof_ue_pucch_f1_res_harq = pucch_params_.nof_res_f1_harq; - pucch_params.nof_ue_pucch_f2_res_harq = pucch_params_.nof_res_f2_harq; - pucch_params.nof_sr_resources = pucch_params_.nof_res_sr; - pucch_params.nof_csi_resources = pucch_params_.nof_res_csi; - pucch_params.nof_cell_harq_pucch_res_sets = pucch_params_.nof_harq_cfg; - pucch_params.f1_params.nof_cyc_shifts = srsran::nof_cyclic_shifts::six; - pucch_params.f1_params.occ_supported = true; + du_cell_config du_cfg = config_helpers::make_default_du_cell_config(); + auto& pucch_params = du_cfg.pucch_cfg; + pucch_params.nof_ue_pucch_f0_or_f1_res_harq = pucch_params_.nof_res_f1_harq; + pucch_params.nof_ue_pucch_f2_res_harq = pucch_params_.nof_res_f2_harq; + pucch_params.nof_sr_resources = pucch_params_.nof_res_sr; + pucch_params.nof_csi_resources = pucch_params_.nof_res_csi; + pucch_params.nof_cell_harq_pucch_res_sets = pucch_params_.nof_harq_cfg; + auto& f1_params = std::get(pucch_params.f0_or_f1_params); + f1_params.nof_cyc_shifts = srsran::nof_cyclic_shifts::six; + f1_params.occ_supported = true; return du_cfg; } @@ -352,11 +353,11 @@ class du_ran_res_mng_multiple_cfg_tester : public du_ran_resource_manager_tester // HARQ from the UE's PUCCH-Config. interval get_pucch_res_id_interval(const pucch_config& pucch_cfg, pucch_format format) const { - const unsigned pucch_res_set_id = format == pucch_format::FORMAT_1 ? 0U : 1U; - const auto& pucch_res_set = pucch_cfg.pucch_res_set[pucch_res_set_id].pucch_res_id_list; - const unsigned expected_pucch_res_set_size = format == pucch_format::FORMAT_1 - ? cell_cfg_list[0].pucch_cfg.nof_ue_pucch_f1_res_harq.to_uint() - : cell_cfg_list[0].pucch_cfg.nof_ue_pucch_f2_res_harq.to_uint(); + const unsigned pucch_res_set_id = format == pucch_format::FORMAT_1 ? 0U : 1U; + const auto& pucch_res_set = pucch_cfg.pucch_res_set[pucch_res_set_id].pucch_res_id_list; + const unsigned expected_pucch_res_set_size = + format == pucch_format::FORMAT_1 ? cell_cfg_list[0].pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq.to_uint() + : cell_cfg_list[0].pucch_cfg.nof_ue_pucch_f2_res_harq.to_uint(); if (expected_pucch_res_set_size != pucch_res_set.size()) { return {}; } @@ -376,7 +377,7 @@ class du_ran_res_mng_multiple_cfg_tester : public du_ran_resource_manager_tester interval get_expected_pucch_res_id_interval(unsigned ue_idx, pucch_format format) const { const unsigned expected_nof_pucch_res = format == pucch_format::FORMAT_1 - ? cell_cfg_list[0].pucch_cfg.nof_ue_pucch_f1_res_harq.to_uint() + ? cell_cfg_list[0].pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq.to_uint() : cell_cfg_list[0].pucch_cfg.nof_ue_pucch_f2_res_harq.to_uint(); if (expected_nof_pucch_res == 0) { @@ -387,7 +388,7 @@ class du_ran_res_mng_multiple_cfg_tester : public du_ran_resource_manager_tester const unsigned f2_res_idx_offset = format == pucch_format::FORMAT_1 ? 0U - : cell_cfg_list[0].pucch_cfg.nof_ue_pucch_f1_res_harq.to_uint() * nof_harq_cfgs + + : cell_cfg_list[0].pucch_cfg.nof_ue_pucch_f0_or_f1_res_harq.to_uint() * nof_harq_cfgs + cell_cfg_list[0].pucch_cfg.nof_sr_resources; return {f2_res_idx_offset + (ue_idx % nof_harq_cfgs) * expected_nof_pucch_res, f2_res_idx_offset + (ue_idx % nof_harq_cfgs) * expected_nof_pucch_res + expected_nof_pucch_res - 1}; @@ -563,12 +564,13 @@ static du_cell_config make_custom_du_cell_config_for_pucch_cnt(const pucch_cnt_builder_params& pucch_params_, const srsran::config_helpers::cell_config_builder_params_extended& params = {}) { - du_cell_config du_cfg = config_helpers::make_default_du_cell_config(params); - auto& pucch_params = du_cfg.pucch_cfg; - pucch_params.nof_sr_resources = pucch_params_.nof_res_sr; - pucch_params.nof_csi_resources = pucch_params_.nof_res_csi; - pucch_params.f1_params.nof_cyc_shifts = srsran::nof_cyclic_shifts::six; - pucch_params.f1_params.occ_supported = true; + du_cell_config du_cfg = config_helpers::make_default_du_cell_config(params); + auto& pucch_params = du_cfg.pucch_cfg; + pucch_params.nof_sr_resources = pucch_params_.nof_res_sr; + pucch_params.nof_csi_resources = pucch_params_.nof_res_csi; + auto& f1_params = std::get(pucch_params.f0_or_f1_params); + f1_params.nof_cyc_shifts = srsran::nof_cyclic_shifts::six; + f1_params.occ_supported = true; du_cfg.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pucch_cfg->sr_res_list[0].period = pucch_params_.sr_period; if (du_cfg.ue_ded_serv_cell_cfg.csi_meas_cfg.has_value()) { @@ -595,7 +597,7 @@ class du_ran_res_mng_pucch_cnt_tester : public du_ran_resource_manager_tester_ba default_ue_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type), "CSI report configuration is required for this unittest;"); lcm_csi_sr_period = - lcm(sr_periodicity_to_slot(GetParam().sr_period), csi_report_periodicity_to_uint(GetParam().csi_period)); + std::lcm(sr_periodicity_to_slot(GetParam().sr_period), csi_report_periodicity_to_uint(GetParam().csi_period)); pucch_cnts.resize(lcm_csi_sr_period, 0); } diff --git a/tests/unittests/du_manager/pucch_resource_generator_test.cpp b/tests/unittests/du_manager/pucch_resource_generator_test.cpp index c4e065cb63..0da2940760 100644 --- a/tests/unittests/du_manager/pucch_resource_generator_test.cpp +++ b/tests/unittests/du_manager/pucch_resource_generator_test.cpp @@ -31,9 +31,12 @@ using namespace srs_du; namespace du_pucch_gen { // Test struct. -struct pucch_gen_params_opt1 { +struct pucch_gen_params { + unsigned nof_res_f0; unsigned nof_res_f1; unsigned nof_res_f2; + bounded_integer f0_nof_symbols{2}; + bool f0_intraslot_freq_hopping{false}; nof_cyclic_shifts nof_cyc_shifts{nof_cyclic_shifts::no_cyclic_shift}; bool occ_supported{false}; bounded_integer f1_nof_symbols{14}; @@ -47,34 +50,13 @@ struct pucch_gen_params_opt1 { // Dummy function overload of template void testing::internal::PrintTo(const T& value, ::std::ostream* os). // This prevents valgrind from complaining about uninitialized variables. -void PrintTo(const pucch_gen_params_opt1& value, ::std::ostream* os) -{ - return; -} - -// Test struct. -struct pucch_gen_params_opt2 { - unsigned max_nof_rbs_f1; - unsigned max_nof_rbs_f2; - nof_cyclic_shifts nof_cyc_shifts{nof_cyclic_shifts::no_cyclic_shift}; - bool occ_supported{false}; - bounded_integer f1_nof_symbols{14}; - bool f1_intraslot_freq_hopping{false}; - bounded_integer f2_nof_symbols{1}; - unsigned max_nof_rbs{1}; - std::optional max_payload_bits; - max_pucch_code_rate max_code_rate{max_pucch_code_rate::dot_25}; - bool f2_intraslot_freq_hopping{false}; -}; - -// Dummy function overload of template void testing::internal::PrintTo(const T& value, ::std::ostream* os). -// This prevents valgrind from complaining about uninitialized variables. -void PrintTo(const pucch_gen_params_opt2& value, ::std::ostream* os) +void PrintTo(const pucch_gen_params& value, ::std::ostream* os) { return; } struct pucch_cfg_builder_params { + unsigned nof_res_f0_harq = 0; unsigned nof_res_f1_harq = 3; unsigned nof_res_f2_harq = 6; unsigned nof_harq_cfg = 1; @@ -106,7 +88,37 @@ class pucch_grid // Allocate the resource on the grid. void add_resource(const pucch_resource& res) { - if (res.format == pucch_format::FORMAT_1) { + if (res.format == pucch_format::FORMAT_0) { + srsran_assert(std::holds_alternative(res.format_params), "Expected PUCCH Format 0"); + const auto& res_f0 = std::get(res.format_params); + + if (res.second_hop_prb.has_value()) { + srsran_assert(res_f0.nof_symbols == 2U, "Intra-slot freq. hopping for PUCCH Format 0 requires 2 symbols"); + // First hop. + for (unsigned sym_idx = res_f0.starting_sym_idx; sym_idx < res_f0.starting_sym_idx + res_f0.nof_symbols / 2; + ++sym_idx) { + auto& grid_elem = grid[sym_idx + nof_symbols * res.starting_prb]; + grid_elem.format = pucch_format::FORMAT_0; + grid_elem.element_used = true; + } + // Second hop. + for (unsigned sym_idx = res_f0.starting_sym_idx + res_f0.nof_symbols / 2; + sym_idx < res_f0.starting_sym_idx + res_f0.nof_symbols; + ++sym_idx) { + auto& grid_elem = grid[sym_idx + nof_symbols * res.second_hop_prb.value()]; + grid_elem.format = pucch_format::FORMAT_0; + grid_elem.element_used = true; + } + } else { + for (unsigned sym_idx = res_f0.starting_sym_idx; sym_idx < res_f0.starting_sym_idx + res_f0.nof_symbols; + ++sym_idx) { + auto& grid_elem = grid[sym_idx + nof_symbols * res.starting_prb]; + grid_elem.format = pucch_format::FORMAT_0; + grid_elem.element_used = true; + } + } + + } else if (res.format == pucch_format::FORMAT_1) { srsran_assert(std::holds_alternative(res.format_params), "Expected PUCCH Format 1"); const auto& res_f1 = std::get(res.format_params); @@ -152,6 +164,7 @@ class pucch_grid const auto& res_f2 = std::get(res.format_params); if (res.second_hop_prb.has_value()) { + srsran_assert(res_f2.nof_symbols == 2U, "Intra-slot freq. hopping for PUCCH Format 0 requires 2 symbols"); // First hop. for (unsigned rb_idx = res.starting_prb; rb_idx < res.starting_prb + res_f2.nof_prbs; ++rb_idx) { for (unsigned sym_idx = res_f2.starting_sym_idx; sym_idx < res_f2.starting_sym_idx + res_f2.nof_symbols / 2; @@ -188,7 +201,41 @@ class pucch_grid // Verify if the given resource collides in the grid with the previously allocated resources. bool verify_collision(const pucch_resource& res) const { - if (res.format == pucch_format::FORMAT_1) { + if (res.format == pucch_format::FORMAT_0) { + srsran_assert(std::holds_alternative(res.format_params), "Expected PUCCH Format 0"); + const auto& res_f0 = std::get(res.format_params); + // Intra-slot frequency hopping. + if (res.second_hop_prb.has_value()) { + // First hop. + for (unsigned sym_idx = res_f0.starting_sym_idx; sym_idx < res_f0.starting_sym_idx + res_f0.nof_symbols / 2; + ++sym_idx) { + const auto& grid_elem = grid[sym_idx + nof_symbols * res.starting_prb]; + if (grid_elem.element_used) { + return true; + } + } + // Second hop. + for (unsigned sym_idx = res_f0.starting_sym_idx + res_f0.nof_symbols / 2; + sym_idx < res_f0.starting_sym_idx + res_f0.nof_symbols; + ++sym_idx) { + const auto& grid_elem = grid[sym_idx + nof_symbols * res.second_hop_prb.value()]; + if (grid_elem.element_used) { + return true; + } + } + + } + // No intra-slot frequency hopping. + else { + for (unsigned sym_idx = res_f0.starting_sym_idx; sym_idx < res_f0.starting_sym_idx + res_f0.nof_symbols; + ++sym_idx) { + const auto& grid_elem = grid[sym_idx + nof_symbols * res.starting_prb]; + if (grid_elem.element_used) { + return true; + } + } + } + } else if (res.format == pucch_format::FORMAT_1) { srsran_assert(std::holds_alternative(res.format_params), "Expected PUCCH Format 1"); const auto& res_f1 = std::get(res.format_params); // Intra-slot frequency hopping. @@ -294,22 +341,6 @@ class pucch_grid return false; } - // Compute the number of RBs used for PUCCH resources. - unsigned get_tot_nof_used_rbs() const - { - unsigned used_rbs_cnt = 0; - for (unsigned rb_idx = 0; rb_idx < nof_rbs; ++rb_idx) { - for (unsigned sym_idx = 0; sym_idx < nof_symbols; ++sym_idx) { - if (grid[sym_idx + nof_symbols * rb_idx].element_used) { - ++used_rbs_cnt; - break; - } - } - } - - return used_rbs_cnt; - } - private: // Maximum 7 different OCCs, as per \c PUCCH-format1, in \c PUCCH-Config, TS 38.331. static const unsigned max_nof_occ = 7; @@ -340,7 +371,7 @@ class test_pucch_res_generator : public ::testing::Test pucch_grid grid; }; -class test_pucch_res_generator_params : public ::testing::TestWithParam +class test_pucch_res_generator_params : public ::testing::TestWithParam { public: test_pucch_res_generator_params() : grid(bwp_size, nof_symbols_per_slot){}; @@ -354,132 +385,15 @@ class test_pucch_res_generator_params : public ::testing::TestWithParam res_list = - generate_cell_pucch_res_list(nof_res_f1, nof_res_f2, params_f1, params_f2, bwp_size); - - ASSERT_TRUE(res_list.size() > 0); - ASSERT_EQ(nof_res_f1 + nof_res_f2, res_list.size()); - - for (const auto& pucch_res : res_list) { - ASSERT_FALSE(grid.verify_collision(pucch_res)); - grid.add_resource(pucch_res); - } -} + const unsigned nof_res_f0 = GetParam().nof_res_f0; + const unsigned nof_res_f1 = GetParam().nof_res_f1; + const unsigned nof_res_f2 = GetParam().nof_res_f2; -INSTANTIATE_TEST_SUITE_P(test_res_generation_given_number, - test_pucch_res_generator_params, - ::testing::Values(pucch_gen_params_opt1{.nof_res_f1 = 15, - .nof_res_f2 = 10, - .nof_cyc_shifts = nof_cyclic_shifts::twelve, - .occ_supported = false, - .f1_nof_symbols = 7, - .f1_intraslot_freq_hopping = true, - .f2_nof_symbols = 2, - .max_nof_rbs = 2, - .max_code_rate = srsran::max_pucch_code_rate::dot_25, - .f2_intraslot_freq_hopping = false}, - pucch_gen_params_opt1{.nof_res_f1 = 39, - .nof_res_f2 = 19, - .nof_cyc_shifts = nof_cyclic_shifts::twelve, - .occ_supported = false, - .f1_nof_symbols = 7, - .f1_intraslot_freq_hopping = true, - .f2_nof_symbols = 2, - .max_nof_rbs = 2, - .max_code_rate = srsran::max_pucch_code_rate::dot_15, - .f2_intraslot_freq_hopping = true}, - pucch_gen_params_opt1{.nof_res_f1 = 39, - .nof_res_f2 = 19, - .nof_cyc_shifts = nof_cyclic_shifts::no_cyclic_shift, - .occ_supported = true, - .f1_nof_symbols = 7, - .f1_intraslot_freq_hopping = true, - .f2_nof_symbols = 2, - .max_nof_rbs = 2, - .max_code_rate = srsran::max_pucch_code_rate::dot_25, - .f2_intraslot_freq_hopping = true}, - pucch_gen_params_opt1{.nof_res_f1 = 39, - .nof_res_f2 = 19, - .nof_cyc_shifts = nof_cyclic_shifts::no_cyclic_shift, - .occ_supported = true, - .f1_nof_symbols = 11, - .f1_intraslot_freq_hopping = true, - .f2_nof_symbols = 2, - .max_nof_rbs = 1, - .max_code_rate = srsran::max_pucch_code_rate::dot_15, - .f2_intraslot_freq_hopping = true}, - pucch_gen_params_opt1{.nof_res_f1 = 137, - .nof_res_f2 = 25, - .nof_cyc_shifts = nof_cyclic_shifts::twelve, - .occ_supported = true, - .f1_nof_symbols = 14, - .f1_intraslot_freq_hopping = true, - .f2_nof_symbols = 2, - .max_nof_rbs = 1, - .max_code_rate = srsran::max_pucch_code_rate::dot_15, - .f2_intraslot_freq_hopping = false}, - pucch_gen_params_opt1{.nof_res_f1 = 36, - .nof_res_f2 = 27, - .nof_cyc_shifts = nof_cyclic_shifts::three, - .occ_supported = true, - .f1_nof_symbols = 9, - .f1_intraslot_freq_hopping = false, - .f2_nof_symbols = 2, - .max_nof_rbs = 7, - .max_code_rate = srsran::max_pucch_code_rate::dot_15, - .f2_intraslot_freq_hopping = true}, - pucch_gen_params_opt1{.nof_res_f1 = 34, - .nof_res_f2 = 10, - .nof_cyc_shifts = nof_cyclic_shifts::two, - .occ_supported = false, - .f1_nof_symbols = 9, - .f1_intraslot_freq_hopping = false, - .f2_nof_symbols = 1, - .max_nof_rbs = 7, - .max_payload_bits = 11, - .max_code_rate = srsran::max_pucch_code_rate::dot_08, - .f2_intraslot_freq_hopping = false}, - pucch_gen_params_opt1{.nof_res_f1 = 15, - .nof_res_f2 = 10, - .nof_cyc_shifts = nof_cyclic_shifts::no_cyclic_shift, - .occ_supported = false, - .f1_nof_symbols = 9, - .f1_intraslot_freq_hopping = false, - .f2_nof_symbols = 2, - .max_nof_rbs = 7, - .max_payload_bits = 11, - .max_code_rate = srsran::max_pucch_code_rate::dot_08, - .f2_intraslot_freq_hopping = true})); - -class test_pucch_res_generator_max_nof_rbs : public ::testing::TestWithParam -{ -public: - test_pucch_res_generator_max_nof_rbs() : grid(bwp_size, nof_symbols_per_slot){}; + ASSERT_FALSE(nof_res_f0 != 0 and nof_res_f1 != 0) << "PUCCH Format 0 and Format 1 resources cannot be used together"; -protected: - // Parameters that are passed by the routing to run the tests. - const unsigned bwp_size{106}; - const unsigned nof_symbols_per_slot{14}; - pucch_grid grid; -}; + pucch_f0_params params_f0{.nof_symbols = GetParam().f0_nof_symbols, + .intraslot_freq_hopping = GetParam().f0_intraslot_freq_hopping}; -TEST_P(test_pucch_res_generator_max_nof_rbs, test_pucch_res_given_max_rbs) -{ - const unsigned max_nof_rbs_f1 = GetParam().max_nof_rbs_f1; - const unsigned max_nof_rbs_f2 = GetParam().max_nof_rbs_f2; pucch_f1_params params_f1{.nof_cyc_shifts = GetParam().nof_cyc_shifts, .occ_supported = GetParam().occ_supported, .nof_symbols = GetParam().f1_nof_symbols, @@ -491,88 +405,190 @@ TEST_P(test_pucch_res_generator_max_nof_rbs, test_pucch_res_given_max_rbs) .max_code_rate = GetParam().max_code_rate, .intraslot_freq_hopping = GetParam().f2_intraslot_freq_hopping}; - std::vector res_list = - generate_cell_pucch_res_list_given_rbs(max_nof_rbs_f1, max_nof_rbs_f2, params_f1, params_f2, bwp_size); + std::vector res_list; + if (nof_res_f0 != 0) { + res_list = generate_cell_pucch_res_list(nof_res_f0, nof_res_f2, params_f0, params_f2, bwp_size); + } else { + res_list = generate_cell_pucch_res_list(nof_res_f1, nof_res_f2, params_f1, params_f2, bwp_size); + } ASSERT_TRUE(res_list.size() > 0); + if (nof_res_f0 != 0) { + ASSERT_EQ(nof_res_f0 + nof_res_f2, res_list.size()); + } else { + ASSERT_EQ(nof_res_f1 + nof_res_f2, res_list.size()); + } - // Allocate the resources on the test grid and verify they do not collide. for (const auto& pucch_res : res_list) { ASSERT_FALSE(grid.verify_collision(pucch_res)); grid.add_resource(pucch_res); } - - // Verify the RBs allocated to PUCCH resources do not exceed threshold given in the input. - ASSERT_TRUE(grid.get_tot_nof_used_rbs() <= max_nof_rbs_f1 + max_nof_rbs_f2); - ASSERT_TRUE(grid.get_tot_nof_used_rbs() > 0); } -INSTANTIATE_TEST_SUITE_P(test_res_generation_given_rbs, - test_pucch_res_generator_max_nof_rbs, - ::testing::Values(pucch_gen_params_opt2{.max_nof_rbs_f1 = 5, - .max_nof_rbs_f2 = 10, - .nof_cyc_shifts = nof_cyclic_shifts::twelve, - .occ_supported = false, - .f1_nof_symbols = 7, - .f1_intraslot_freq_hopping = true, - .f2_nof_symbols = 2, - .max_nof_rbs = 2, - .max_code_rate = srsran::max_pucch_code_rate::dot_25, - .f2_intraslot_freq_hopping = false}, - pucch_gen_params_opt2{.max_nof_rbs_f1 = 12, - .max_nof_rbs_f2 = 7, - .nof_cyc_shifts = nof_cyclic_shifts::twelve, - .occ_supported = true, - .f1_nof_symbols = 14, - .f1_intraslot_freq_hopping = true, - .f2_nof_symbols = 2, - .max_nof_rbs = 1, - .max_code_rate = srsran::max_pucch_code_rate::dot_15, - .f2_intraslot_freq_hopping = false}, - pucch_gen_params_opt2{.max_nof_rbs_f1 = 5, - .max_nof_rbs_f2 = 16, - .nof_cyc_shifts = nof_cyclic_shifts::three, - .occ_supported = true, - .f1_nof_symbols = 9, - .f1_intraslot_freq_hopping = false, - .f2_nof_symbols = 2, - .max_nof_rbs = 7, - .max_code_rate = srsran::max_pucch_code_rate::dot_15, - .f2_intraslot_freq_hopping = true}, - pucch_gen_params_opt2{.max_nof_rbs_f1 = 7, - .max_nof_rbs_f2 = 15, - .nof_cyc_shifts = nof_cyclic_shifts::two, - .occ_supported = false, - .f1_nof_symbols = 9, - .f1_intraslot_freq_hopping = false, - .f2_nof_symbols = 1, - .max_nof_rbs = 7, - .max_payload_bits = 11, - .max_code_rate = srsran::max_pucch_code_rate::dot_08, - .f2_intraslot_freq_hopping = false}, - pucch_gen_params_opt2{.max_nof_rbs_f1 = 5, - .max_nof_rbs_f2 = 10, - .nof_cyc_shifts = nof_cyclic_shifts::no_cyclic_shift, - .occ_supported = false, - .f1_nof_symbols = 9, - .f1_intraslot_freq_hopping = false, - .f2_nof_symbols = 2, - .max_nof_rbs = 7, - .max_payload_bits = 11, - .max_code_rate = srsran::max_pucch_code_rate::dot_25, - .f2_intraslot_freq_hopping = true})); - -/////////////// +INSTANTIATE_TEST_SUITE_P(test_res_generation_given_number, + test_pucch_res_generator_params, + ::testing::Values(pucch_gen_params{.nof_res_f1 = 15, + .nof_res_f2 = 10, + .nof_cyc_shifts = nof_cyclic_shifts::twelve, + .occ_supported = false, + .f1_nof_symbols = 7, + .f1_intraslot_freq_hopping = true, + .f2_nof_symbols = 2, + .max_nof_rbs = 2, + .max_code_rate = srsran::max_pucch_code_rate::dot_25, + .f2_intraslot_freq_hopping = false}, + pucch_gen_params{.nof_res_f1 = 39, + .nof_res_f2 = 19, + .nof_cyc_shifts = nof_cyclic_shifts::twelve, + .occ_supported = false, + .f1_nof_symbols = 7, + .f1_intraslot_freq_hopping = true, + .f2_nof_symbols = 2, + .max_nof_rbs = 2, + .max_code_rate = srsran::max_pucch_code_rate::dot_15, + .f2_intraslot_freq_hopping = true}, + pucch_gen_params{.nof_res_f1 = 39, + .nof_res_f2 = 19, + .nof_cyc_shifts = nof_cyclic_shifts::no_cyclic_shift, + .occ_supported = true, + .f1_nof_symbols = 7, + .f1_intraslot_freq_hopping = true, + .f2_nof_symbols = 2, + .max_nof_rbs = 2, + .max_code_rate = srsran::max_pucch_code_rate::dot_25, + .f2_intraslot_freq_hopping = true}, + pucch_gen_params{.nof_res_f1 = 39, + .nof_res_f2 = 19, + .nof_cyc_shifts = nof_cyclic_shifts::no_cyclic_shift, + .occ_supported = true, + .f1_nof_symbols = 11, + .f1_intraslot_freq_hopping = true, + .f2_nof_symbols = 2, + .max_nof_rbs = 1, + .max_code_rate = srsran::max_pucch_code_rate::dot_15, + .f2_intraslot_freq_hopping = true}, + pucch_gen_params{.nof_res_f1 = 137, + .nof_res_f2 = 25, + .nof_cyc_shifts = nof_cyclic_shifts::twelve, + .occ_supported = true, + .f1_nof_symbols = 14, + .f1_intraslot_freq_hopping = true, + .f2_nof_symbols = 2, + .max_nof_rbs = 1, + .max_code_rate = srsran::max_pucch_code_rate::dot_15, + .f2_intraslot_freq_hopping = false}, + pucch_gen_params{.nof_res_f1 = 36, + .nof_res_f2 = 27, + .nof_cyc_shifts = nof_cyclic_shifts::three, + .occ_supported = true, + .f1_nof_symbols = 9, + .f1_intraslot_freq_hopping = false, + .f2_nof_symbols = 2, + .max_nof_rbs = 7, + .max_code_rate = srsran::max_pucch_code_rate::dot_15, + .f2_intraslot_freq_hopping = true}, + pucch_gen_params{.nof_res_f1 = 34, + .nof_res_f2 = 10, + .nof_cyc_shifts = nof_cyclic_shifts::two, + .occ_supported = false, + .f1_nof_symbols = 9, + .f1_intraslot_freq_hopping = false, + .f2_nof_symbols = 1, + .max_nof_rbs = 7, + .max_payload_bits = 11, + .max_code_rate = srsran::max_pucch_code_rate::dot_08, + .f2_intraslot_freq_hopping = false}, + pucch_gen_params{.nof_res_f1 = 15, + .nof_res_f2 = 10, + .nof_cyc_shifts = nof_cyclic_shifts::no_cyclic_shift, + .occ_supported = false, + .f1_nof_symbols = 9, + .f1_intraslot_freq_hopping = false, + .f2_nof_symbols = 2, + .max_nof_rbs = 7, + .max_payload_bits = 11, + .max_code_rate = srsran::max_pucch_code_rate::dot_08, + .f2_intraslot_freq_hopping = true}, + pucch_gen_params{.nof_res_f0 = 15, + .nof_res_f2 = 10, + .f0_nof_symbols = 2, + .f0_intraslot_freq_hopping = true, + .f2_nof_symbols = 2, + .max_nof_rbs = 2, + .max_code_rate = srsran::max_pucch_code_rate::dot_25, + .f2_intraslot_freq_hopping = false}, + pucch_gen_params{.nof_res_f0 = 39, + .nof_res_f2 = 19, + .f0_nof_symbols = 2, + .f0_intraslot_freq_hopping = true, + .f2_nof_symbols = 2, + .max_nof_rbs = 2, + .max_code_rate = srsran::max_pucch_code_rate::dot_15, + .f2_intraslot_freq_hopping = true}, + pucch_gen_params{.nof_res_f0 = 39, + .nof_res_f2 = 19, + .f0_nof_symbols = 1, + .f0_intraslot_freq_hopping = false, + .f2_nof_symbols = 2, + .max_nof_rbs = 1, + .max_code_rate = srsran::max_pucch_code_rate::dot_15, + .f2_intraslot_freq_hopping = true}, + pucch_gen_params{.nof_res_f0 = 9, + .nof_res_f2 = 19, + .f0_nof_symbols = 2, + .f0_intraslot_freq_hopping = true, + .f2_nof_symbols = 2, + .max_nof_rbs = 1, + .max_code_rate = srsran::max_pucch_code_rate::dot_15, + .f2_intraslot_freq_hopping = true}, + pucch_gen_params{.nof_res_f0 = 39, + .nof_res_f2 = 25, + .f0_nof_symbols = 2, + .f0_intraslot_freq_hopping = true, + .f2_nof_symbols = 2, + .max_nof_rbs = 1, + .max_code_rate = srsran::max_pucch_code_rate::dot_15, + .f2_intraslot_freq_hopping = false}, + pucch_gen_params{.nof_res_f0 = 36, + .nof_res_f2 = 27, + .f0_nof_symbols = 1, + .f0_intraslot_freq_hopping = false, + .f2_nof_symbols = 2, + .max_nof_rbs = 7, + .max_code_rate = srsran::max_pucch_code_rate::dot_15, + .f2_intraslot_freq_hopping = true}, + pucch_gen_params{.nof_res_f1 = 34, + .nof_res_f2 = 10, + .f0_nof_symbols = 1, + .f0_intraslot_freq_hopping = false, + .f2_nof_symbols = 1, + .max_nof_rbs = 7, + .max_payload_bits = 11, + .max_code_rate = srsran::max_pucch_code_rate::dot_08, + .f2_intraslot_freq_hopping = false}, + pucch_gen_params{.nof_res_f0 = 15, + .nof_res_f2 = 10, + .f0_nof_symbols = 1, + .f0_intraslot_freq_hopping = false, + .f2_nof_symbols = 2, + .max_nof_rbs = 7, + .max_payload_bits = 11, + .max_code_rate = srsran::max_pucch_code_rate::dot_08, + .f2_intraslot_freq_hopping = true})); + +/////////////////// class test_ue_pucch_config_builder : public ::testing::TestWithParam { protected: test_ue_pucch_config_builder() : + nof_f0_res_harq_per_ue(GetParam().nof_res_f0_harq), nof_f1_res_harq_per_ue(GetParam().nof_res_f1_harq), nof_f2_res_harq_per_ue(GetParam().nof_res_f2_harq), nof_harq_cfg_per_ue(GetParam().nof_harq_cfg), nof_sr_res_per_cell(GetParam().nof_res_sr), nof_csi_res_per_cell(GetParam().nof_res_csi), + f0_params(pucch_f0_params{.nof_symbols = 2, .intraslot_freq_hopping = false}), f1_params(pucch_f1_params{.nof_cyc_shifts = nof_cyclic_shifts::no_cyclic_shift, .occ_supported = false, .nof_symbols = 14, @@ -598,12 +614,15 @@ class test_ue_pucch_config_builder : public ::testing::TestWithParam 0; + const unsigned nof_f0_f1_res_harq_per_ue = has_f0_res ? nof_f0_res_harq_per_ue : nof_f1_res_harq_per_ue; + // Check the number of resources in the PUCCH resource list is correct. bool test_result = pucch_cfg.pucch_res_list.size() == - (nof_f1_res_harq_per_ue + nof_f2_res_harq_per_ue + nof_sr_res_per_ue + nof_csi_res_per_ue); + (nof_f0_f1_res_harq_per_ue + nof_f2_res_harq_per_ue + nof_sr_res_per_ue + nof_csi_res_per_ue); // Check the number of PUCCH F1 resources in the PUCCH resource sets is correct. - test_result = test_result && pucch_cfg.pucch_res_set[0].pucch_res_id_list.size() == nof_f1_res_harq_per_ue; + test_result = test_result && pucch_cfg.pucch_res_set[0].pucch_res_id_list.size() == nof_f0_f1_res_harq_per_ue; test_result = test_result && pucch_cfg.pucch_res_set[1].pucch_res_id_list.size() == nof_f2_res_harq_per_ue; // Helper to retrives a given PUCCH resource given its ID from the PUCCH resource list. @@ -615,13 +634,13 @@ class test_ue_pucch_config_builder : public ::testing::TestWithParam= nof_f1_res_harq_per_ue * harq_cfg_idx and - res_idx.cell_res_id < nof_f1_res_harq_per_ue * (harq_cfg_idx + 1); + const bool pucch_cell_res_id_test = res_idx.cell_res_id >= nof_f0_f1_res_harq_per_ue * harq_cfg_idx and + res_idx.cell_res_id < nof_f0_f1_res_harq_per_ue * (harq_cfg_idx + 1); // The PUCCH resource ID for the ASN1 message for PUCCH F1 resources is expected to be from 0 to - // nof_f1_res_harq_per_ue for all UEs. - const bool pucch_ue_res_id_test = res_idx.ue_res_id < nof_f1_res_harq_per_ue; + // nof_f0_f1_res_harq_per_ue for all UEs. + const bool pucch_ue_res_id_test = res_idx.ue_res_id < nof_f0_f1_res_harq_per_ue; const bool pucch_ue_cell_res_id_test = - res_idx.cell_res_id - nof_f1_res_harq_per_ue * harq_cfg_idx == res_idx.ue_res_id; + res_idx.cell_res_id - nof_f0_f1_res_harq_per_ue * harq_cfg_idx == res_idx.ue_res_id; // Make sure the PUCCH resource ID in the set has a corresponding resource in the PUCCH resource list. auto* res_it = get_pucch_resource_with_id(res_idx); test_result = test_result and pucch_cell_res_id_test and pucch_ue_res_id_test and pucch_ue_cell_res_id_test and @@ -630,19 +649,19 @@ class test_ue_pucch_config_builder : public ::testing::TestWithParam= nof_f1_res_harq_per_ue * nof_harq_cfg_per_ue + nof_sr_res_per_cell + + res_idx.cell_res_id >= nof_f0_f1_res_harq_per_ue * nof_harq_cfg_per_ue + nof_sr_res_per_cell + nof_f2_res_harq_per_ue * harq_cfg_idx and - res_idx.cell_res_id < nof_f1_res_harq_per_ue * nof_harq_cfg_per_ue + nof_sr_res_per_cell + + res_idx.cell_res_id < nof_f0_f1_res_harq_per_ue * nof_harq_cfg_per_ue + nof_sr_res_per_cell + nof_f2_res_harq_per_ue * (harq_cfg_idx + 1); // The PUCCH resource ID for the ASN1 message for PUCCH F2 resources is expected to be from - // (nof_f1_res_harq_per_ue + 1) to (nof_f1_res_harq_per_ue + 1 + nof_f2_res_harq_per_ue) for all UEs. - const bool pucch_ue_res_id_test = res_idx.ue_res_id >= nof_f1_res_harq_per_ue + 1 and - res_idx.ue_res_id < nof_f1_res_harq_per_ue + 1 + nof_f2_res_harq_per_ue; + // (nof_f0_f1_res_harq_per_ue + 1) to (nof_f0_f1_res_harq_per_ue + 1 + nof_f2_res_harq_per_ue) for all UEs. + const bool pucch_ue_res_id_test = res_idx.ue_res_id >= nof_f0_f1_res_harq_per_ue + 1 and + res_idx.ue_res_id < nof_f0_f1_res_harq_per_ue + 1 + nof_f2_res_harq_per_ue; // Check if the PUCCH cell resourece ID is set correspondingly to the PUCCH UE resource ID. const bool pucch_ue_cell_res_id_test = - res_idx.cell_res_id - (nof_f1_res_harq_per_ue * nof_harq_cfg_per_ue + nof_sr_res_per_cell + + res_idx.cell_res_id - (nof_f0_f1_res_harq_per_ue * nof_harq_cfg_per_ue + nof_sr_res_per_cell + nof_f2_res_harq_per_ue * harq_cfg_idx) == - res_idx.ue_res_id - (nof_f1_res_harq_per_ue + 1); + res_idx.ue_res_id - (nof_f0_f1_res_harq_per_ue + 1); // Make sure the PUCCH resource ID in the set has a corresponding resource in the PUCCH resource list. auto* res_it = get_pucch_resource_with_id(res_idx); test_result = test_result and pucch_cell_res_id_test and pucch_ue_res_id_test and pucch_ue_cell_res_id_test and @@ -650,10 +669,11 @@ class test_ue_pucch_config_builder : public ::testing::TestWithParam res_list = - generate_cell_pucch_res_list(nof_f1_res, nof_f2_res, f1_params, f2_params, bwp_size); + std::vector res_list; + + ASSERT_FALSE(nof_f0_res_harq_per_ue != 0 and nof_f1_res_harq_per_ue != 0) + << "PUCCH Format 0 and Format 1 resources cannot be used together"; + + if (nof_f0_res_harq_per_ue != 0) { + res_list = generate_cell_pucch_res_list(nof_f0_res, nof_f2_res, f0_params, f2_params, bwp_size); + } else { + res_list = generate_cell_pucch_res_list(nof_f1_res, nof_f2_res, f1_params, f2_params, bwp_size); + } const unsigned harq_idx_cfg = test_rgen::uniform_int(0, nof_harq_cfg_per_ue - 1); const unsigned sr_idx_cfg = test_rgen::uniform_int(0, nof_sr_res_per_cell - 1); @@ -731,7 +762,7 @@ TEST_P(test_ue_pucch_config_builder, test_validator_too_many_resources) harq_idx_cfg, sr_idx_cfg, csi_idx_cfg, - nof_f1_res_harq_per_ue, + nof_f0_res_harq_per_ue != 0 ? nof_f0_res_harq_per_ue : nof_f1_res_harq_per_ue, nof_f2_res_harq_per_ue, nof_harq_cfg_per_ue, nof_sr_res_per_cell, @@ -743,18 +774,27 @@ TEST_P(test_ue_pucch_config_builder, test_validator_too_many_resources) INSTANTIATE_TEST_SUITE_P(ue_pucch_config_builder, test_ue_pucch_config_builder, // clang-format off - // nof: f1 | f2 | harq | sr | csi - // nof: f1 | f2 | cfg | sr | csi + // nof: f0 | f1 | f2 | harq | sr | csi + // nof: f0 | f1 | f2 | cfg | sr | csi ::testing::Values( - pucch_cfg_builder_params{ 3, 6, 1, 2, 1 }, - pucch_cfg_builder_params{ 7, 3, 1, 1, 1 }, - pucch_cfg_builder_params{ 8, 8, 1, 4, 1 }, - pucch_cfg_builder_params{ 1, 1, 1, 1, 1 }, - pucch_cfg_builder_params{ 7, 7, 1, 3, 1 }, - pucch_cfg_builder_params{ 8, 8, 4, 4, 4 }, - pucch_cfg_builder_params{ 5, 2, 10, 2, 7 }, - pucch_cfg_builder_params{ 2, 7, 3, 7, 3 }, - pucch_cfg_builder_params{ 6, 4, 5, 6, 2 } + pucch_cfg_builder_params{ 0, 3, 6, 1, 2, 1 }, + pucch_cfg_builder_params{ 0, 7, 3, 1, 1, 1 }, + pucch_cfg_builder_params{ 0, 8, 8, 1, 4, 1 }, + pucch_cfg_builder_params{ 0, 1, 1, 1, 1, 1 }, + pucch_cfg_builder_params{ 0, 7, 7, 1, 3, 1 }, + pucch_cfg_builder_params{ 0, 8, 8, 4, 4, 4 }, + pucch_cfg_builder_params{ 0, 5, 2, 10, 2, 7 }, + pucch_cfg_builder_params{ 0, 2, 7, 3, 7, 3 }, + pucch_cfg_builder_params{ 0, 6, 4, 5, 6, 2 }, + pucch_cfg_builder_params{ 3, 0, 6, 1, 2, 1 }, + pucch_cfg_builder_params{ 7, 0, 3, 1, 1, 1 }, + pucch_cfg_builder_params{ 8, 0, 8, 1, 4, 1 }, + pucch_cfg_builder_params{ 1, 0, 1, 1, 1, 1 }, + pucch_cfg_builder_params{ 7, 0, 7, 1, 3, 1 }, + pucch_cfg_builder_params{ 8, 0, 8, 4, 4, 4 }, + pucch_cfg_builder_params{ 5, 0, 2, 10, 2, 7 }, + pucch_cfg_builder_params{ 2, 0, 7, 3, 7, 3 }, + pucch_cfg_builder_params{ 6, 0, 4, 5, 6, 2 } ) // clang-format on ); diff --git a/tests/unittests/ngap/ngap_nas_message_test.cpp b/tests/unittests/ngap/ngap_nas_message_test.cpp index 65b183e625..1ba36849b8 100644 --- a/tests/unittests/ngap/ngap_nas_message_test.cpp +++ b/tests/unittests/ngap/ngap_nas_message_test.cpp @@ -52,6 +52,12 @@ class ngap_nas_message_routine_test : public ngap_test bool was_dl_nas_transport_dropped(const test_ue& ue) const { return ue.rrc_ue_dl_nas_handler.last_nas_pdu.empty(); } + bool was_ue_radio_capability_info_indication_sent() const + { + return n2_gw.last_ngap_msgs.back().pdu.init_msg().value.type() == + asn1::ngap::ngap_elem_procs_o::init_msg_c::types_opts::ue_radio_cap_info_ind; + } + bool was_ul_nas_transport_forwarded() const { return n2_gw.last_ngap_msgs.back().pdu.init_msg().value.type() == @@ -137,6 +143,29 @@ TEST_F(ngap_nas_message_routine_test, when_no_ue_present_dl_nas_transport_is_dro ASSERT_TRUE(was_error_indication_sent()); } +/// Test DL NAS transport handling +TEST_F(ngap_nas_message_routine_test, + when_dl_nas_transport_contains_ue_cap_info_request_then_ue_radio_cap_info_indication_is_sent) +{ + // Test preamble + ue_index_t ue_index = this->start_procedure(); + + auto& ue = test_ues.at(ue_index); + ue.amf_ue_id = uint_to_amf_ue_id( + test_rgen::uniform_int(amf_ue_id_to_uint(amf_ue_id_t::min), amf_ue_id_to_uint(amf_ue_id_t::max))); + + // Inject DL NAS transport message from AMF + ngap_message dl_nas_transport = + generate_downlink_nas_transport_message_with_ue_cap_info_request(ue.amf_ue_id.value(), ue.ran_ue_id.value()); + ngap->handle_message(dl_nas_transport); + + // Check that RRC notifier was called + ASSERT_TRUE(was_dl_nas_transport_forwarded(ue)); + + // Check that UE Radio Capability Info Indication was sent to AMF + ASSERT_TRUE(was_ue_radio_capability_info_indication_sent()); +} + /// Test UL NAS transport handling TEST_F(ngap_nas_message_routine_test, when_ue_present_and_amf_set_ul_nas_transport_is_forwared) { diff --git a/tests/unittests/ngap/ngap_test_helpers.cpp b/tests/unittests/ngap/ngap_test_helpers.cpp index a2b476c6c5..0e61c51e9b 100644 --- a/tests/unittests/ngap/ngap_test_helpers.cpp +++ b/tests/unittests/ngap/ngap_test_helpers.cpp @@ -77,6 +77,7 @@ ue_index_t ngap_test::create_ue(rnti_t rnti) test_ue& new_test_ue = test_ues.at(ue_index); ue_mng.get_ngap_rrc_ue_adapter(ue_index).connect_rrc_ue(new_test_ue.rrc_ue_dl_nas_handler, + new_test_ue.rrc_ue_radio_access_cap_handler, new_test_ue.rrc_ue_ho_prep_handler); // generate and inject valid initial ue message @@ -104,6 +105,7 @@ ue_index_t ngap_test::create_ue_without_init_ue_message(rnti_t rnti) test_ue& new_test_ue = test_ues.at(ue_index); ue_mng.get_ngap_rrc_ue_adapter(ue_index).connect_rrc_ue(new_test_ue.rrc_ue_dl_nas_handler, + new_test_ue.rrc_ue_radio_access_cap_handler, new_test_ue.rrc_ue_ho_prep_handler); return ue_index; diff --git a/tests/unittests/ngap/ngap_test_helpers.h b/tests/unittests/ngap/ngap_test_helpers.h index c544ce0763..9ebd67dc97 100644 --- a/tests/unittests/ngap/ngap_test_helpers.h +++ b/tests/unittests/ngap/ngap_test_helpers.h @@ -48,8 +48,9 @@ class ngap_test : public ::testing::Test std::optional amf_ue_id; std::optional ran_ue_id; - dummy_rrc_dl_nas_message_handler rrc_ue_dl_nas_handler; - dummy_rrc_ue_handover_preparation_handler rrc_ue_ho_prep_handler; + dummy_rrc_dl_nas_message_handler rrc_ue_dl_nas_handler; + dummy_rrc_ue_radio_access_capability_handler rrc_ue_radio_access_cap_handler; + dummy_rrc_ue_handover_preparation_handler rrc_ue_ho_prep_handler; }; ngap_test(); diff --git a/tests/unittests/ngap/ngap_test_messages.cpp b/tests/unittests/ngap/ngap_test_messages.cpp index cdfa53bbc9..cce3ca106a 100644 --- a/tests/unittests/ngap/ngap_test_messages.cpp +++ b/tests/unittests/ngap/ngap_test_messages.cpp @@ -193,6 +193,19 @@ ngap_message srsran::srs_cu_cp::generate_downlink_nas_transport_message(amf_ue_i return dl_nas_transport; } +ngap_message srsran::srs_cu_cp::generate_downlink_nas_transport_message_with_ue_cap_info_request(amf_ue_id_t amf_ue_id, + ran_ue_id_t ran_ue_id, + byte_buffer nas_pdu) +{ + ngap_message dl_nas_transport = generate_downlink_nas_transport_message(amf_ue_id, ran_ue_id, std::move(nas_pdu)); + + auto& dl_nas_transport_msg = dl_nas_transport.pdu.init_msg().value.dl_nas_transport(); + dl_nas_transport_msg->ue_cap_info_request_present = true; + dl_nas_transport_msg->ue_cap_info_request = ue_cap_info_request_opts::options::requested; + + return dl_nas_transport; +} + cu_cp_ul_nas_transport srsran::srs_cu_cp::generate_ul_nas_transport_message(ue_index_t ue_index) { cu_cp_ul_nas_transport ul_nas_transport = {}; diff --git a/tests/unittests/ngap/ngap_test_messages.h b/tests/unittests/ngap/ngap_test_messages.h index e94fd871f8..c488ec1a1a 100644 --- a/tests/unittests/ngap/ngap_test_messages.h +++ b/tests/unittests/ngap/ngap_test_messages.h @@ -116,6 +116,11 @@ cu_cp_initial_ue_message generate_initial_ue_message(ue_index_t ue_index); ngap_message generate_downlink_nas_transport_message(amf_ue_id_t amf_ue_id, ran_ue_id_t ran_ue_id, byte_buffer nas_pdu = {}); +/// \brief Generate a dummy DL NAS Transport Message with UE Cap Info Request. +ngap_message generate_downlink_nas_transport_message_with_ue_cap_info_request(amf_ue_id_t amf_ue_id, + ran_ue_id_t ran_ue_id, + byte_buffer nas_pdu = {}); + /// \brief Generate a dummy UL NAS Transport Message. cu_cp_ul_nas_transport generate_ul_nas_transport_message(ue_index_t ue_index); diff --git a/tests/unittests/ngap/test_helpers.h b/tests/unittests/ngap/test_helpers.h index 9652c4190b..5944dfe053 100644 --- a/tests/unittests/ngap/test_helpers.h +++ b/tests/unittests/ngap/test_helpers.h @@ -134,6 +134,8 @@ class dummy_ngap_rrc_ue_notifier : public ngap_rrc_ue_pdu_notifier, public ngap_ logger.info("Received a NAS PDU"); } + byte_buffer on_ue_radio_access_cap_info_required() override { return make_byte_buffer("deadbeef").value(); } + byte_buffer on_handover_preparation_message_required() override { return ho_preparation_message.copy(); } void set_ho_preparation_message(byte_buffer ho_preparation_message_) @@ -410,6 +412,17 @@ class dummy_rrc_dl_nas_message_handler : public rrc_dl_nas_message_handler srslog::basic_logger& logger; }; +class dummy_rrc_ue_radio_access_capability_handler : public rrc_ue_radio_access_capability_handler +{ +public: + dummy_rrc_ue_radio_access_capability_handler() : logger(srslog::fetch_basic_logger("TEST")){}; + + byte_buffer get_packed_ue_radio_access_cap_info() const override { return make_byte_buffer("deadbeef").value(); } + +private: + srslog::basic_logger& logger; +}; + class dummy_rrc_ue_handover_preparation_handler : public rrc_ue_handover_preparation_handler { public: diff --git a/tests/unittests/ofh/transmitter/ofh_uplink_request_handler_impl_test.cpp b/tests/unittests/ofh/transmitter/ofh_uplink_request_handler_impl_test.cpp index bd52e5c92b..0aacbb56fa 100644 --- a/tests/unittests/ofh/transmitter/ofh_uplink_request_handler_impl_test.cpp +++ b/tests/unittests/ofh/transmitter/ofh_uplink_request_handler_impl_test.cpp @@ -89,7 +89,7 @@ class resource_grid_dummy : public resource_grid { public: void - map(const re_buffer_reader& input, const re_pattern& pattern, const precoding_configuration& precoding) override + map(const re_buffer_reader<>& input, const re_pattern& pattern, const precoding_configuration& precoding) override { } diff --git a/tests/unittests/phy/generic_functions/precoding/channel_precoder_test.cpp b/tests/unittests/phy/generic_functions/precoding/channel_precoder_test.cpp index df10af59b1..968b3cdb34 100644 --- a/tests/unittests/phy/generic_functions/precoding/channel_precoder_test.cpp +++ b/tests/unittests/phy/generic_functions/precoding/channel_precoder_test.cpp @@ -87,7 +87,7 @@ class PrecodingFixture : public ::testing::TestWithParam } // Generates and returns random RE values, as many as the specified number of layers and RE. - const re_buffer_reader& generate_random_data(unsigned nof_layers, unsigned nof_re) + const re_buffer_reader<>& generate_random_data(unsigned nof_layers, unsigned nof_re) { // Resize buffer. random_data.resize(nof_layers, nof_re); @@ -117,8 +117,8 @@ class PrecodingFixture : public ::testing::TestWithParam } // Generates the golden RE sequence, with the dimensions specified by the input buffer and precoding matrix. - const re_buffer_reader& generate_golden(const re_buffer_reader& input, - const precoding_weight_matrix& precoding_matrix) + const re_buffer_reader<>& generate_golden(const re_buffer_reader<>& input, + const precoding_weight_matrix& precoding_matrix) { // Get dimensions. unsigned nof_re = input.get_nof_re(); @@ -152,7 +152,7 @@ class PrecodingFixture : public ::testing::TestWithParam } // Generates the golden RE sequence, with the dimensions specified by the input buffer and precoding matrix. - const re_buffer_reader& generate_golden(span input, const precoding_weight_matrix& precoding_matrix) + const re_buffer_reader<>& generate_golden(span input, const precoding_weight_matrix& precoding_matrix) { // Get dimensions. unsigned nof_layers = precoding_matrix.get_nof_layers(); @@ -239,13 +239,13 @@ TEST_P(PrecodingFixture, RandomWeightsCft) nof_re); for (unsigned nof_layers = 1; nof_layers <= nof_ports; ++nof_layers) { // Generate random RE arranged by layers. - const re_buffer_reader& input_data = generate_random_data(nof_layers, nof_re); + const re_buffer_reader<>& input_data = generate_random_data(nof_layers, nof_re); // Create a random precoding configuration precoding_weight_matrix precoding_matrix = generate_random_precoding(nof_layers, nof_ports); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, precoding_matrix); + const re_buffer_reader<>& golden = generate_golden(input_data, precoding_matrix); // Apply precoding. precoder->apply_precoding(precoding_buffer, input_data, precoding_matrix); @@ -279,7 +279,7 @@ TEST_P(PrecodingFixture, RandomWeightsCi8) precoding_weight_matrix precoding_matrix = generate_random_precoding(nof_layers, nof_ports); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, precoding_matrix); + const re_buffer_reader<>& golden = generate_golden(input_data, precoding_matrix); // Apply precoding. precoder->apply_layer_map_and_precoding(precoding_buffer, input_data, precoding_matrix); diff --git a/tests/unittests/phy/lower/modulation/ofdm_modulator_unittest.cpp b/tests/unittests/phy/lower/modulation/ofdm_modulator_unittest.cpp index 669b847519..3716c2fdae 100644 --- a/tests/unittests/phy/lower/modulation/ofdm_modulator_unittest.cpp +++ b/tests/unittests/phy/lower/modulation/ofdm_modulator_unittest.cpp @@ -109,7 +109,7 @@ int main() // Generate random data in the resource grid. unsigned nsymb = get_nsymb_per_slot(cp); - resource_grid_reader_spy rg(1, nsymb, nsubc); + resource_grid_reader_spy rg; for (unsigned symbol_idx = 0; symbol_idx != nsymb; ++symbol_idx) { for (unsigned subc_idx = 0; subc_idx != nsubc; ++subc_idx) { resource_grid_reader_spy::expected_entry_t entry = {}; diff --git a/tests/unittests/phy/lower/modulation/ofdm_modulator_vectortest.cpp b/tests/unittests/phy/lower/modulation/ofdm_modulator_vectortest.cpp index dc3d230764..2610b6a008 100644 --- a/tests/unittests/phy/lower/modulation/ofdm_modulator_vectortest.cpp +++ b/tests/unittests/phy/lower/modulation/ofdm_modulator_vectortest.cpp @@ -63,7 +63,7 @@ int main() unsigned input_offset = 0; // Map the read data into a reader_spy structure. unsigned nsymb = get_nsymb_per_slot(test_case.test_config.config.cp); - resource_grid_reader_spy rg(1, nsymb, nsubc); + resource_grid_reader_spy rg; for (unsigned symbol_idx = 0; symbol_idx != nsymb; ++symbol_idx) { for (unsigned subc_idx = 0; subc_idx != nsubc; ++subc_idx) { resource_grid_reader_spy::expected_entry_t entry = {}; diff --git a/tests/unittests/phy/lower/processors/downlink/pdxch/pdxch_processor_test.cpp b/tests/unittests/phy/lower/processors/downlink/pdxch/pdxch_processor_test.cpp index a22bdab5ea..d2b9d9efcd 100644 --- a/tests/unittests/phy/lower/processors/downlink/pdxch/pdxch_processor_test.cpp +++ b/tests/unittests/phy/lower/processors/downlink/pdxch/pdxch_processor_test.cpp @@ -287,7 +287,7 @@ TEST_P(LowerPhyDownlinkProcessorFixture, FlowFloodRequest) pdxch_processor_notifier_spy pdxch_proc_notifier_spy; pdxch_proc->connect(pdxch_proc_notifier_spy); - resource_grid_reader_spy rg_spy(nof_tx_ports, 1, 1); + resource_grid_reader_spy rg_spy; // Add a single resource grid entry per port. This makes the grid non-empty on all ports. for (unsigned i_port = 0; i_port != nof_tx_ports; ++i_port) { diff --git a/tests/unittests/phy/support/resource_grid_mapper_test.cpp b/tests/unittests/phy/support/resource_grid_mapper_test.cpp index cc44a6e229..a823147720 100644 --- a/tests/unittests/phy/support/resource_grid_mapper_test.cpp +++ b/tests/unittests/phy/support/resource_grid_mapper_test.cpp @@ -74,7 +74,7 @@ static bool operator==(span lhs, span rhs) } // namespace srsran // Asserts that the contents of the resource grid match the golden symbols for the give allocation pattern. -static void assert_grid(const re_buffer_reader& golden, +static void assert_grid(const re_buffer_reader<>& golden, const re_pattern_list& allocation, const re_pattern_list& reserved, const resource_grid_reader& grid, @@ -117,7 +117,7 @@ static void assert_grid(const re_buffer_reader& golden, } } -static void assert_grid(const re_buffer_reader& golden, +static void assert_grid(const re_buffer_reader<>& golden, const re_pattern_list& allocation, const resource_grid_reader& grid, unsigned nof_grid_rb) @@ -154,7 +154,7 @@ class ResourceGridMapperFixture : public ::testing::TestWithParam& generate_random_data(unsigned nof_layers, unsigned nof_re) { // Resize buffer. random_data.resize(nof_layers, nof_re); @@ -212,10 +212,10 @@ class ResourceGridMapperFixture : public ::testing::TestWithParam& generate_golden(const re_buffer_reader<>& input, + const re_pattern_list& allocation, + const precoding_configuration& configuration, + const re_pattern_list& reserved) { // Get dimensions. unsigned nof_layers = configuration.get_nof_layers(); @@ -282,9 +282,9 @@ class ResourceGridMapperFixture : public ::testing::TestWithParam& generate_golden(const re_buffer_reader<>& input, + const re_pattern_list& allocation, + const precoding_configuration& configuration) { return generate_golden(input, allocation, configuration, re_pattern_list()); } @@ -349,7 +349,7 @@ TEST_F(ResourceGridMapperFixture, SinglePort) unsigned nof_data_re = allocation.get_inclusion_count(0, MAX_NSYMB_PER_SLOT, ~bounded_bitset(MAX_RB)); // Generate random RE arranged by layers. - const re_buffer_reader& input_data = generate_random_data(nof_layers, nof_data_re); + const re_buffer_reader<>& input_data = generate_random_data(nof_layers, nof_data_re); // Get the resource grid mapper. resource_grid_mapper& mapper = grid->get_mapper(); @@ -358,7 +358,7 @@ TEST_F(ResourceGridMapperFixture, SinglePort) mapper.map(input_data, allocation.get_re_patterns().front(), precoding_config); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, allocation, precoding_config); + const re_buffer_reader<>& golden = generate_golden(input_data, allocation, precoding_config); // Assert resource grid contents. assert_grid(golden, allocation, grid->get_reader(), MAX_RB); @@ -389,7 +389,7 @@ TEST_F(ResourceGridMapperFixture, OneLayerToOnePort) unsigned nof_data_re = allocation.get_inclusion_count(0, MAX_NSYMB_PER_SLOT, ~bounded_bitset(MAX_RB)); // Generate random RE arranged by layers. - const re_buffer_reader& input_data = generate_random_data(nof_layers, nof_data_re); + const re_buffer_reader<>& input_data = generate_random_data(nof_layers, nof_data_re); // Get the resource grid mapper. resource_grid_mapper& mapper = grid->get_mapper(); @@ -398,7 +398,7 @@ TEST_F(ResourceGridMapperFixture, OneLayerToOnePort) mapper.map(input_data, allocation.get_re_patterns().front(), precoding_config); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, allocation, precoding_config); + const re_buffer_reader<>& golden = generate_golden(input_data, allocation, precoding_config); // Assert resource grid contents. assert_grid(golden, allocation, grid->get_reader(), MAX_RB); @@ -427,7 +427,7 @@ TEST_F(ResourceGridMapperFixture, OneLayerAllPorts) unsigned nof_data_re = allocation.get_inclusion_count(0, MAX_NSYMB_PER_SLOT, ~bounded_bitset(MAX_RB)); // Generate random RE arranged by layers. - const re_buffer_reader& input_data = generate_random_data(nof_layers, nof_data_re); + const re_buffer_reader<>& input_data = generate_random_data(nof_layers, nof_data_re); // Get the resource grid mapper. resource_grid_mapper& mapper = grid->get_mapper(); @@ -436,7 +436,7 @@ TEST_F(ResourceGridMapperFixture, OneLayerAllPorts) mapper.map(input_data, allocation.get_re_patterns().front(), precoding_config); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, allocation, precoding_config); + const re_buffer_reader<>& golden = generate_golden(input_data, allocation, precoding_config); // Assert resource grid contents. assert_grid(golden, allocation, grid->get_reader(), MAX_RB); @@ -461,7 +461,7 @@ TEST_F(ResourceGridMapperFixture, Identity) unsigned nof_data_re = allocation.get_inclusion_count(0, MAX_NSYMB_PER_SLOT, ~bounded_bitset(MAX_RB)); // Generate random RE arranged by layers. - const re_buffer_reader& input_data = generate_random_data(nof_streams, nof_data_re); + const re_buffer_reader<>& input_data = generate_random_data(nof_streams, nof_data_re); // Get the resource grid mapper. resource_grid_mapper& mapper = grid->get_mapper(); @@ -470,7 +470,7 @@ TEST_F(ResourceGridMapperFixture, Identity) mapper.map(input_data, allocation.get_re_patterns().front(), precoding_config); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, allocation, precoding_config); + const re_buffer_reader<>& golden = generate_golden(input_data, allocation, precoding_config); // Assert resource grid contents. assert_grid(golden, allocation, grid->get_reader(), MAX_RB); @@ -501,7 +501,7 @@ TEST_F(ResourceGridMapperFixture, OneLayerTwoPorts) unsigned nof_data_re = allocation.get_inclusion_count(0, MAX_NSYMB_PER_SLOT, ~bounded_bitset(MAX_RB)); // Generate random RE arranged by layers. - const re_buffer_reader& input_data = generate_random_data(nof_layers, nof_data_re); + const re_buffer_reader<>& input_data = generate_random_data(nof_layers, nof_data_re); // Get the resource grid mapper. resource_grid_mapper& mapper = grid->get_mapper(); @@ -510,7 +510,7 @@ TEST_F(ResourceGridMapperFixture, OneLayerTwoPorts) mapper.map(input_data, allocation.get_re_patterns().front(), precoding_config); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, allocation, precoding_config); + const re_buffer_reader<>& golden = generate_golden(input_data, allocation, precoding_config); // Assert resource grid contents. assert_grid(golden, allocation, grid->get_reader(), MAX_RB); @@ -541,7 +541,7 @@ TEST_F(ResourceGridMapperFixture, TwoLayerTwoPorts) unsigned nof_data_re = allocation.get_inclusion_count(0, MAX_NSYMB_PER_SLOT, ~bounded_bitset(MAX_RB)); // Generate random RE arranged by layers. - const re_buffer_reader& input_data = generate_random_data(nof_layers, nof_data_re); + const re_buffer_reader<>& input_data = generate_random_data(nof_layers, nof_data_re); // Get the resource grid mapper. resource_grid_mapper& mapper = grid->get_mapper(); @@ -550,7 +550,7 @@ TEST_F(ResourceGridMapperFixture, TwoLayerTwoPorts) mapper.map(input_data, allocation.get_re_patterns().front(), precoding_config); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, allocation, precoding_config); + const re_buffer_reader<>& golden = generate_golden(input_data, allocation, precoding_config); // Assert resource grid contents. assert_grid(golden, allocation, grid->get_reader(), MAX_RB); @@ -583,7 +583,7 @@ TEST_F(ResourceGridMapperFixture, OneLayerFourPorts) unsigned nof_data_re = allocation.get_inclusion_count(0, MAX_NSYMB_PER_SLOT, ~bounded_bitset(MAX_RB)); // Generate random RE arranged by layers. - const re_buffer_reader& input_data = generate_random_data(nof_layers, nof_data_re); + const re_buffer_reader<>& input_data = generate_random_data(nof_layers, nof_data_re); // Get the resource grid mapper. resource_grid_mapper& mapper = grid->get_mapper(); @@ -592,7 +592,7 @@ TEST_F(ResourceGridMapperFixture, OneLayerFourPorts) mapper.map(input_data, allocation.get_re_patterns().front(), precoding_config); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, allocation, precoding_config); + const re_buffer_reader<>& golden = generate_golden(input_data, allocation, precoding_config); // Assert resource grid contents. assert_grid(golden, allocation, grid->get_reader(), MAX_RB); @@ -627,7 +627,7 @@ TEST_F(ResourceGridMapperFixture, TwoLayerFourPorts) unsigned nof_data_re = allocation.get_inclusion_count(0, MAX_NSYMB_PER_SLOT, ~bounded_bitset(MAX_RB)); // Generate random RE arranged by layers. - const re_buffer_reader& input_data = generate_random_data(nof_layers, nof_data_re); + const re_buffer_reader<>& input_data = generate_random_data(nof_layers, nof_data_re); // Get the resource grid mapper. resource_grid_mapper& mapper = grid->get_mapper(); @@ -636,7 +636,7 @@ TEST_F(ResourceGridMapperFixture, TwoLayerFourPorts) mapper.map(input_data, allocation.get_re_patterns().front(), precoding_config); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, allocation, precoding_config); + const re_buffer_reader<>& golden = generate_golden(input_data, allocation, precoding_config); // Assert resource grid contents. assert_grid(golden, allocation, grid->get_reader(), MAX_RB); @@ -671,7 +671,7 @@ TEST_F(ResourceGridMapperFixture, ThreeLayerFourPorts) unsigned nof_data_re = allocation.get_inclusion_count(0, MAX_NSYMB_PER_SLOT, ~bounded_bitset(MAX_RB)); // Generate random RE arranged by layers. - const re_buffer_reader& input_data = generate_random_data(nof_layers, nof_data_re); + const re_buffer_reader<>& input_data = generate_random_data(nof_layers, nof_data_re); // Get the resource grid mapper. resource_grid_mapper& mapper = grid->get_mapper(); @@ -680,7 +680,7 @@ TEST_F(ResourceGridMapperFixture, ThreeLayerFourPorts) mapper.map(input_data, allocation.get_re_patterns().front(), precoding_config); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, allocation, precoding_config); + const re_buffer_reader<>& golden = generate_golden(input_data, allocation, precoding_config); // Assert resource grid contents. assert_grid(golden, allocation, grid->get_reader(), MAX_RB); @@ -714,7 +714,7 @@ TEST_F(ResourceGridMapperFixture, FourLayerFourPorts) unsigned nof_data_re = allocation.get_inclusion_count(0, MAX_NSYMB_PER_SLOT, ~bounded_bitset(MAX_RB)); // Generate random RE arranged by layers. - const re_buffer_reader& input_data = generate_random_data(nof_layers, nof_data_re); + const re_buffer_reader<>& input_data = generate_random_data(nof_layers, nof_data_re); // Get the resource grid mapper. resource_grid_mapper& mapper = grid->get_mapper(); @@ -723,7 +723,7 @@ TEST_F(ResourceGridMapperFixture, FourLayerFourPorts) mapper.map(input_data, allocation.get_re_patterns().front(), precoding_config); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, allocation, precoding_config); + const re_buffer_reader<>& golden = generate_golden(input_data, allocation, precoding_config); // Assert resource grid contents. assert_grid(golden, allocation, grid->get_reader(), MAX_RB); @@ -766,7 +766,7 @@ TEST_P(ResourceGridMapperFixture, MultiplePrg) unsigned nof_data_re = allocation.get_inclusion_count(0, MAX_NSYMB_PER_SLOT, ~bounded_bitset(MAX_RB)); // Generate random RE arranged by layers. - const re_buffer_reader& input_data = generate_random_data(nof_layers, nof_data_re); + const re_buffer_reader<>& input_data = generate_random_data(nof_layers, nof_data_re); // Get the resource grid mapper. resource_grid_mapper& mapper = grid->get_mapper(); @@ -775,7 +775,7 @@ TEST_P(ResourceGridMapperFixture, MultiplePrg) mapper.map(input_data, allocation.get_re_patterns().front(), precoding_config); // Generate the golden precoded data. - const re_buffer_reader& golden = generate_golden(input_data, allocation, precoding_config); + const re_buffer_reader<>& golden = generate_golden(input_data, allocation, precoding_config); // Assert resource grid contents. assert_grid(golden, allocation, grid->get_reader(), nof_rb); diff --git a/tests/unittests/phy/support/resource_grid_mapper_test_doubles.h b/tests/unittests/phy/support/resource_grid_mapper_test_doubles.h index ffbaba8fb1..759378d484 100644 --- a/tests/unittests/phy/support/resource_grid_mapper_test_doubles.h +++ b/tests/unittests/phy/support/resource_grid_mapper_test_doubles.h @@ -30,7 +30,8 @@ namespace srsran { class resource_grid_mapper_dummy : public resource_grid_mapper { public: - void map(const re_buffer_reader& input, const re_pattern& pattern, const precoding_configuration& precoding) override + void + map(const re_buffer_reader<>& input, const re_pattern& pattern, const precoding_configuration& precoding) override { } diff --git a/tests/unittests/phy/support/resource_grid_test_doubles.h b/tests/unittests/phy/support/resource_grid_test_doubles.h index 488c8588e9..7f4815e207 100644 --- a/tests/unittests/phy/support/resource_grid_test_doubles.h +++ b/tests/unittests/phy/support/resource_grid_test_doubles.h @@ -31,6 +31,7 @@ #include "srsran/ran/cyclic_prefix.h" #include "srsran/srslog/srslog.h" #include "srsran/srsvec/copy.h" +#include "srsran/srsvec/fill.h" #include "srsran/support/error_handling.h" #include "srsran/support/file_vector.h" #include "srsran/support/srsran_assert.h" @@ -267,9 +268,14 @@ class resource_grid_reader_spy : public resource_grid_reader public: using expected_entry_t = resource_grid_writer_spy::expected_entry_t; - resource_grid_reader_spy(unsigned max_ports_ = 0, unsigned max_symb_ = 0, unsigned max_prb_ = 0) : - max_ports(max_ports_), max_symb(max_symb_), max_prb(max_prb_), temp_view(max_prb * NRE) + resource_grid_reader_spy() : max_ports(0), max_symb(0), max_prb(0) {} + + resource_grid_reader_spy(unsigned max_ports_, unsigned max_symb_, unsigned max_prb_) : + max_ports(max_ports_), max_symb(max_symb_), max_prb(max_prb_), grid({max_prb * NRE, max_symb, max_ports}) { + // Fill grid with NAN. + cf_t nan = {std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}; + srsvec::fill(grid.get_data(), to_cbf16(nan)); } unsigned get_nof_ports() const override { return max_ports; } @@ -335,19 +341,7 @@ class resource_grid_reader_spy : public resource_grid_reader span get_view(unsigned port, unsigned l) const override { ++count; - - // Fill temporal view with NAN. - cf_t nan = {std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}; - std::fill(temp_view.begin(), temp_view.end(), to_cbf16(nan)); - - // Write the available 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; - } - } - - return temp_view; + return grid.get_view({l, port}); } void write(span entries_) @@ -362,6 +356,10 @@ class resource_grid_reader_spy : public resource_grid_reader entry_key_t key = {entry.port, entry.symbol, entry.subcarrier}; entries.emplace(key, entry.value); + + if (!grid.empty()) { + grid[{entry.subcarrier, entry.symbol, entry.port}] = entry.value; + } } unsigned get_count() const { return count; } @@ -385,8 +383,8 @@ class resource_grid_reader_spy : public resource_grid_reader /// Stores the resource grid written 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; + /// Actual grid, for the method get_view(). + dynamic_tensor<3, cbf16_t> grid; cbf16_t get(uint8_t port, uint8_t symbol, uint16_t subcarrier) const { @@ -445,7 +443,7 @@ class resource_grid_spy : public resource_grid, private resource_grid_mapper resource_grid_mapper& get_mapper() override { return *this; } - void map(const re_buffer_reader& /* input */, + void map(const re_buffer_reader<>& /* input */, const re_pattern& /* pattern */, const precoding_configuration& /* precoding */) override { @@ -498,7 +496,8 @@ class resource_grid_dummy : public resource_grid, private resource_grid_mapper resource_grid_mapper& get_mapper() override { return *this; } - void map(const re_buffer_reader& input, const re_pattern& pattern, const precoding_configuration& precoding) override + void + map(const re_buffer_reader<>& input, const re_pattern& pattern, const precoding_configuration& precoding) override { failure(); } 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 614c0a04b1..a78697528b 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 @@ -128,8 +128,14 @@ TEST_P(PuschDemodulatorFixture, PuschDemodulatorUnittest) { const test_case_t& test_case = GetParam(); + // Calculate resource grid dimensions. + unsigned nof_rx_ports = + (*std::max_element(test_case.context.config.rx_ports.begin(), test_case.context.config.rx_ports.end())) + 1; + unsigned nof_ofdm_symbols = test_case.context.config.start_symbol_index + test_case.context.config.nof_symbols; + unsigned nof_prb = test_case.context.config.rb_mask.size(); + // Prepare resource grid. - resource_grid_reader_spy grid; + resource_grid_reader_spy grid(nof_rx_ports, nof_ofdm_symbols, nof_prb); grid.write(test_case.symbols.read()); // Read estimated channel from the 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 b5879a3d97..427db8e5c0 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 @@ -352,8 +352,13 @@ TEST_P(PuschProcessorFixture, PuschProcessorVectortest) const test_case_context& context = test_case.context; const pusch_processor::pdu_t& config = context.config; + // Calculate resource grid dimensions. + unsigned nof_ports = (*std::max_element(config.rx_ports.begin(), config.rx_ports.end())) + 1; + unsigned nof_ofdm_symbols = MAX_NSYMB_PER_SLOT; + unsigned nof_prb = config.bwp_start_rb + config.bwp_size_rb; + // Prepare resource grid. - resource_grid_reader_spy grid; + resource_grid_reader_spy grid(nof_ports, nof_ofdm_symbols, nof_prb); grid.write(test_case.grid.read()); // Read expected data. @@ -446,8 +451,13 @@ TEST_P(PuschProcessorFixture, PuschProcessorVectortestZero) std::vector grid_data = test_case.grid.read(); std::for_each(grid_data.begin(), grid_data.end(), [](auto& e) { e.value = 0; }); + // Calculate resource grid dimensions. + unsigned nof_ports = (*std::max_element(config.rx_ports.begin(), config.rx_ports.end())) + 1; + unsigned nof_ofdm_symbols = MAX_NSYMB_PER_SLOT; + unsigned nof_prb = config.bwp_start_rb + config.bwp_size_rb; + // Prepare resource grid. - resource_grid_reader_spy grid; + resource_grid_reader_spy grid(nof_ports, nof_ofdm_symbols, nof_prb); grid.write(grid_data); // Prepare receive data. diff --git a/tests/unittests/phy/upper/equalization/channel_equalizer_test.cpp b/tests/unittests/phy/upper/equalization/channel_equalizer_test.cpp index 3ef251068f..c44950380c 100644 --- a/tests/unittests/phy/upper/equalization/channel_equalizer_test.cpp +++ b/tests/unittests/phy/upper/equalization/channel_equalizer_test.cpp @@ -21,10 +21,14 @@ */ #include "channel_equalizer_test_data.h" +#include "srsran/phy/support/re_buffer.h" +#include "srsran/phy/upper/equalization/dynamic_ch_est_list.h" #include "srsran/phy/upper/equalization/equalization_factories.h" +#include "srsran/srsvec/copy.h" #include "srsran/srsvec/zero.h" #include "fmt/ostream.h" #include +#include using namespace srsran; @@ -89,17 +93,14 @@ namespace { using ChannelEqualizerParams = test_case_t; -using re_dims = channel_equalizer::re_list::dims; -using ch_dims = channel_equalizer::ch_est_list::dims; - class ChannelEqualizerFixture : public ::testing::TestWithParam { protected: - dynamic_tensor(re_dims::nof_dims), cbf16_t, re_dims> rx_symbols; - dynamic_tensor(ch_dims::nof_dims), cbf16_t, ch_dims> test_ch_estimates; - std::vector eq_symbols_expected; - std::vector eq_symbols_actual; - std::vector eq_noise_vars_expected; + dynamic_re_buffer rx_symbols; + dynamic_ch_est_list test_ch_estimates; + std::vector eq_symbols_expected; + std::vector eq_symbols_actual; + std::vector eq_noise_vars_expected; std::vector eq_noise_vars_actual; @@ -108,6 +109,8 @@ class ChannelEqualizerFixture : public ::testing::TestWithParam equalizer_factory; std::unique_ptr test_equalizer; + ChannelEqualizerFixture() : TestWithParam(), rx_symbols(4, 1000) {} + void SetUp() override { const test_case_t& t_case = GetParam(); @@ -148,12 +151,15 @@ class ChannelEqualizerFixture : public ::testing::TestWithParam(rx_symbols_vector).subspan(nof_re * i_port, nof_re)); + } srsvec::copy(test_ch_estimates.get_data(), t_case.ch_estimates.read()); // Prepare noise variance per receive port. @@ -219,9 +225,9 @@ TEST_P(ChannelEqualizerFixture, ChannelEqualizerZeroEst) for (unsigned i_layer = 0; i_layer != nof_layers; ++i_layer) { for (unsigned i_port = 0; i_port != nof_rx_ports; ++i_port) { for (unsigned i_re = 0; i_re < nof_re; i_re += stride) { - test_ch_estimates[{i_re, i_port, i_layer}] = cf_t(); - eq_symbols_expected[nof_layers * i_re + i_layer] = cf_t(); - eq_noise_vars_expected[nof_layers * i_re + i_layer] = std::numeric_limits::infinity(); + test_ch_estimates.get_channel(i_port, i_layer)[i_re] = cf_t(); + eq_symbols_expected[nof_layers * i_re + i_layer] = cf_t(); + eq_noise_vars_expected[nof_layers * i_re + i_layer] = std::numeric_limits::infinity(); } } } @@ -272,9 +278,9 @@ TEST_P(ChannelEqualizerFixture, ChannelEqualizerInfEst) for (unsigned i_layer = 0; i_layer != nof_layers; ++i_layer) { for (unsigned i_port = 0; i_port != nof_rx_ports; ++i_port) { for (unsigned i_re = 0; i_re < nof_re; i_re += stride) { - test_ch_estimates[{i_re, i_port, i_layer}] = std::numeric_limits::infinity(); - eq_symbols_expected[nof_layers * i_re + i_layer] = cf_t(); - eq_noise_vars_expected[nof_layers * i_re + i_layer] = std::numeric_limits::infinity(); + test_ch_estimates.get_channel(i_port, i_layer)[i_re] = std::numeric_limits::infinity(); + eq_symbols_expected[nof_layers * i_re + i_layer] = cf_t(); + eq_noise_vars_expected[nof_layers * i_re + i_layer] = std::numeric_limits::infinity(); } } } @@ -325,9 +331,9 @@ TEST_P(ChannelEqualizerFixture, ChannelEqualizerNanEst) for (unsigned i_layer = 0; i_layer != nof_layers; ++i_layer) { for (unsigned i_port = 0; i_port != nof_rx_ports; ++i_port) { for (unsigned i_re = 0; i_re < nof_re; i_re += stride) { - test_ch_estimates[{i_re, i_port, i_layer}] = std::numeric_limits::quiet_NaN(); - eq_symbols_expected[nof_layers * i_re + i_layer] = cf_t(); - eq_noise_vars_expected[nof_layers * i_re + i_layer] = std::numeric_limits::infinity(); + test_ch_estimates.get_channel(i_port, i_layer)[i_re] = std::numeric_limits::quiet_NaN(); + eq_symbols_expected[nof_layers * i_re + i_layer] = cf_t(); + eq_noise_vars_expected[nof_layers * i_re + i_layer] = std::numeric_limits::infinity(); } } } diff --git a/tests/unittests/phy/upper/log_likelihood_ratio_test.cpp b/tests/unittests/phy/upper/log_likelihood_ratio_test.cpp index 8445224ecb..6ca5b2d44c 100644 --- a/tests/unittests/phy/upper/log_likelihood_ratio_test.cpp +++ b/tests/unittests/phy/upper/log_likelihood_ratio_test.cpp @@ -24,11 +24,19 @@ /// \brief Unit test for the log-likelihood ratio type. /// /// Tests all the operators and functions defined for the type. +#include "../../srslog/testing_helpers.h" + #include "srsran/phy/upper/log_likelihood_ratio.h" #include "srsran/support/srsran_test.h" +#include using namespace srsran; +bool operator==(span lhs, span rhs) +{ + return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} + int main() { log_likelihood_ratio llr0 = 0; @@ -84,4 +92,24 @@ int main() fmt::memory_buffer buf; fmt::format_to(buf, "{}", log_likelihood_ratio(1)); TESTASSERT_EQ(to_string(buf), "1"); + + log_likelihood_ratio low = -32; + log_likelihood_ratio high = 32; + std::mt19937 rgen; + std::uniform_int_distribution llr_clamp_dist(log_likelihood_ratio::min().to_int(), + log_likelihood_ratio::max().to_int()); + std::array clamp_in_data; + std::array clamp_expected_data; + std::array clamp_out_data; + std::generate( + clamp_in_data.begin(), clamp_in_data.end(), [&llr_clamp_dist, &rgen]() { return llr_clamp_dist(rgen); }); + std::transform(clamp_in_data.begin(), + clamp_in_data.end(), + clamp_expected_data.begin(), + [low, high](log_likelihood_ratio value) { return std::clamp(value, low, high); }); + clamp(clamp_out_data, clamp_in_data, low, high); + TESTASSERT_EQ(span(clamp_expected_data), + span(clamp_out_data)); + + return 0; } diff --git a/tests/unittests/ran/cell_identity_test.cpp b/tests/unittests/ran/cell_identity_test.cpp index b60c6479d8..d7a0786cb7 100644 --- a/tests/unittests/ran/cell_identity_test.cpp +++ b/tests/unittests/ran/cell_identity_test.cpp @@ -418,15 +418,15 @@ TEST_F(nci_test, nci_creation_from_valid_string_succeeds) ASSERT_EQ(fmt::format("{:x}", nci), "123456789"); } -TEST_F(nci_test, nci_to_local_cell_id) +TEST_F(nci_test, nci_to_sector_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); + ASSERT_EQ(nci.sector_id(4), 0x01); + ASSERT_EQ(nci.sector_id(8), 0x01); + ASSERT_EQ(nci.sector_id(12), 0xb01); + ASSERT_EQ(nci.sector_id(14), 0x1b01); } TEST_F(nci_test, nci_to_gnb_id) @@ -442,19 +442,19 @@ TEST_F(nci_test, nci_to_gnb_id) ASSERT_EQ(nci.gnb_id(32), gnb_id); } -TEST_F(nci_test, invalid_gnb_id_and_local_cell_id_to_nci_fails) +TEST_F(nci_test, invalid_gnb_id_and_sector_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) +TEST_F(nci_test, valid_gnb_id_and_sector_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); + ASSERT_EQ(nci.sector_id(12), 0xb01); } diff --git a/tests/unittests/scheduler/multiple_ue_sched_test.cpp b/tests/unittests/scheduler/multiple_ue_sched_test.cpp index aebdf6dbe7..02ad123629 100644 --- a/tests/unittests/scheduler/multiple_ue_sched_test.cpp +++ b/tests/unittests/scheduler/multiple_ue_sched_test.cpp @@ -130,10 +130,13 @@ class scheduler_impl_tester test_logger.set_context(current_slot.sfn(), current_slot.slot_index()); bench->sched_res = &bench->sch.slot_indication(current_slot, to_du_cell_index(0)); - pucch_builder_params pucch_basic_params{ - .nof_ue_pucch_f1_res_harq = 8, .nof_ue_pucch_f2_res_harq = 8, .nof_sr_resources = 8, .nof_csi_resources = 8}; - pucch_basic_params.f1_params.nof_cyc_shifts = srsran::nof_cyclic_shifts::twelve; - pucch_basic_params.f1_params.occ_supported = true; + pucch_builder_params pucch_basic_params{.nof_ue_pucch_f0_or_f1_res_harq = 8, + .nof_ue_pucch_f2_res_harq = 8, + .nof_sr_resources = 8, + .nof_csi_resources = 8}; + auto& f1_params = pucch_basic_params.f0_or_f1_params.emplace(); + f1_params.nof_cyc_shifts = srsran::nof_cyclic_shifts::twelve; + f1_params.occ_supported = true; pucch_cfg_builder.setup(bench->cell_cfg, pucch_basic_params); } diff --git a/tests/unittests/scheduler/policy/scheduler_policy_test.cpp b/tests/unittests/scheduler/policy/scheduler_policy_test.cpp index 4e2a5028be..1828921e37 100644 --- a/tests/unittests/scheduler/policy/scheduler_policy_test.cpp +++ b/tests/unittests/scheduler/policy/scheduler_policy_test.cpp @@ -326,6 +326,22 @@ TEST_P(scheduler_policy_test, scheduler_allocates_ues_with_ul_retx_first_than_ue 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_ul_srb_newtx_first_than_ues_with_ul_drb_newtx) +{ + const lcg_id_t drb_lcg_id = uint_to_lcg_id(2); + const lcg_id_t srb_lcg_id = uint_to_lcg_id(0); + ue& u1 = add_ue(make_ue_create_req(to_du_ue_index(0), to_rnti(0x4601), {LCID_MIN_DRB}, drb_lcg_id)); + ue& u2 = add_ue(make_ue_create_req(to_du_ue_index(1), to_rnti(0x4602), {LCID_SRB1}, srb_lcg_id)); + + notify_ul_bsr(u1.ue_index, drb_lcg_id, 1000); + notify_ul_bsr(u2.ue_index, srb_lcg_id, 1000); + + bool pusch_scheduled = run_until([this]() { return not this->res_grid[0].result.ul.puschs.empty(); }); + ASSERT_TRUE(pusch_scheduled); + // UE2 with SRB newTx data is scheduled first. + ASSERT_EQ(this->res_grid[0].result.ul.puschs[0].context.ue_index, u2.ue_index); +} + 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); @@ -375,6 +391,22 @@ TEST_P(scheduler_policy_test, scheduler_allocates_ues_with_dl_retx_first_than_ue ASSERT_EQ(this->res_grid[0].result.dl.ue_grants[0].context.ue_index, ue_with_retx); } +TEST_P(scheduler_policy_test, scheduler_allocates_ues_with_dl_srb_newtx_first_than_ues_with_dl_drb_newtx) +{ + const lcg_id_t drb_lcg_id = uint_to_lcg_id(2); + const lcg_id_t srb_lcg_id = uint_to_lcg_id(0); + ue& u1 = add_ue(make_ue_create_req(to_du_ue_index(0), to_rnti(0x4601), {LCID_MIN_DRB}, drb_lcg_id)); + ue& u2 = add_ue(make_ue_create_req(to_du_ue_index(1), to_rnti(0x4602), {LCID_SRB1}, srb_lcg_id)); + + push_dl_bs(u1.ue_index, LCID_MIN_DRB, 1000); + push_dl_bs(u2.ue_index, LCID_SRB1, 1000); + + bool pdsch_scheduled = run_until([this]() { return not this->res_grid[0].result.dl.ue_grants.empty(); }); + ASSERT_TRUE(pdsch_scheduled); + // UE2 with SRB newTx data is scheduled first. + ASSERT_EQ(this->res_grid[0].result.dl.ue_grants[0].context.ue_index, u2.ue_index); +} + class scheduler_policy_partial_slot_tdd_test : public base_scheduler_policy_test, public ::testing::TestWithParam { diff --git a/tests/unittests/scheduler/test_doubles/test_pucch_res_test_builder_helper.cpp b/tests/unittests/scheduler/test_doubles/test_pucch_res_test_builder_helper.cpp index 443cac51ef..8a31036aa9 100644 --- a/tests/unittests/scheduler/test_doubles/test_pucch_res_test_builder_helper.cpp +++ b/tests/unittests/scheduler/test_doubles/test_pucch_res_test_builder_helper.cpp @@ -97,7 +97,7 @@ TEST_P(sched_pucch_res_builder_tester, when_ues_are_added_their_cfg_have_differe const auto& ue_pucch_cfg = ue->serv_cell_cfg.ul_config.value().init_ul_bwp.pucch_cfg.value(); // Each UE should have 2 PUCCH resource sets configured ASSERT_EQ(ue_pucch_cfg.pucch_res_set.size(), 2); - ASSERT_EQ(ue_pucch_cfg.pucch_res_set[0].pucch_res_id_list.size(), pucch_params.nof_ue_pucch_f1_res_harq); + ASSERT_EQ(ue_pucch_cfg.pucch_res_set[0].pucch_res_id_list.size(), pucch_params.nof_ue_pucch_f0_or_f1_res_harq); ASSERT_EQ(ue_pucch_cfg.pucch_res_set[1].pucch_res_id_list.size(), pucch_params.nof_ue_pucch_f2_res_harq); // Make sure UE has all PUCCH resources with different cell_res_id. { diff --git a/tests/unittests/scheduler/test_utils/config_generators.h b/tests/unittests/scheduler/test_utils/config_generators.h index 870b690e89..8edc1a6772 100644 --- a/tests/unittests/scheduler/test_utils/config_generators.h +++ b/tests/unittests/scheduler/test_utils/config_generators.h @@ -65,10 +65,10 @@ make_default_sched_cell_configuration_request(const config_helpers::cell_config_ sched_req.searchspace0 = params.search_space0_index; sched_req.sib1_payload_size = 101; // Random size. - pucch_builder_params default_pucch_builder_params = du_cell_config{}.pucch_cfg; - default_pucch_builder_params.nof_ue_pucch_f1_res_harq = 3; - default_pucch_builder_params.nof_ue_pucch_f2_res_harq = 6; - default_pucch_builder_params.nof_sr_resources = 2; + pucch_builder_params default_pucch_builder_params = du_cell_config{}.pucch_cfg; + default_pucch_builder_params.nof_ue_pucch_f0_or_f1_res_harq = 3; + default_pucch_builder_params.nof_ue_pucch_f2_res_harq = 6; + default_pucch_builder_params.nof_sr_resources = 2; sched_req.pucch_guardbands = config_helpers::build_pucch_guardbands_list( default_pucch_builder_params, sched_req.ul_cfg_common.init_ul_bwp.generic_params.crbs.length()); diff --git a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_harq_sr_csi_test.cpp b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_harq_sr_csi_test.cpp index 0f01c5958b..13dca0cfdb 100644 --- a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_harq_sr_csi_test.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_harq_sr_csi_test.cpp @@ -867,6 +867,74 @@ TEST_F(test_pucch_allocator_ded_resources, test_common_plus_ded_with_csi_res_fai ASSERT_EQ(7, slot_grid.result.ul.pucchs.size()); } +TEST_F(test_pucch_allocator_ded_resources, ded_resource_allocation_when_common_resource_is_present_is_not_allowed) +{ + // NOTE: this allocation should be done with the function alloc_common_and_ded_harq_res(), which handles this special + // case. + t_bench.pucch_alloc.alloc_common_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.k0, t_bench.k1, t_bench.dci_info); + + std::optional pucch_res_ind = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + ASSERT_FALSE(pucch_res_ind.has_value()); +} + +TEST_F(test_pucch_allocator_ded_resources, if_ded_common_alloc_fails_no_harq_grants_should_be_kept_in_the_scheduler) +{ + // This test recreates an edge-case scenario where the allocation of common + dedicated resources could fail if the + // scheduler didn't clean the HARQ grant after the resource multiplexing fails. + // The conditions to recreate this scenario are: + // - Occupy all PUCCH resources from PUCCH res. set 1. + // - We need to force the multiplexing for UE under test; for this, we add a CSI grant. For this peculiar case, we + // need the allocation to fail in the multiplexing process, and not because of lack of available PUCCH resource + // indicators from PUCCH set 0. + // - It is important that we only occupy the PUCCH resources PUCCH res. set 1 with a PUCCH resource indicator that + // also exists in PUCCH res. set 0; if this is not the case, the PUCCH allocator fails because of lack of available + // PUCCH resource indicators from PUCCH set 0. + // + // 1) At this point, we call the allocator to allocate common + dedicated resources for the UE under test. The + // allocation should fail because all PUCCH resources PUCCH res. set 1 are occupied (failure during multiplexing). + // 2) We release the PUCCH resource for one of the UE occupying PUCCH res. set 1. This frees one of the PUCCH res. + // indicator from PUCCH res. set 1. + // 3) We repeat the allocation of common + dedicated resources for the UE under test; this time we expect a success; + // if the allocator didn't clean the HARQ grants, the allocation would fail because it finds a dedicated HARQ grant + // for the UE (which is not allowed the allocation of common + dedicated resources). + + add_ue_with_format2_harq_grant(); + add_ue_with_format2_harq_grant(); + add_ue_with_format2_harq_grant(); + + const unsigned csi_part1_bits = 4; + t_bench.pucch_alloc.pucch_allocate_csi_opportunity(t_bench.res_grid[t_bench.k0 + t_bench.k1], + t_bench.get_main_ue().crnti, + t_bench.get_main_ue().get_pcell().cfg(), + csi_part1_bits); + + std::optional pucch_res_ind = + t_bench.pucch_alloc.alloc_common_and_ded_harq_res(t_bench.res_grid, + t_bench.get_main_ue().crnti, + t_bench.get_main_ue().get_pcell().cfg(), + t_bench.k0, + t_bench.k1, + t_bench.dci_info); + + ASSERT_FALSE(pucch_res_ind.has_value()); + + t_bench.pucch_alloc.remove_ue_uci_from_pucch(t_bench.res_grid[t_bench.k0 + t_bench.k1], + t_bench.get_ue(t_bench.last_allocated_ue_idx).crnti, + t_bench.get_ue(t_bench.last_allocated_ue_idx).get_pcell().cfg()); + + pucch_res_ind = t_bench.pucch_alloc.alloc_common_and_ded_harq_res(t_bench.res_grid, + t_bench.get_main_ue().crnti, + t_bench.get_main_ue().get_pcell().cfg(), + t_bench.k0, + t_bench.k1, + t_bench.dci_info); + + ASSERT_TRUE(pucch_res_ind.has_value()); +} + /////// Test PUCCH Format 2 PRB configuration. /////// TEST_F(test_pucch_f2_alloc_several_prbs, test_prb_allocation_csi_only) diff --git a/tests/unittests/scheduler/uci_and_pucch/pucch_res_manager_test.cpp b/tests/unittests/scheduler/uci_and_pucch/pucch_res_manager_test.cpp index 32e3db9fd3..3a043eeafe 100644 --- a/tests/unittests/scheduler/uci_and_pucch/pucch_res_manager_test.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/pucch_res_manager_test.cpp @@ -401,6 +401,129 @@ TEST_F(test_pucch_resource_manager, test_allocation_specific_f2) res_manager.reserve_set_1_res_by_res_indicator(sl_tx, to_rnti(0x4603), res_indicator, pucch_cfg)); } +TEST_F(test_pucch_resource_manager, cancel_last_ue_res_reservations_when_nothing_was_reserved_should_not_assert) +{ + res_manager.reset_latest_reserved_res_tracker(); + + // Only expects that it doesn't assert. + res_manager.cancel_last_ue_res_reservations(sl_tx, to_rnti(0x4601), ue_cell_cfg); +} + +TEST_F(test_pucch_resource_manager, test_cancel_last_ue_res_reservations_for_specific_resources) +{ + res_manager.reset_latest_reserved_res_tracker(); + + auto alloc = res_manager.reserve_next_set_0_harq_res_available(sl_tx, to_rnti(0x4601), pucch_cfg); + ASSERT_EQ(0U, alloc.pucch_res_indicator); + + res_manager.set_new_resource_allocation(to_rnti(0x4601), srsran::pucch_resource_usage::HARQ_SET_0); + + // Try to allocate a new UE using the PUCCH resource indicator assigned to UE 0x4601; it should fail. + ASSERT_EQ( + nullptr, + res_manager.reserve_set_0_res_by_res_indicator(sl_tx, to_rnti(0x4602), alloc.pucch_res_indicator, pucch_cfg)); + + // Release the resources of the first UE. + res_manager.cancel_last_ue_res_reservations(sl_tx, to_rnti(0x4601), ue_cell_cfg); + + // Try to allocate the new UE again, now it should succeed. + ASSERT_NE( + nullptr, + res_manager.reserve_set_0_res_by_res_indicator(sl_tx, to_rnti(0x4602), alloc.pucch_res_indicator, pucch_cfg)); + + alloc = res_manager.reserve_next_set_1_harq_res_available(sl_tx, to_rnti(0x4601), pucch_cfg); + ASSERT_EQ(0U, alloc.pucch_res_indicator); + + res_manager.set_new_resource_allocation(to_rnti(0x4601), srsran::pucch_resource_usage::HARQ_SET_1); + + // Try to allocate a new UE using the PUCCH resource indicator assigned to UE 0x4601; it should fail. + ASSERT_EQ( + nullptr, + res_manager.reserve_set_1_res_by_res_indicator(sl_tx, to_rnti(0x4602), alloc.pucch_res_indicator, pucch_cfg)); + + // Release the resources of the first UE. + res_manager.cancel_last_ue_res_reservations(sl_tx, to_rnti(0x4601), ue_cell_cfg); + + // Try to allocate the new UE again, now it should succeed. + ASSERT_NE( + nullptr, + res_manager.reserve_set_1_res_by_res_indicator(sl_tx, to_rnti(0x4602), alloc.pucch_res_indicator, pucch_cfg)); +} + +TEST_F(test_pucch_resource_manager, test_cancel_last_ue_res_reservations_for_harq_resources_different_sets) +{ + res_manager.reset_latest_reserved_res_tracker(); + + auto alloc = res_manager.reserve_next_set_0_harq_res_available(sl_tx, to_rnti(0x4601), pucch_cfg); + ASSERT_EQ(0U, alloc.pucch_res_indicator); + + res_manager.set_new_resource_allocation(to_rnti(0x4601), srsran::pucch_resource_usage::HARQ_SET_0); + + // Don't set this reservation in the tracker, as we want to preserve it. + alloc = res_manager.reserve_next_set_1_harq_res_available(sl_tx, to_rnti(0x4601), pucch_cfg); + ASSERT_EQ(0U, alloc.pucch_res_indicator); + + // Try to allocate a new UE using the PUCCH resource indicator assigned to UE 0x4601; it should fail. + ASSERT_EQ( + nullptr, + res_manager.reserve_set_0_res_by_res_indicator(sl_tx, to_rnti(0x4602), alloc.pucch_res_indicator, pucch_cfg)); + + // Release the resources of the first UE. + res_manager.cancel_last_ue_res_reservations(sl_tx, to_rnti(0x4601), ue_cell_cfg); + + // Release the tracked resources of the first UE. + res_manager.cancel_last_ue_res_reservations(sl_tx, to_rnti(0x4601), ue_cell_cfg); + + // Try to allocate the new UE again, it should succeed. + ASSERT_NE(nullptr, res_manager.reserve_set_0_res_by_res_indicator(sl_tx, to_rnti(0x4602), 0U, pucch_cfg)); +} + +TEST_F(test_pucch_resource_manager, test_cancel_last_ue_res_reservations_for_harq_and_csi) +{ + res_manager.reset_latest_reserved_res_tracker(); + + auto alloc = res_manager.reserve_next_set_1_harq_res_available(sl_tx, to_rnti(0x4601), pucch_cfg); + ASSERT_EQ(0U, alloc.pucch_res_indicator); + + // Don't set this reservation in the tracker, as we want to preserve this resource. + res_manager.set_new_resource_allocation(to_rnti(0x4601), srsran::pucch_resource_usage::CSI); + + auto* csi_resource = res_manager.reserve_csi_resource(sl_tx, to_rnti(0x4601), ue_cell_cfg); + ASSERT_NE(nullptr, csi_resource); + + auto* csi_resource_1 = res_manager.reserve_csi_resource(sl_tx, to_rnti(0x4602), ue_cell_cfg); + ASSERT_EQ(nullptr, csi_resource_1); + + // Release the tracked resources of the first UE. + res_manager.cancel_last_ue_res_reservations(sl_tx, to_rnti(0x4601), ue_cell_cfg); + + csi_resource_1 = res_manager.reserve_csi_resource(sl_tx, to_rnti(0x4602), ue_cell_cfg); + ASSERT_NE(nullptr, csi_resource_1); +} + +TEST_F(test_pucch_resource_manager, test_cancel_last_ue_res_reservations_for_harq_and_sr) +{ + res_manager.reset_latest_reserved_res_tracker(); + + // Don't set this reservation in the tracker, as we want to preserve this resource. + auto alloc = res_manager.reserve_next_set_0_harq_res_available(sl_tx, to_rnti(0x4601), pucch_cfg); + ASSERT_EQ(0U, alloc.pucch_res_indicator); + + auto* sr_resource = res_manager.reserve_sr_res_available(sl_tx, to_rnti(0x4601), pucch_cfg); + ASSERT_NE(nullptr, sr_resource); + + res_manager.set_new_resource_allocation(to_rnti(0x4601), srsran::pucch_resource_usage::SR); + + auto* sr_resource_1 = res_manager.reserve_sr_res_available(sl_tx, to_rnti(0x4602), pucch_cfg); + ASSERT_EQ(nullptr, sr_resource_1); + + // Release the tracked resources of the first UE. + res_manager.cancel_last_ue_res_reservations(sl_tx, to_rnti(0x4601), ue_cell_cfg); + + sr_resource_1 = res_manager.reserve_sr_res_available(sl_tx, to_rnti(0x4602), pucch_cfg); + ASSERT_NE(nullptr, sr_resource_1); +} + //////////// Test the PUCCH resource manager: UEs with different configs //////////// class test_pucch_res_manager_multiple_cfg : public test_pucch_resource_manager